Internationalisation
The frontend is fully resource-based through i18next/react-i18next. English UK is canonical; American English is derived at runtime.
Frontend Locales
['en', 'en-US', 'fr', 'fi', 'cy', 'pl', 'et']
| UI tag | Persisted BCP-47 |
|---|---|
en | en-GB |
en-US | en-US |
fr | fr-FR |
fi | fi-FI |
cy | cy-GB |
pl | pl-PL |
et | et-EE |
English JSON defines typed namespace keys. en-US applies a UK-to-US spelling
transform rather than maintaining duplicate JSON.
Detection and Persistence
Order:
?lng=- localStorage
i18nextLng - browser navigator
- HTML language
Fallback is en. Base/region variants normalize through
nonExplicitSupportedLngs.
useChangeLocale():
- changes i18next immediately,
- lets the detector cache locally,
- best-effort updates
users.localewhen authenticated, - updates auth-store profile,
- does not roll back UI on DB failure.
Marketing forces the selector available. In-app selector visibility follows
multilingual_enabled, with system-admin bypass.
Namespaces
Resources are split into typed domains such as:
common
taxonomy
formats
components.*
pages.auth.*
pages.dashboard
pages.session*
pages.company_admin
pages.system_admin
pages.overview
pages.multiplayer
pages.oauth_consent
titles
Add copy to the narrowest appropriate namespace. Avoid dynamic key construction that defeats compile-time checking.
Audit
Run:
cd frontend
npm run i18n:audit
The script compares French, Finnish, Welsh, Polish, and Estonian against English:
- every English file/key exists,
- missing keys fail,
- i18next
{{variable}}and React<0>token counts match, en-USis excluded because it is derived.
Extra keys are reported so drift can be cleaned.
Server Locale
_shared/locale.ts normalizes:
en-GB, en-US, fr-FR, de-DE, es-ES, fi-FI, cy-GB, pl-PL, et-EE
German and Spanish are recognized for server prompt/voice normalization even though complete frontend UI catalogues are not checked in. Do not advertise them as fully supported UI locales.
Server helpers:
- load
users.locale, - normalize loose/base tags,
- default to
en-GB, - provide a human-readable prompt language,
- fall Welsh voice audio back to English where unsupported.
Email
Invitation templates exist for English, French, Finnish, Welsh, Polish and Estonian. Locale is normalized and base language selected; unknown values fall back to English.
Template interpolation escapes HTML/attributes, removes line breaks from subjects, and validates role codes. Preserve both HTML and plain-text output.
Guided Demo and Voice
Committed guided-demo narration exists for:
en, fr, fi, pl, et
Welsh and unsupported demo narration fall back to English. Billy narration is English only.
TTS/live voice language capability is not identical to UI translation capability. Document each surface separately.
Formatting
Use locale-aware utilities for:
- dates/times
- numbers and token counts
- currency estimates
- pluralization
Do not concatenate translated fragments. Provide complete sentences with named interpolation values.
Adding a Locale
- Add every English JSON file with identical key shape.
- Add imports/resources and locale tag.
- Add BCP-47 persistence mapping.
- Update audit target list.
- Add marketing/legal content.
- Add server normalization/language name only when intended.
- Decide email, demo, text AI, Live voice and TTS support separately.
- Test long text, plurals, dates, keyboard/screen-reader language.
- Verify
langattributes and fallback.
Troubleshooting
- Literal
{{count}}: interpolation token drift; run audit. - UI changes but AI remains English: DB locale save failed or server fallback.
en-USfolder missing: expected; it is runtime-derived.- Welsh voice is English: intentional provider fallback, UI can remain Welsh.