import fs from "node:fs"; import path from "node:path"; import { fileURLToPath } from "node:url"; import ts from "typescript"; const root = fileURLToPath(new URL("../src/", import.meta.url)); const files = (directory) => fs.readdirSync(directory, { withFileTypes: true }).flatMap((entry) => entry.isDirectory() ? files(path.join(directory, entry.name)) : [path.join(directory, entry.name)], ); const source = (file) => ts.createSourceFile(file, fs.readFileSync(file, "utf8"), ts.ScriptTarget.Latest, true, ts.ScriptKind.TSX); const catalog = new Map(); const problems = []; const slots = (value) => [...value.matchAll(/\{([A-Za-z][A-Za-z0-9_]*)\}/g)].map((match) => match[1]).sort().join(","); for (const file of files(path.join(root, "i18n/locales"))) { if (!/it.*\.ts$/.test(file)) continue; function visit(node) { if (ts.isPropertyAssignment(node) && ts.isStringLiteral(node.name) && ts.isStringLiteral(node.initializer)) { const key = node.name.text; const value = node.initializer.text; if (slots(key) !== slots(value)) problems.push(`${path.relative(root, file)}: interpolation mismatch for ${key}`); // Shared gettext messages may appear in multiple feature catalogues. // Their meaning and translation must agree regardless of merge order. if (catalog.has(key) && catalog.get(key) !== value) problems.push(`${path.relative(root, file)}: conflicting translation for ${key}`); catalog.set(key, value); } ts.forEachChild(node, visit); } visit(source(file)); } let references = 0; for (const file of files(root)) { if (!/\.tsx?$/.test(file) || /(?:\.test\.|\/test\/|\/i18n\/|prototype)/i.test(file)) continue; const tree = source(file); function visit(node) { if (ts.isCallExpression(node) && /^(t|translate)$/.test(node.expression.getText(tree))) { const arg = node.arguments[0]; if (arg && ts.isStringLiteral(arg)) { references++; if (!catalog.has(arg.text)) { const line = tree.getLineAndCharacterOfPosition(arg.getStart(tree)).line + 1; problems.push(`${path.relative(root, file)}:${line}: missing Italian message ${JSON.stringify(arg.text)}`); } } } ts.forEachChild(node, visit); } visit(tree); } console.log(`${catalog.size} Italian messages; ${references} static translation references checked.`); if (problems.length) { console.error(problems.join("\n")); process.exitCode = 1; }