feat(portal-shell): collapse accessibility routes into one i18n-marked route (#94)
CI / check (push) Successful in 4m0s
CI / commits (push) Has been skipped
CI / scan (push) Successful in 2m56s
CI / a11y (push) Successful in 2m6s
CI / perf (push) Successful in 3m50s

## Summary

Continue ADR-0019: replace the `/accessibility` + `/accessibilite` twin routes with a single canonical route whose content is i18n-marked in the template. The per-locale build (en/fr) already inlines the right copy — the route-data + `copy()` service indirection is no longer carrying its weight.

## What changes

- **`AccessibilityStatement`** loses its `ActivatedRoute` injection, the `Lang` discriminator, and the `COPY` lookup table. The component is now a plain shell over the template.
- **`accessibility.html`** carries the title + intro + status panel as `i18n="@@page.accessibility.*"` markers. Six new trans-units in [`messages.fr.xlf`](apps/portal-shell/src/locale/messages.fr.xlf) provide the French copy — verbatim from the old `COPY.fr` block, so the page reads the same in FR as before.
- **`app.routes.ts`** declares the single canonical route at `path: 'accessibility'` and keeps `/accessibilite` alive as a `redirectTo: 'accessibility'`. Drops `data: { lang: ... }` — no longer consumed.
- **`footer.html`** collapses the dual link into one i18n-marked link (`@@footer.accessibilityLink`). EN bundle reads "Accessibility statement"; FR bundle reads "Déclaration d'accessibilité".

## Decision worth flagging

The path stays in English across both locales for now: `/en/accessibility` and `/fr/accessibility`. Translating route *segments* (`/fr/declaration-d-accessibilite`) needs either a custom URL serializer or per-locale route trees — not worth the complexity at this scale. The page title and the link label already differ per locale via i18n, which is what's actually visible to users.

The historical `/accessibilite` path keeps working via the route-level redirect. Drops out of the codebase once analytics confirm no traffic reaches it.

## Test plan

- [x] `pnpm exec nx run-many -t lint test build --projects=portal-shell` — green. **35 / 35 specs** (was 36; the obsolete `lang`-fallback test is removed).
- [x] Production build emits both locales. FR bundle contains `Statut`, `Déclaration`, the FR intro / panel bodies. No leftover English on swept strings.
- [x] `extract-i18n` clean (49 unique units now: +5 for `page.accessibility.*` + `footer.accessibilityLink`, −0; the old route-data `lang` markers were not i18n).
- [ ] Manual: serve-static then `/en/accessibility` and `/fr/accessibility` render their respective content; `/fr/accessibilite` 301-redirects to `/fr/accessibility`.
- [ ] Manual: footer shows one link, locale-aware ("Accessibility statement" / "Déclaration d'accessibilité").
- [ ] Manual: browser tab title flips between bundles ("Accessibility statement · APF Portal" / "Déclaration d'accessibilité · Portail APF").

## What this PR explicitly does NOT do

- Translate the URL path segment (next ADR-only refinement if needed).
- Add the locale switcher in the footer — that's the next PR on the i18n track.
- Wire a CI gate on missing translations.

---------

Co-authored-by: Julien Gautier <julien.gautier@apf.asso.fr>
Reviewed-on: #94
This commit was merged in pull request #94.
This commit is contained in:
2026-05-11 20:18:28 +02:00
parent 65fae7f963
commit 8f84cc6389
7 changed files with 105 additions and 134 deletions
+11 -11
View File
@@ -1,9 +1,9 @@
import { Route } from '@angular/router';
// Tab titles per route — marked with `$localize` so each locale's
// bundle ships its own. The `/accessibility` and `/accessibilite`
// pair shares a title key; the upcoming route fusion (per ADR-0019)
// collapses them to a single localised route.
// bundle ships its own. The accessibility statement now lives at the
// single canonical path `/accessibility` (per ADR-0019, replacing the
// previous `/accessibility` + `/accessibilite` pair).
const homeTitle = $localize`:@@route.home.title:APF Portal`;
const accessibilityTitle = $localize`:@@route.accessibility.title:Accessibility statement · APF Portal`;
@@ -14,21 +14,21 @@ export const appRoutes: Route[] = [
loadComponent: () => import('./pages/home/home').then((m) => m.Home),
title: homeTitle,
},
// RGAA + ADR-0016: the accessibility statement must be reachable
// from both locales' canonical paths. Same component, content
// driven by the `lang` route data property.
// RGAA + ADR-0016: the accessibility statement is reachable from
// `/accessibility` in every locale. Content is i18n-marked so the
// per-locale bundle ships the right copy automatically.
{
path: 'accessibility',
loadComponent: () =>
import('./pages/accessibility/accessibility').then((m) => m.AccessibilityStatement),
data: { lang: 'en' },
title: accessibilityTitle,
},
// Backward-compat: the historical French path redirects to the
// canonical one. Bookmarks and existing inbound links keep working.
// Drops out of the codebase once analytics confirm no traffic.
{
path: 'accessibilite',
loadComponent: () =>
import('./pages/accessibility/accessibility').then((m) => m.AccessibilityStatement),
data: { lang: 'fr' },
title: accessibilityTitle,
redirectTo: 'accessibility',
pathMatch: 'full',
},
];