---
id: w5/m3/t003
title: Language switcher in the dashboard chrome
worker: worker5
status: done
estimate: 45m
depends_on: [w5/m3/t001, w5/m3/t002]
---

## Objective

Give users a control to change language. A switcher lists `SUPPORTED_LANGUAGES`
by native name, and selecting one calls `i18n.changeLanguage(lang)` +
`persistLanguage(lang)` so the choice takes effect immediately and survives the
next SSR reload.

## Context

- Build on `LANGUAGE_NAMES` / `SUPPORTED_LANGUAGES` (t001) and `persistLanguage`
  (t002).
- Placement should match the existing chrome patterns in
  `src/common/components/dashboard-layout/{index.tsx,dashboard-sidebar.tsx}`
  (see `dashboard/CLAUDE.md` header/sidebar conventions). Reuse this repo's
  shadcn primitives (`common/components/ui/`) — a `DropdownMenu` or `Select`,
  not a hand-rolled control.
- Also usable on auth pages (no sidebar) — keep the component self-contained so
  it can drop into `auth-page-shell` too if desired (auth localization is t005).

## Steps

1. Create `src/features/i18n/language-switcher.tsx` (or under `common/components`)
   rendering native language names from `LANGUAGE_NAMES`.
2. On select: `i18n.changeLanguage(lang)` + `persistLanguage(lang)`.
3. Mount it in the dashboard header (and/or sidebar footer) consistent with the
   existing chrome.

## Files

- `dashboard/src/features/i18n/language-switcher.tsx`
- `dashboard/src/common/components/dashboard-layout/{index.tsx,dashboard-sidebar.tsx}`

## Acceptance criteria

- [ ] Switcher shows each supported language by native name and marks the active one.
- [ ] Selecting a language re-renders the UI in it immediately.
- [ ] The choice persists across a full reload (cookie + localStorage from t002).
- [ ] `yarn typecheck` / `yarn lint` pass.

## Out of scope

- Which strings are translated (t004) — the switcher works even if only `en`+`zh`
  resources exist.
- Ory form localization (t005).
</content>
