scripts/fr-audit.mjs
Rama main · réplica de lectura del repositorio original.
// fr-value audit: flags any fr value that is literally identical to its en
// counterpart (English text hiding in fr.ts passes tsc AND i18n:check).
// Legit-identical exemptions: brand names, URLs, emails, ICU-only strings.
import ts from "typescript";
import { createRequire } from "node:module";
import { fileURLToPath } from "node:url";
import fs from "node:fs";
import path from "node:path";
function loadMessages(file) {
const src = fs.readFileSync(file, "utf8");
const js = ts.transpileModule(src, {
compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2020 },
}).outputText;
const mod = { exports: {} };
const req = createRequire(import.meta.url);
new Function("exports", "require", "module", js)(mod.exports, req, mod);
return mod.exports;
}
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const base = path.join(__dirname, "..", "src", "locales");
const en = loadMessages(path.join(base, "en.ts"));
const fr = loadMessages(path.join(base, "fr.ts"));
// Values that are legitimately identical across locales.
const IDENTICAL_OK = [
/^[\s\d.,%()/:+-]*$/, // digits/punct only (incl. ICU {placeholders})
/^LinkBreeze/,
/^(https?:|mailto:|\/)/i, // URLs / paths
/@(gmail|example|linkbreeze)\./i,
/^(vCard|WhatsApp|SMS|URL|UTM|Satoshi|Inter|Poppins|Roboto|Mono|Grotesk|Sans|Serif)/,
/^[A-Z][a-z]+ [A-Z][a-z]+$/, // "First Last" demo names? (flagged anyway below)
];
const findings = [];
function walk(a, b, trail) {
for (const k of Object.keys(a)) {
const key = trail ? trail + "." + k : k;
const av = a[k], bv = b?.[k];
if (typeof av === "object" && av !== null) { walk(av, bv, key); continue; }
if (typeof av !== "string") continue;
if (av === bv) {
const exempt = IDENTICAL_OK.some((re) => re.test(av)) || av.length <= 2;
findings.push({ key, value: av, exempt });
}
}
}
walk(en, fr, "");
const bad = findings.filter((f) => !f.exempt);
console.log(`identical en/fr values: ${findings.length} total, ${findings.length - bad.length} exempt (brands/URLs/digits)`);
console.log(`SUSPECT (English in fr.ts): ${bad.length}`);
for (const f of bad) console.log(` ${f.key} = ${JSON.stringify(f.value).slice(0, 90)}`);