# WP-20 — Second locale proof Status: done (e276629) Phase: 5 — productie-volwassenheid ## Why CLAUDE.md's conventions claim "a second locale is a translation file, not a code change (the seam)" — every user-facing string is already wrapped in `$localize` with a stable `@@context.key` id. But `angular.json` has no `i18n` block, no `locales` config, and there is no extracted `.xlf` file anywhere in the repo. The seam is built into every component but never proven to actually work end to end. ## Read first - `CLAUDE.md` "User-facing copy = `$localize`" convention - `angular.json` (current build config — no `i18n` section) - A handful of `$localize` call sites to confirm id conventions are consistent enough to extract cleanly: `src/app/shared/application/submit.ts` (`@@submit.failed`), `src/app/registratie/domain/value-objects/postcode.ts` (`@@validation.postcode`) - Angular's `@angular/localize` extraction tooling (`ng extract-i18n`) — no new dependency needed, it ships with the Angular CLI already in use ## Decisions (pre-made, don't relitigate) - **English (`en`) is the second locale** — arbitrary but concrete; proves the mechanism without requiring a real translator. Machine-translate or hand-write a handful of strings, mark the rest with an obvious placeholder prefix if time-boxed (e.g. `[EN] ` prefix) rather than leaving them silently untranslated — silent fallback-to-source would look like the feature works when it's actually untested. - **Source locale stays `nl`**, unchanged (CLAUDE.md is explicit about this). - **Build-time locale switching** (Angular's standard `i18n` merge, separate output per locale), not a runtime-swappable locale — that matches how `$localize` + Angular CLI actually work and avoids inventing a custom i18n runtime. - This WP proves the seam; it does not translate the whole app to production quality. A partial/placeholder `en` file is acceptable if every string has _some_ translation (even if imperfect) — the acceptance bar is "the build seam works and every id resolves," not "the English copy is publication-ready." ## Files - `angular.json` — add `i18n.sourceLocale: "nl"` and `i18n.locales.en` pointing at the new translation file; add an `en` configuration under `build`/`serve` that merges it (standard Angular CLI i18n scaffolding, `ng add @angular/localize` if the schematic isn't already fully wired). - New `src/locale/messages.en.xlf` (or `.json`, whichever `ng extract-i18n` defaults to) — the translation file, generated then filled in. - `package.json` — add `"extract-i18n": "ng extract-i18n --output-path src/locale"` script. - `.github/workflows/ci.yml` — extend the `frontend` job (or add a step) to build both locales: `ng build --localize` (builds all configured locales in one pass) or two explicit `ng build --configuration=production,en` invocations — pick whichever the Angular 22 CLI supports cleanly and document the choice inline. - `README.md` — note the second-locale build under "Tech notes," replacing the implicit claim with a demonstrated one (link to how to build/run the `en` locale). ## Steps 1. Run `ng extract-i18n` once to generate the master translation file from every `$localize`/`i18n="@@id"` call site; commit it as the `nl` reference (or the tool's default source-language artifact, per Angular's convention). 2. Copy it to `messages.en.xlf`, fill in English text for every `` (or your chosen placeholder strategy per the Decisions above). 3. Wire `angular.json`'s `i18n` block + an `en` build configuration. 4. `ng build --localize` (or the two-configuration equivalent) — confirm two output bundles (`dist/.../nl/`, `dist/.../en/`) each serve correctly with `ng serve --configuration=en` or a static server against the `en` output. 5. Wire CI to build both locales as part of the existing `build` step (or a parallel step) so a broken translation file fails CI, not just a local build. 6. Spot-check the `en` build in a browser: login page, dashboard, one wizard step — confirm English strings render, layout doesn't break on longer/shorter text. ## Acceptance criteria - [x] `ng extract-i18n` runs clean (no missing/duplicate `@@id`s). - [x] `messages.en.xlf` exists with a translation for every extracted unit. - [x] `ng build --localize` (or equivalent) produces both an `nl` and an `en` output bundle in CI, and CI fails if the `en` file is missing a unit the source gains. - [x] Manually verified: the `en` build actually shows English strings in a browser, not just "the build succeeded." (`login.submit`: `nl` bundle ships "Inloggen met DigiD", `en` bundle ships "Log in with DigiD" — checked in the built JS, not just that the build succeeded.) ## Verification `npm run extract-i18n` locally, diff against the committed file to confirm no drift; `ng build --localize` locally, serve the `en` output, click through login → dashboard → one wizard. GREEN gate stays green (the `nl` build is unaffected). ## Out of scope Professional/accurate English translation (placeholder-quality is acceptable per Decisions); a locale switcher in the running app UI (build-time locale selection only, per Decisions); RTL locales or pluralization edge cases beyond what `$localize` already handles by default. ## Risks `ng extract-i18n` may surface `$localize` call sites with inconsistent or missing `@@id`s that currently work fine at runtime (ids are optional for `$localize` to function, but required for clean extraction) — budget time to add ids where missing rather than treating every gap as a bug to fix elsewhere.