feat(i18n): auto-discover valid locales and harden language menu

- derive available locales from locale folders with required namespace validation

- exclude incomplete locales and report missing namespace files

- align system-language suggestion and selectors with discovered locales

- improve launch HUD language menu interaction, scrolling, and viewport clipping

- make i18n-check discover locale folders automatically
This commit is contained in:
imAaryash
2026-04-12 05:13:31 +05:30
parent 1ef30ff1c7
commit d1c9555464
8 changed files with 314 additions and 74 deletions
+10 -4
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env node
/**
* Validates that all locale translation files have identical key structures.
* Compares zh-CN and es against the en baseline for every namespace.
* Compares all locale folders (except en) against the en baseline for every namespace.
*
* Usage: node scripts/i18n-check.mjs
*/
@@ -11,7 +11,6 @@ import path from "node:path";
const LOCALES_DIR = path.resolve("src/i18n/locales");
const BASE_LOCALE = "en";
const COMPARE_LOCALES = ["zh-CN", "es", "tr", "ko-KR"];
function getKeys(obj, prefix = "") {
const keys = [];
@@ -34,12 +33,19 @@ const namespaces = fs
.filter((f) => f.endsWith(".json"))
.map((f) => f.replace(".json", ""));
const compareLocales = fs
.readdirSync(LOCALES_DIR, { withFileTypes: true })
.filter((entry) => entry.isDirectory())
.map((entry) => entry.name)
.filter((locale) => locale !== BASE_LOCALE)
.sort((a, b) => a.localeCompare(b));
for (const namespace of namespaces) {
const basePath = path.join(baseDir, `${namespace}.json`);
const baseData = JSON.parse(fs.readFileSync(basePath, "utf-8"));
const baseKeys = getKeys(baseData);
for (const locale of COMPARE_LOCALES) {
for (const locale of compareLocales) {
const localePath = path.join(LOCALES_DIR, locale, `${namespace}.json`);
if (!fs.existsSync(localePath)) {
@@ -77,6 +83,6 @@ if (hasErrors) {
process.exit(1);
} else {
console.log(
`i18n check PASSED — all ${COMPARE_LOCALES.length} locales match ${BASE_LOCALE} across ${namespaces.length} namespaces.`,
`i18n check PASSED — all ${compareLocales.length} locales match ${BASE_LOCALE} across ${namespaces.length} namespaces.`,
);
}