dorfteich/scripts/i18n-check.mjs
Claude Fable 5 e855192d23 Add i18n with i18next, German and English, and a key-parity check
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>
2026-07-04 19:23:45 +02:00

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.`);