diff --git a/apps/portal-shell/src/app/components/footer/footer.spec.ts b/apps/portal-shell/src/app/components/footer/footer.spec.ts
index 6f13e09..768d06c 100644
--- a/apps/portal-shell/src/app/components/footer/footer.spec.ts
+++ b/apps/portal-shell/src/app/components/footer/footer.spec.ts
@@ -36,4 +36,10 @@ describe('Footer', () => {
// redirects at the route level rather than being a separate link.
expect(root.querySelector('a[href="/accessibilite"]')).toBeNull();
});
+
+ it('embeds the locale switcher', async () => {
+ const fixture = TestBed.createComponent(Footer);
+ await fixture.whenStable();
+ expect(fixture.nativeElement.querySelector('app-locale-switcher')).not.toBeNull();
+ });
});
diff --git a/apps/portal-shell/src/app/components/footer/footer.ts b/apps/portal-shell/src/app/components/footer/footer.ts
index 5c06715..ea2d6c6 100644
--- a/apps/portal-shell/src/app/components/footer/footer.ts
+++ b/apps/portal-shell/src/app/components/footer/footer.ts
@@ -1,22 +1,24 @@
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { RouterLink } from '@angular/router';
+import { LocaleSwitcher } from '../locale-switcher/locale-switcher';
/**
* Thin full-width footer pinned to the bottom of the shell.
*
* v1 ships:
* - APF France handicap copyright with the current year.
- * - Accessibility statement links in both languages (RGAA-mandated;
- * ADR-0016). Both stay visible until the language selector lands,
- * after which we drop the one matching the current locale.
+ * - Locale switcher (FR / EN) — per ADR-0019, hard-refreshes to
+ * the matching `/fr/...` or `/en/...` prefix.
+ * - Accessibility statement link — single i18n-marked link
+ * pointing to the canonical `/accessibility` path.
*
- * The footer is reserved for cross-cutting page furniture — once we
- * have them, the language toggle and a dev-only version badge land
- * here. Real product navigation stays in the sidebar.
+ * The footer is reserved for cross-cutting page furniture. A
+ * dev-only version badge lands here once `SERVICE_VERSION` is wired
+ * to a build-time injection.
*/
@Component({
selector: 'app-footer',
- imports: [RouterLink],
+ imports: [RouterLink, LocaleSwitcher],
templateUrl: './footer.html',
changeDetection: ChangeDetectionStrategy.OnPush,
})
diff --git a/apps/portal-shell/src/app/components/icon/icon.ts b/apps/portal-shell/src/app/components/icon/icon.ts
index 79b58f9..c0cc980 100644
--- a/apps/portal-shell/src/app/components/icon/icon.ts
+++ b/apps/portal-shell/src/app/components/icon/icon.ts
@@ -11,6 +11,7 @@ import {
CircleHelp,
FileText,
Folder,
+ Globe,
Grid2x2,
House,
LayoutDashboard,
@@ -60,6 +61,7 @@ const ICON_REGISTRY = {
'circle-help': CircleHelp,
'file-text': FileText,
folder: Folder,
+ globe: Globe,
'grid-2x2': Grid2x2,
home: House,
'layout-dashboard': LayoutDashboard,
diff --git a/apps/portal-shell/src/app/components/locale-switcher/locale-switcher.html b/apps/portal-shell/src/app/components/locale-switcher/locale-switcher.html
new file mode 100644
index 0000000..ca49c7d
--- /dev/null
+++ b/apps/portal-shell/src/app/components/locale-switcher/locale-switcher.html
@@ -0,0 +1,38 @@
+
+
+
+
+
diff --git a/apps/portal-shell/src/app/components/locale-switcher/locale-switcher.scss b/apps/portal-shell/src/app/components/locale-switcher/locale-switcher.scss
new file mode 100644
index 0000000..23cf88c
--- /dev/null
+++ b/apps/portal-shell/src/app/components/locale-switcher/locale-switcher.scss
@@ -0,0 +1,107 @@
+// Trigger sits inside the thin footer. Visual height stays small
+// (~28px) but the tap target meets the AAA 44×44 minimum from
+// ADR-0016 by extending vertical padding to the full footer height
+// — the surrounding `