Four locales received English on surfaces whose translations were already complete, because the resolving code was a de/en binary. - Medication reminder push and the Telegram inline-keyboard labels admitted only de/en, so es/fr/it/pl got the English template on every channel. Route the resolver through a shared coerceLocale(). - The Coach's trailing "reply now in X" clause said English for any non-German locale, overriding the correct system-prompt directive. Read the language name from a shared map instead. - Coach refusal copy was a de/en constant pair, answering four locales in English at the one turn the Coach is declining to help. Move the copy into the bundles; the EN/DE constants now derive from them. - The sleep-stage chart formatted axis ticks as en-US for four locales; the cycle calendar and the device-score tile followed the *browser* locale rather than the app locale. - Three components carried a byte-identical duration formatter that branched on German. Lift it to one key-driven helper. Polish needs a third plural tier: 2-4 takes a "few" form a two-form scheme cannot express, so "2 godzin temu" read as "2 of-hours ago". Add a pluralKey() helper driven by Intl.PluralRules and a Few tier to the relative-time and stale-hint keys. The five locales whose rules have no "few" category resolve exactly as before by construction, and their Few values mirror Other. Teach the reverse-coverage guard to expand a pluralKey() base into its three tiers.
src/components/
React components. The UI primitives are shadcn/ui (new-york style, zinc base) over Radix + Tailwind 4 + Lucide; everything above the primitives is feature-grouped by surface. Components are server components unless they need a hook, state, or browser API, in which case they open with "use client". Charts use Recharts 3 — replacing the chart library requires explicit approval.
Primitives and shell
ui/— shadcn/ui primitives (button, dialog, sheet, table, …). Generated by the shadcn CLI; keep edits minimal and on-style.layout/— the app shell: sidebar, topbar, bottom nav.providers.tsx,version-poller.tsx— top-level context providers and the self-healing stale-shell poller.i18n/— translation context + locale switcher.
Feature groups
insights/— insight status, assessment cards, Coach surface, advisor cards (the largest group).medications/— medication cards, the schedule wizard, intake timeline, detail history.settings/— the section components behind each settings page.measurements/,mood/,dashboard/— entry forms, lists, dashboard tiles.charts/— Recharts wrappers.monitoring/— Umami / GlitchTip client bootstrap.gamification/,targets/,comparison/,doctor-report/,clinician/,onboarding/,admin/— the remaining per-surface groups.
Conventions
Kebab-case filenames; React component exports stay PascalCase regardless. (A handful of PascalCase-filename outliers under medications/ + onboarding/ are pre-existing drift, noted in ../../CLAUDE.md.) Every t() call needs a matching key in messages/en.json — guarded by i18n-call-site-coverage.test.ts. Data reads go through TanStack Query with keys from @/lib/query-keys and unwrap (await res.json()).data. Tests live in __tests__/.