feat(portal-shell): collapse accessibility routes into one i18n-marked route (#94)
## 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:
@@ -6,24 +6,11 @@
|
||||
<p i18n="@@footer.copyright">© {{ year }} APF France handicap</p>
|
||||
|
||||
<nav i18n-aria-label="@@footer.aria.legal" aria-label="Legal">
|
||||
<ul class="flex items-center gap-4">
|
||||
<li>
|
||||
<a
|
||||
routerLink="/accessibility"
|
||||
lang="en"
|
||||
class="rounded hover:text-brand-primary-500 hover:underline focus-visible:outline-2 focus-visible:outline-offset-4 focus-visible:outline-brand-primary-500 dark:hover:text-brand-primary-300"
|
||||
>Accessibility</a
|
||||
>
|
||||
</li>
|
||||
<li aria-hidden="true" class="text-gray-300 dark:text-gray-700">|</li>
|
||||
<li>
|
||||
<a
|
||||
routerLink="/accessibilite"
|
||||
lang="fr"
|
||||
class="rounded hover:text-brand-primary-500 hover:underline focus-visible:outline-2 focus-visible:outline-offset-4 focus-visible:outline-brand-primary-500 dark:hover:text-brand-primary-300"
|
||||
>Accessibilité</a
|
||||
>
|
||||
</li>
|
||||
</ul>
|
||||
<a
|
||||
routerLink="/accessibility"
|
||||
class="rounded hover:text-brand-primary-500 hover:underline focus-visible:outline-2 focus-visible:outline-offset-4 focus-visible:outline-brand-primary-500 dark:hover:text-brand-primary-300"
|
||||
i18n="@@footer.accessibilityLink"
|
||||
>Accessibility statement</a
|
||||
>
|
||||
</nav>
|
||||
</footer>
|
||||
|
||||
@@ -25,11 +25,15 @@ describe('Footer', () => {
|
||||
expect(text).toContain(String(new Date().getFullYear()));
|
||||
});
|
||||
|
||||
it('renders both accessibility statement links (EN + FR)', async () => {
|
||||
it('renders the accessibility statement link (single, locale-aware via i18n)', async () => {
|
||||
const fixture = TestBed.createComponent(Footer);
|
||||
await fixture.whenStable();
|
||||
const root = fixture.nativeElement as HTMLElement;
|
||||
expect(root.querySelector('a[href="/accessibility"]')).not.toBeNull();
|
||||
expect(root.querySelector('a[href="/accessibilite"]')).not.toBeNull();
|
||||
const link = root.querySelector('a[href="/accessibility"]') as HTMLAnchorElement | null;
|
||||
expect(link).not.toBeNull();
|
||||
expect(link?.textContent?.trim()).toBe('Accessibility statement');
|
||||
// The historical `/accessibilite` link is gone — that path now
|
||||
// redirects at the route level rather than being a separate link.
|
||||
expect(root.querySelector('a[href="/accessibilite"]')).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user