ADR-004: Derived Translation Keys¶
Status¶
Accepted
Context¶
Translations lived in three hand-synchronized files: english.ts, czech.ts, and a 787-line keys.ts mapping every leaf to its dot-path. Every new key required three coordinated edits, and nothing verified the files stayed structurally identical.
Decision¶
english.ts is the single schema source of truth. TranslationKeys is derived from it at runtime — keys.ts recursively walks the object and replaces every leaf with its dot-path, typed via a TranslationKeyTree<typeof english> mapped type. czech.ts is structurally enforced with satisfies typeof english. Seam tests verify every derived key resolves to a string in both languages.
Codegen was rejected (build step plus a drift window between generation runs); the hand-maintained map was rejected (three files to keep in sync manually).
Consequences¶
- Adding a translation is two edits (
english.ts,czech.ts); the key appears onTranslationKeysautomatically - Missing Czech entries fail compilation instead of falling back silently at runtime
- Key leaf types widen from string literals to
string— no consumer relied on literal types - The
@/i18n/keysimport path is unchanged; 134 consumer files were untouched