| |
| |
| |
| import { resources } from "./resources.js"; |
| import fs from "fs"; |
| const languageNames = new Intl.DisplayNames(Object.keys(resources), { |
| type: "language", |
| }); |
|
|
| function langDisplayName(lang) { |
| return languageNames.of(lang); |
| } |
|
|
| function compareStructures(lang, a, b, subdir = null) { |
| |
| if (typeof a !== typeof b && a !== null && b !== null) { |
| console.log("Invalid type comparison", [ |
| { |
| lang, |
| a: typeof a, |
| b: typeof b, |
| values: { |
| a, |
| b, |
| }, |
| ...(!!subdir ? { subdir } : {}), |
| }, |
| ]); |
| return false; |
| } |
|
|
| |
| |
| if (a && typeof a === "object") { |
| var keysA = Object.keys(a).sort(), |
| keysB = Object.keys(b).sort(); |
|
|
| |
| if (keysA.length !== keysB.length) { |
| console.log("Keys are missing!", { |
| [lang]: keysA, |
| en: keysB, |
| ...(!!subdir ? { subdir } : {}), |
| diff: { |
| added: keysB.filter((key) => !keysA.includes(key)), |
| removed: keysA.filter((key) => !keysB.includes(key)), |
| }, |
| }); |
| return false; |
| } |
|
|
| |
| if ( |
| !keysA.every(function (k, i) { |
| return k === keysB[i]; |
| }) |
| ) { |
| console.log("Keys are not equal!", { |
| [lang]: keysA, |
| en: keysB, |
| ...(!!subdir ? { subdir } : {}), |
| }); |
| return false; |
| } |
|
|
| |
| return keysA.every(function (key) { |
| |
| return compareStructures(lang, a[key], b[key], key); |
| }); |
|
|
| |
| } else { |
| return true; |
| } |
| } |
|
|
| function normalizeTranslations(lang, source, target, subdir = null) { |
| |
| if (!source || typeof source !== "object") { |
| return target ?? null; |
| } |
|
|
| |
| const normalized = target && typeof target === "object" ? { ...target } : {}; |
|
|
| |
| for (const key of Object.keys(source)) { |
| normalized[key] = normalizeTranslations( |
| lang, |
| source[key], |
| normalized[key], |
| key |
| ); |
| } |
|
|
| |
| for (const key of Object.keys(normalized)) { |
| if (!source[key]) delete normalized[key]; |
| } |
|
|
| return normalized; |
| } |
|
|
| function ISOToFilename(lang) { |
| const ISO_TO_FILENAME = { |
| "zh-tw": "zh_TW", |
| pt: "pt_BR", |
| vi: "vn", |
| }; |
| return ISO_TO_FILENAME[lang] || lang.replace("-", "_"); |
| } |
|
|
| const failed = []; |
| const TRANSLATIONS = {}; |
| for (const [lang, { common }] of Object.entries(resources)) { |
| TRANSLATIONS[lang] = common; |
| } |
|
|
| const PRIMARY = { ...TRANSLATIONS["en"] }; |
| delete TRANSLATIONS["en"]; |
|
|
| console.log( |
| `The following translation files will be normalized against the English file: [${Object.keys( |
| TRANSLATIONS |
| ).join(",")}]` |
| ); |
|
|
| |
| for (const [lang, translations] of Object.entries(TRANSLATIONS)) { |
| const normalized = normalizeTranslations(lang, PRIMARY, translations); |
|
|
| |
| resources[lang].common = normalized; |
|
|
| |
| const passed = compareStructures(lang, normalized, PRIMARY); |
| console.log(`${langDisplayName(lang)} (${lang}): ${passed ? "✅" : "❌"}`); |
| !passed && failed.push(lang); |
|
|
| const langFilename = ISOToFilename(lang); |
| fs.writeFileSync( |
| `./${langFilename}/common.js`, |
| `// Anything with "null" requires a translation. Contribute to translation via a PR! |
| const TRANSLATIONS = ${JSON.stringify(normalized, null, 2)} |
| |
| export default TRANSLATIONS;` |
| ); |
| } |
|
|
| if (failed.length !== 0) { |
| throw new Error( |
| `Error verifying normalized translations. Please check the logs.`, |
| failed |
| ); |
| } |
|
|
| console.log( |
| `👍 All translation files have been normalized to match the English schema!` |
| ); |
|
|
| process.exit(0); |
|
|