Files
govoplan-core/webui/scripts/i18n-catalog-validation.mjs

70 lines
2.1 KiB
JavaScript

import ts from "typescript";
export function findDuplicateTranslationKeys(sourceText, fileName = "generatedTranslations.ts") {
const source = ts.createSourceFile(
fileName,
sourceText,
ts.ScriptTarget.Latest,
true,
ts.ScriptKind.TS
);
const findings = [];
function visit(node) {
if (
ts.isVariableDeclaration(node) &&
ts.isIdentifier(node.name) &&
node.name.text === "generatedTranslations" &&
node.initializer &&
ts.isObjectLiteralExpression(node.initializer)
) {
scanCatalog(node.initializer);
}
ts.forEachChild(node, visit);
}
function scanCatalog(catalog) {
for (const localeProperty of catalog.properties) {
if (!ts.isPropertyAssignment(localeProperty) || !ts.isObjectLiteralExpression(localeProperty.initializer)) {
continue;
}
const locale = propertyName(localeProperty.name);
const firstDeclarations = new Map();
for (const translationProperty of localeProperty.initializer.properties) {
if (!ts.isPropertyAssignment(translationProperty)) continue;
const key = propertyName(translationProperty.name);
if (!key) continue;
const first = firstDeclarations.get(key);
if (!first) {
firstDeclarations.set(key, translationProperty.name);
continue;
}
const duplicatePosition = source.getLineAndCharacterOfPosition(
translationProperty.name.getStart(source)
);
const firstPosition = source.getLineAndCharacterOfPosition(first.getStart(source));
findings.push(
`${fileName}:${duplicatePosition.line + 1}:${duplicatePosition.character + 1} ` +
`duplicate generated translation key ${JSON.stringify(key)} in locale ` +
`${JSON.stringify(locale)}; first declared at line ${firstPosition.line + 1}`
);
}
}
}
visit(source);
return findings;
}
function propertyName(name) {
if (
ts.isIdentifier(name) ||
ts.isStringLiteral(name) ||
ts.isNumericLiteral(name) ||
ts.isNoSubstitutionTemplateLiteral(name)
) {
return name.text;
}
return "";
}