UNPKG

@lingui/cli

Version:

Lingui CLI to extract messages, compile catalogs, and manage translation workflows

104 lines (103 loc) 3.44 kB
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; }