localization: three shipped locales were never loaded

es-ES, de-DE and fr-FR shipped dead. Every one of their 1,129 keys was
translated and in parity, all three were in SHIPPED_LOCALES so the picker
offered them, and choosing one rendered the whole UI in raw keys.

All three catalogue files still imported only en-GB and pt-PT, so
`catalogues[code]` resolved to `{}` in every namespace — present, and useless.
Adding a locale is four edits across four files and nothing knew whether all
four had happened.

The parity tests could not have caught this and never will. They compare the
JSON files against each other, and a file nobody imports is exactly as
parity-correct as one that is loaded. The gap was between "the translation
exists" and "the app can reach it".

Registers all three in appCatalogs, panelCatalogs and uiCatalogs, moves the
merge out of I18nBridge into lib/catalogues.ts so it is testable without
mounting React, and adds catalogues.test.ts as the place that knows: every
shipped locale must resolve to a populated catalogue in all three namespaces,
and be the same size as the fallback.

Verified the test fails against the shipped code — 6 failures, panel.* for
each of the three — and passes after. 26 pass.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Officer-Session: 656f5c85-e49a-4f1f-b9f3-042a04b4d043
This commit is contained in:
2026-08-23 17:38:55 +00:00
co-authored by Claude Opus 5
parent d7a4a18ce8
commit 6490dbc368
5 changed files with 120 additions and 24 deletions
+10 -1
View File
@@ -1,8 +1,17 @@
import type { TranslationMap } from 'i18n';
import deDE from './de-DE.json';
import enGB from './en-GB.json';
import esES from './es-ES.json';
import frFR from './fr-FR.json';
import ptPT from './pt-PT.json';
/** Shared components' own catalog, per locale — merged into the app-wide map by `I18nBridge` under `ui.*`. */
export const uiCatalogs: TranslationMap = { 'en-GB': enGB, 'pt-PT': ptPT };
export const uiCatalogs: TranslationMap = {
'en-GB': enGB,
'pt-PT': ptPT,
'es-ES': esES,
'de-DE': deDE,
'fr-FR': frFR,
};
export { useTranslation } from './useTranslation';
+10 -1
View File
@@ -1,9 +1,18 @@
import type { TranslationMap } from 'i18n';
import deDE from './de-DE.json';
import enGB from './en-GB.json';
import esES from './es-ES.json';
import frFR from './fr-FR.json';
import ptPT from './pt-PT.json';
/** Panel apps' own catalog, per locale — merged into the app-wide map by `I18nBridge` under `panel.*`. */
export const panelCatalogs: TranslationMap = { 'en-GB': enGB, 'pt-PT': ptPT };
export const panelCatalogs: TranslationMap = {
'en-GB': enGB,
'pt-PT': ptPT,
'es-ES': esES,
'de-DE': deDE,
'fr-FR': frFR,
};
export { useTranslation } from './useTranslation';
export type { PanelTranslationKey } from './useTranslation';