@lingui/cli
Version:
Lingui CLI to extract messages, compile catalogs, and manage translation workflows
104 lines (103 loc) • 3.44 kB
JavaScript
import { styleText } from "node:util";
import path from "path";
import extract from "../extractors/index.js";
import { prettyOrigin } from "../utils.js";
function compareOrigins(a, b) {
const byPath = a[0].localeCompare(b[0]);
if (byPath !== 0) {
return byPath;
}
const lineA = a[1] ?? 0;
const lineB = b[1] ?? 0;
return lineA - lineB;
}
function mergeOrigins(prev, next) {
if (!next) {
return prev;
}
return [...prev, next].sort(compareOrigins);
}
function mergePlaceholders(prev, next) {
const res = { ...prev };
// Handle case where next is null or undefined
if (!next)
return res;
Object.entries(next).forEach(([key, value]) => {
if (!res[key]) {
res[key] = [];
}
if (!res[key].includes(value)) {
res[key].push(value);
}
res[key].sort();
});
return res;
}
export async function extractFromFiles(paths, config) {
const messages = {};
let catalogSuccess = true;
for (const filename of paths) {
const fileSuccess = await extract(filename, (next) => {
mergeExtractedMessage(next, messages, config);
}, config);
catalogSuccess &&= fileSuccess;
}
if (!catalogSuccess)
return undefined;
return messages;
}
export function mergeExtractedMessage(next, messages, config) {
if (!messages[next.id]) {
messages[next.id] = {
message: next.message,
context: next.context,
placeholders: {},
comments: [],
origin: [],
};
}
const prev = messages[next.id];
// there might be a case when filename was not mapped from sourcemaps
const nextOrigin = next.origin
? [
path.relative(config.rootDir, next.origin[0]).replace(/\\/g, "/"),
next.origin[1],
]
: undefined;
if (prev.message && next.message && prev.message !== next.message) {
throw new Error(`Encountered different default translations for message ${styleText("yellow", next.id)}` +
`\n${styleText("yellow", prettyOrigin(prev.origin))} ${prev.message}` +
`\n${styleText("yellow", prettyOrigin([nextOrigin || [""]]))} ${next.message}`);
}
messages[next.id] = {
...prev,
message: prev.message ?? next.message,
comments: next.comment
? [...prev.comments, next.comment].sort()
: prev.comments,
origin: mergeOrigins(prev.origin, nextOrigin),
placeholders: mergePlaceholders(prev.placeholders, next.placeholders),
};
}
export async function extractFromFilesWithWorkerPool(workerPool, paths, config) {
const messages = {};
let catalogSuccess = true;
const resolvedConfigPath = config.resolvedConfigPath;
if (!resolvedConfigPath) {
throw new Error("Multithreading is only supported when lingui config loaded from file system, not passed by API");
}
const results = await Promise.all(paths.map((filename) => workerPool.run(filename, resolvedConfigPath)));
results.forEach((result) => {
if (!result.success) {
catalogSuccess = false;
}
else {
result.messages.forEach((message) => {
mergeExtractedMessage(message, messages, config);
});
}
});
if (!catalogSuccess)
return undefined;
return messages;
}