Second template axis (org identity: letterhead, footer, signature, margins) server-side: OrgTemplateStore with JSON version history, publish/rollback, sent-brief version pinning, admin role + capability, 5 admin endpoints, org-logo upload category. FE seam widened only (Role/Capability unions, interceptor); WP-24/26 consume it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
104 lines
5.5 KiB
Markdown
104 lines
5.5 KiB
Markdown
# 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 `<trans-unit>`
|
|
(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.
|