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