Translation resources live in packages/shared/i18n/<lang>/<ns>.json (common, errors) and ship with de and en. The web app initializes react-i18next with bundled resources (?lng= wins, then the browser language); all shell components use useTranslation and the temporary t() stub is gone. The api localizes its uniform error bodies via a minimal i18next instance negotiated from Accept-Language. `pnpm i18n:check` fails CI when any key is missing in any language, backed by tested helpers in @dorfteich/shared. Closes #5 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
53 lines
1.7 KiB
JavaScript
53 lines
1.7 KiB
JavaScript
#!/usr/bin/env node
|
|
/**
|
|
* Verifies that every translation key exists in every language, in both
|
|
* directions. Run via `pnpm i18n:check`; requires @dorfteich/shared to be
|
|
* built (`pnpm build`) because it imports the shared tooling helpers.
|
|
*/
|
|
import { readdirSync, readFileSync } from 'node:fs';
|
|
import path from 'node:path';
|
|
import { fileURLToPath } from 'node:url';
|
|
|
|
import { missingTranslationKeys } from '../packages/shared/dist/index.mjs';
|
|
|
|
const i18nDir = path.join(path.dirname(fileURLToPath(import.meta.url)), '../packages/shared/i18n');
|
|
const languages = readdirSync(i18nDir).sort();
|
|
|
|
if (languages.length < 2) {
|
|
console.error(`i18n:check: expected at least two languages in ${i18nDir}`);
|
|
process.exit(1);
|
|
}
|
|
|
|
const namespaces = new Set(languages.flatMap((lang) => readdirSync(path.join(i18nDir, lang))));
|
|
|
|
let problems = 0;
|
|
for (const namespaceFile of [...namespaces].sort()) {
|
|
const trees = {};
|
|
for (const lang of languages) {
|
|
try {
|
|
trees[lang] = JSON.parse(readFileSync(path.join(i18nDir, lang, namespaceFile), 'utf8'));
|
|
} catch {
|
|
console.error(`✗ ${lang}/${namespaceFile}: missing or invalid JSON`);
|
|
problems += 1;
|
|
trees[lang] = {};
|
|
}
|
|
}
|
|
for (const reference of languages) {
|
|
for (const candidate of languages) {
|
|
if (reference === candidate) continue;
|
|
for (const key of missingTranslationKeys(trees[reference], trees[candidate])) {
|
|
console.error(
|
|
`✗ ${candidate}/${namespaceFile}: missing key "${key}" (present in ${reference})`,
|
|
);
|
|
problems += 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (problems > 0) {
|
|
console.error(`i18n:check: ${problems} problem(s) found.`);
|
|
process.exit(1);
|
|
}
|
|
console.log(`i18n:check: ${languages.join(', ')} — all keys present in all languages.`);
|