90 lines
3.0 KiB
JavaScript
90 lines
3.0 KiB
JavaScript
/**
|
|||
|
|
* Apply English-value → locale phrase map across all packs (force update).
|
||
|
|
* Phrase map: scripts/phrase-map.json { "English": { es, fr, de, it, pt, nl, pl, ja } }
|
||
|
|
*/
|
||
|
|
import fs from "node:fs";
|
||
|
|
import path from "node:path";
|
||
|
|
import { fileURLToPath } from "node:url";
|
||
|
|
import { SAME_AS_EN } from "./locale-extra.mjs";
|
||
|
|
|
||
|
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||
|
|
const messagesDir = path.join(__dirname, "../src/lib/i18n/messages");
|
||
|
|
const locales = ["es", "fr", "de", "it", "pt", "nl", "pl", "ja"];
|
||
|
|
const mapPath = path.join(__dirname, "phrase-map.json");
|
||
|
|
|
||
|
|
function parseDict(source) {
|
||
|
|
const dict = {};
|
||
|
|
const re = /"([^"\\]+)"\s*:\s*((?:"(?:\\.|[^"\\])*")|(?:`(?:\\.|[^`\\])*`))/gs;
|
||
|
|
let m;
|
||
|
|
while ((m = re.exec(source))) {
|
||
|
|
const key = m[1];
|
||
|
|
const raw = m[2];
|
||
|
|
dict[key] = raw.startsWith("`")
|
||
|
|
? raw.slice(1, -1).replace(/\\`/g, "`").replace(/\\\$/g, "$").replace(/\\n/g, "\n")
|
||
|
|
: JSON.parse(raw);
|
||
|
|
}
|
||
|
|
return dict;
|
||
|
|
}
|
||
|
|
|
||
|
|
function emitPack(exportName, comment, dict, keyOrder) {
|
||
|
|
const lines = [
|
||
|
|
`import type { MessageDict } from "./types";`,
|
||
|
|
``,
|
||
|
|
`/** ${comment} */`,
|
||
|
|
`export const ${exportName}: MessageDict = {`
|
||
|
|
];
|
||
|
|
for (const key of keyOrder) {
|
||
|
|
lines.push(`\t${JSON.stringify(key)}: ${JSON.stringify(dict[key])},`);
|
||
|
|
}
|
||
|
|
lines.push(`};`, ``);
|
||
|
|
return lines.join("\n");
|
||
|
|
}
|
||
|
|
|
||
|
|
const comments = {
|
||
|
|
es: "Spanish (es) UI pack — keys must stay in sync with en.ts.",
|
||
|
|
fr: "French (fr) UI pack — keys must stay in sync with en.ts.",
|
||
|
|
de: "German (de) UI pack — keys must stay in sync with en.ts.",
|
||
|
|
it: "Italian (it) UI pack — keys must stay in sync with en.ts.",
|
||
|
|
pt: "Portuguese (pt) UI pack — keys must stay in sync with en.ts.",
|
||
|
|
nl: "Dutch (nl) UI pack — keys must stay in sync with en.ts.",
|
||
|
|
pl: "Polish (pl) UI pack — keys must stay in sync with en.ts.",
|
||
|
|
ja: "Japanese (ja) UI pack — keys must stay in sync with en.ts."
|
||
|
|
};
|
||
|
|
|
||
|
|
if (!fs.existsSync(mapPath)) {
|
||
|
|
console.error("missing phrase-map.json — run fill scripts first");
|
||
|
|
process.exit(1);
|
||
|
|
}
|
||
|
|
|
||
|
|
const phraseMap = JSON.parse(fs.readFileSync(mapPath, "utf8"));
|
||
|
|
const en = parseDict(fs.readFileSync(path.join(messagesDir, "en.ts"), "utf8"));
|
||
|
|
const keyOrder = Object.keys(en);
|
||
|
|
|
||
|
|
let hit = 0;
|
||
|
|
let miss = 0;
|
||
|
|
for (const code of locales) {
|
||
|
|
const existing = parseDict(fs.readFileSync(path.join(messagesDir, `${code}.ts`), "utf8"));
|
||
|
|
const dict = {};
|
||
|
|
for (const key of keyOrder) {
|
||
|
|
const enVal = en[key];
|
||
|
|
const mapped = phraseMap[enVal]?.[code];
|
||
|
|
if (typeof mapped === "string" && mapped.trim()) {
|
||
|
|
dict[key] = mapped;
|
||
|
|
if (existing[key] === enVal || !existing[key]) hit++;
|
||
|
|
} else if (typeof existing[key] === "string" && existing[key].trim()) {
|
||
|
|
dict[key] = existing[key];
|
||
|
|
} else {
|
||
|
|
dict[key] = enVal;
|
||
|
|
if (!SAME_AS_EN.has(key)) miss++;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
fs.writeFileSync(
|
||
|
|
path.join(messagesDir, `${code}.ts`),
|
||
|
|
emitPack(code, comments[code], dict, keyOrder),
|
||
|
|
"utf8"
|
||
|
|
);
|
||
|
|
const same = keyOrder.filter((k) => dict[k] === en[k] && !SAME_AS_EN.has(k)).length;
|
||
|
|
console.log(code, "keys", keyOrder.length, "sameAsEn", same);
|
||
|
|
}
|
||
|
|
console.log("phrase hits(approx)", hit, "pads", miss);
|