localization: how to add a locale, and it is built now
the doc said "DESIGN, NOT BUILT — a working 5% skeleton". six locales ship, three catalogues, parity in both directions and a test that catches a dead one. it has said the wrong thing since the day the work landed. and there was no procedure for the thing anybody would actually come here to do. seven files: three catalogues, three registrations, and the picker. written out because the failure it is written from was somebody doing four of them — es-ES, de-DE and fr-FR once shipped translated, in perfect key parity, listed in the picker, and completely dead, because all three registration files still imported only en-GB and pt-PT. nothing caught that at the time. the section names what catches it now, so whoever adds the seventh language knows `bun test` settles it rather than having to trust that it does. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Officer-Session: 656f5c85-e49a-4f1f-b9f3-042a04b4d043
This commit is contained in:
+57
-1
@@ -1,6 +1,10 @@
|
|||||||
# Localization
|
# 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
|
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.
|
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.
|
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
|
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.
|
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/<code>.json app.* the shell, screens, settings
|
||||||
|
src/workspaces/officerdev/src/locales/<code>.json panel.* the windowed apps
|
||||||
|
src/workspaces/components/locales/<code>.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 `<code>.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.
|
||||||
|
|||||||
Reference in New Issue
Block a user