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>
25 lines
870 B
TypeScript
25 lines
870 B
TypeScript
/**
|
|
* Tooling helpers for translation resources. Used by the repo-level
|
|
* `pnpm i18n:check` script and its tests: every key must exist in every
|
|
* language, in both directions.
|
|
*/
|
|
|
|
type TranslationTree = { [key: string]: string | TranslationTree };
|
|
|
|
/** Flattens {a:{b:"x"}} to ["a.b"]. */
|
|
export function translationKeys(tree: TranslationTree, prefix = ''): string[] {
|
|
return Object.entries(tree).flatMap(([key, value]) => {
|
|
const path = prefix ? `${prefix}.${key}` : key;
|
|
return typeof value === 'string' ? [path] : translationKeys(value, path);
|
|
});
|
|
}
|
|
|
|
/** Keys present in `reference` but missing in `candidate`. */
|
|
export function missingTranslationKeys(
|
|
reference: TranslationTree,
|
|
candidate: TranslationTree,
|
|
): string[] {
|
|
const have = new Set(translationKeys(candidate));
|
|
return translationKeys(reference).filter((key) => !have.has(key));
|
|
}
|