Files
WRNexusJS/packages/cli/src/i18n-command.ts
T
Clintchiz 586a6db8ff
Quality / quality (ubuntu-latest) (push) Failing after 21s
Quality / quality (windows-latest) (push) Canceled after 0s
release: WRNexusJS 0.8.0
2026-08-02 23:18:51 +05:30

67 lines
2.7 KiB
TypeScript

import { existsSync, mkdirSync, readdirSync, writeFileSync } from "node:fs";
import { extname, join, resolve } from "node:path";
import {
auditLocaleKeys,
extractTranslationKeysFromFiles,
flattenMessageKeys,
loadLocales,
} from "@wrnexus/i18n";
function sourceFiles(root: string): string[] {
const output: string[] = [];
const visit = (directory: string) => {
if (!existsSync(directory)) return;
for (const entry of readdirSync(directory, { withFileTypes: true })) {
if (entry.name.startsWith(".") || entry.name === "node_modules" || entry.name === "dist")
continue;
const path = join(directory, entry.name);
if (entry.isDirectory()) visit(path);
else if ([".wrn", ".ts", ".tsx", ".js", ".jsx"].includes(extname(entry.name)))
output.push(path);
}
};
visit(join(root, "app"));
return output.sort();
}
export function runI18nCommand(appRoot: string, command: string): boolean {
const root = resolve(appRoot);
const extracted = extractTranslationKeysFromFiles(sourceFiles(root));
const keys = [...new Set(extracted.map((entry) => entry.key))].sort();
const messages = loadLocales(join(root, "app", "locales"), { strict: true });
const locales = Object.keys(messages).sort();
const reference = locales[0] ?? "en";
const audit = auditLocaleKeys(messages, reference);
const referenceKeys = new Set(flattenMessageKeys(messages[reference] ?? {}));
const unused = [...referenceKeys].filter((key) => !keys.includes(key)).sort();
const missingFromReference = keys.filter((key) => !referenceKeys.has(key));
const report = { reference, locales, extracted: keys, missingFromReference, unused, audit };
if (command === "extract") {
const directory = join(root, ".wrnexus");
mkdirSync(directory, { recursive: true });
const file = join(directory, "i18n-keys.json");
writeFileSync(file, `${JSON.stringify(report, null, 2)}\n`, "utf8");
console.log(`✓ Extracted ${keys.length} translation keys to ${file}`);
return true;
}
if (command !== "validate")
throw new Error("WRN-I18N-COMMAND: use i18n extract or i18n validate.");
for (const locale of locales) {
const missing = [
...new Set([...(audit[locale]?.missing ?? []), ...missingFromReference]),
].sort();
if (missing.length) {
console.log(`Missing in ${locale}:`);
missing.forEach((key) => console.log(`- ${key}`));
}
}
if (unused.length) {
console.log("Unused keys:");
unused.forEach((key) => console.log(`- ${key}`));
}
const healthy =
missingFromReference.length === 0 &&
Object.values(audit).every((value) => value.missing.length === 0);
if (healthy) console.log(`✓ ${locales.length} locales contain every extracted key.`);
return healthy;
}