Localization: integrate next-intl and migrate every English string in the UI into a message catalog #146
Labels
No labels
area:auth
area:ci
area:db
area:infra
area:native
area:pwa
area:service
epic
feature
foundation
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
james/carol#146
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Every user-facing string in
app/(app)/,app/login,app/registeris currently hardcoded English. Self-hosters deploying Carol to a non-English speaker need locale support, and adopting an i18n library now means future strings start in the catalog instead of inline.Scope
next-intl. Works with the App Router (server + client components), supports compile-time-validated keys via TypeScript, light runtime. Capture the choice and the alternatives rejected (react-i18next,lingui) in an ADR.next-intlprovider at the layout level. Locale resolution order:?locale=query →localecookie →Accept-Language→DEFAULT_LOCALE. Persist the choice in thelocalecookie when the user picks one.messages/en.jsonis the source of truth; new locales land as sibling files (messages/es.json, etc.). Document explicitly that en.json is the only catalog the codebase commits to keeping in sync; other locales accept gaps that fall through to en.app/(app)/,app/login,app/registerbecomes at('namespace.key')reference. Namespacing follows the route tree (profile.basics.name,account.signinMethods.title, etc.).DEFAULT_LOCALE=en,SUPPORTED_LOCALES=en(comma-separated allowlist). Both land inREADME.mdConfiguration → Operations.messages/es.json— just a translated home/login pair) to confirm the switch round-trips.messages/en.json; never hardcode strings in JSX.Acceptance criteria
next-intlinstalled; provider wired intoapp/(app)/layout.tsxandapp/layout.tsx.messages/en.jsonexists with the full set of current strings; every(app)//login//registersurface renders viat(...).messages/es.json(home + login translated) confirms?locale=esand the cookie round-trip work.README.mdConfiguration gains theDEFAULT_LOCALE/SUPPORTED_LOCALESrows; CLAUDE.md Conventions gains the no-hardcoded-strings bullet.lavamoat.allowScriptsreview fornext-intlinstall scripts done in the PR per ADR-0010.Out of scope
es.jsonis just the mechanism check).docs/ci.md, ADRs, API JSON error bodies) — stay English.Dependency note
If the Carol's-voice copy-rewrite ticket lands first, this ticket migrates the already-voice-corrected strings. If this lands first, the voice rewrite edits
messages/en.jsoninstead of JSX. Either order works; the PR description must call out which.Composes with
Design package (locale picker fits naturally next to the theme switch in the sidebar), CLAUDE.md Conventions, README Configuration table from #114, ADR-0008 (locale cookie follows the same path as the theme cookie), ADR-0010 (install scripts).