Translation files
The web app loads translations per locale from this directory. Each *.json
under <locale>/ corresponds to one i18next namespace.
Layout
apps/web/public/locales/
├── de/
│ ├── translations.json # Legacy single-file namespace (4000+ keys)
│ ├── errors.json # HTTP/auth/app error copy
│ ├── mail.json # Subjects + bodies for transactional emails
│ ├── common.json # Buttons / generic UI / ICU plurals (#72)
│ ├── settings.json # Per-area: settings surfaces (#72)
│ ├── billing.json # Per-area: billing surfaces (#72)
│ ├── crm.json # Per-area: CRM surfaces (#72)
│ ├── auth.json # Per-area: authentication surfaces (#72)
│ └── emailMarketing.json # Per-area: email marketing surfaces (#72)
└── en/ (same files)
Conventions
- Default namespace:
translations(legacy). Existing callsites continue to work unchanged. - Per-area namespaces: use the existing namespace that matches the surface:
common,settings,billing,crm,auth, oremailMarketing. Use the registered name exactly;emailMarketingis camelCase. - Brand-new namespaces: create matching
de/anden/JSON files and register the namespace inpackages/i18n/src/index.ts#i18nNamespaces. - Key naming: dotted scope.action —
locale.title,action.save. - ICU plurals: wrap the count expression as
{count, plural, one {# item} other {# items}}. The customuseTranslate()hook detects the ICU shape and routes it throughintl-messageformat; the legacy i18next_one/_othersuffix style stays supported for back-compat. - Parity gate: every namespace must have identical keys across
de/anden/. Enforced bypackages/i18n/src/index.test.tsandscripts/i18n-check.ts.
See docs/contributing/i18n.md for the long-form guide.