Internationalization (i18n)¶
Maxwell's Wallet ships in 10 locales. The UI is fully localized with locale-aware date and currency formatting, powered by next-intl.
Supported Locales¶
| Locale | Language |
|---|---|
en-US |
English (US) — source |
en-GB |
English (UK) |
es-ES |
Spanish |
fr-FR |
French |
it-IT |
Italian |
pt-PT |
Portuguese |
de-DE |
German |
nl-NL |
Dutch |
aa-ER |
Afar |
pseudo |
Pseudo-locale (QA only) |
Users pick a language in Settings, or leave it on "browser" to follow the
Accept-Language header. Any translation key missing from a locale falls back to
the English (en-US) string, so the UI never shows a raw key.
The Golden Rule¶
Only frontend/src/messages/en-US.json is hand-edited. Every other locale
file is managed by Crowdin and synced automatically — do
not edit them by hand (a CI guard rejects PRs that do).
Adding Translatable Strings¶
- Add the English string to
frontend/src/messages/en-US.jsonusing dot-notation keys (e.g.admin.backups.createButton). - Use it in code via
useTranslations:
import { useTranslations } from 'next-intl';
const t = useTranslations('admin.backups');
return <button>{t('createButton')}</button>;
- Commit and push to
main. A GitHub Action uploads the new source strings to Crowdin; completed translations come back as a pull request.
Just Recipes¶
| Command | Description |
|---|---|
just i18n::upload |
Push en-US.json source strings to Crowdin |
just i18n::download |
Pull translations from Crowdin |
just i18n::status |
Show translation progress |
just i18n::pseudo |
Regenerate the pseudo-locale for QA |
CI Safety Gates¶
Translation completeness is enforced by tests in
frontend/src/test/i18n.test.ts, which verify that every shipped locale has all
keys from en-US.json and that ICU {placeholder} tokens are preserved. A
separate guard workflow blocks direct edits to Crowdin-managed locale files.
For the complete workflow — including Crowdin configuration, handling sync PRs, and providing translator context — see the in-repo i18n workflow guide.