diff --git a/docs/localization.md b/docs/localization.md index 731c9d68..c58d0a66 100644 --- a/docs/localization.md +++ b/docs/localization.md @@ -1,6 +1,10 @@ # Localization -**Status: DESIGN, NOT BUILT — with a working 5% skeleton already in the tree.** +**Status: BUILT and shipping six locales** — en-GB, pt-PT, es-ES, de-DE, fr-FR, sv-SE. The mechanism +is done; what remains is coverage, which is the count-taking below. + +**To add a seventh language, read "Adding a locale" at the bottom and nothing else.** It is seven +files and the order does not matter, but missing one is silent — see the failure it is written from. This is an execution plan. It is written to be handed to an agent who has not had the conversation that produced it, so it states the decisions and the reasons for them, not just the steps. @@ -412,3 +416,55 @@ else will ever notice. the UI. It is a different feature that happens to sit in the same object — do not conflate them. 5. **Does a member see the owner's locale anywhere?** Any server-rendered or cached string shared between accounts needs a locale decision. D1 mostly removes this by removing server prose. + +--- + +## Adding a locale + +Seven files. Six are mechanical; the seventh is the one that gets missed. + +**Three catalogues, one per workspace.** Copy `en-GB.json` beside itself and translate the values, +keeping every key: + +``` +src/apps/officer-web/locales/.json app.* the shell, screens, settings +src/workspaces/officerdev/src/locales/.json panel.* the windowed apps +src/workspaces/components/locales/.json ui.* shared components +``` + +**Three registrations, one per catalogue.** Each is an `import` plus a line in a map: + +``` +src/apps/officer-web/lib/catalogues.ts +src/workspaces/officerdev/src/locales/index.ts +src/workspaces/components/locales/index.ts +``` + +**And the picker**, which is the one that gets missed the other way round: + +``` +src/apps/officer-web/lib/locales.ts SHIPPED_LOCALES +``` + +### Why this is spelled out rather than left to reading the code + +es-ES, de-DE and fr-FR once shipped translated, in perfect key parity, listed in the picker — and +completely dead. All three registration files still imported only en-GB and pt-PT, so the merge +produced three empty catalogues and the UI rendered raw keys in three of five languages. + +Nothing caught it. The parity tests compare the JSON files against each other and every file was +correct; a locale that is never imported is invisible to them by construction. The picker was right. +Four edits in four files with no single place that knew all four had happened. + +### What checks you now + +Run `bun test` and it is settled. Two things stand where nothing stood: + +- `src/apps/officer-web/lib/catalogues.test.ts` asserts every locale in `SHIPPED_LOCALES` resolves to + a populated catalogue in all three namespaces, and matches en-GB's size. A locale in the picker but + not imported fails here. So does one imported but empty. +- The three `parity.test.ts` files read their own directory rather than listing locales by hand, so a + new `.json` is checked the moment it exists — for missing keys AND extra ones, because + `findMissingKeys` alone reports half of a rename. + +Then `bunx tsgo` and `bunx prettier --write` on what you touched, as everywhere else.