fix(i18n): align all 14 locales + strict key parity (no dead keys, no dupes)
What & why
The 14 frontend locale files were fragmented — 90 keys diverged across locales (missing in some, extra in others), the root cause of the aiGeneratedNoDocs near-miss and the reason a strict parity test was never enforceable.
Audited every divergent key against actual code usage (literal $t()/translate() + dynamic bracket access):
| Category | Count | Action |
|---|---|---|
| Dead (orphaned by removed features) | 81 | Removed from every locale that had them |
| Used but missing from some locales | 9 | Added with real translations |
Dead clusters (verified 0 code refs, incl. dynamic): admin.userEdit.*, admin.reindex*/dbActions.reindex/operations.reindexDatabase, settings email-change + account-deletion, chatbot.streamTimeout/generating/translationFailed, quickhelp.* prompts.
EN was right on the admin/settings clusters (already clean); the dead keys lived in other locales. The fix is mostly deletion (−299 lines), not addition.
Also fixed a typo: sw responseRating.ratingLabels key 44 → 4 (value was correct; caught via dynamic-usage check — ratingLabels[rating] is used).
Result
All 14 locales now share an identical 1136-key set (deep parity, 0 drift).
Test (strict — replaces baseline tolerance)
localeConsistency.test.js now enforces, CI-blocking:
- All 14 locales share the identical deep key set (any missing/extra key fails).
-
No duplicate keys within any file (AST scan — JS silently keeps the last value for a dup key, so this needs source-level detection).
Removed
localeParity.baseline.json(the tolerance hack is no longer needed).
Verification
- frontend: 1223 tests pass, ESLint clean, Prettier clean.
- Method: surgical line-deletion (formatting preserved) + by-hand translated insertions — no full reserialize.
-
CI green before merge.