# i18n Conventions ## Scope This document defines translation conventions for both apps in MVP0+. - Public app i18n: `next-intl` message namespaces and route-level usage - Admin app i18n: JSON dictionaries + runtime resolver/provider - Shared locale contract: `@cms/i18n` (`de`, `en`, `es`, `fr`; default `en`) ## Locale Policy - Source of truth: `packages/i18n/src/index.ts` - Current enabled locales are code-driven and shared across web/admin. - Admin-managed locale toggles are planned for a later MVP. ## Key Naming Conventions - Use `camelCase` for keys. - Group by domain namespace (not by component filename). - Keep keys stable; update values, not key names, during copy edits. ### Public app namespaces - `Layout.*` - `Home.*` - `LanguageSwitcher.*` - Page-specific namespaces, e.g. `About.*`, `Contact.*` - Metadata namespace: `Seo.*` ### Admin app namespaces - `common.*` - `auth.*` - `dashboard.*` - `settings.*` ## Message Structure - Keep messages as nested JSON objects. - Avoid very deep nesting (prefer 2-3 levels). - Keep punctuation in translation values, not code. - Avoid embedding HTML in message strings. ## Fallback Rules - Unknown/invalid locale values fallback to default locale `en`. - Missing translation key behavior: - Admin: `translateMessage` returns provided fallback, else key. - Public: ensure required keys exist in locale JSON; avoid runtime missing-key states. ## Adding New Translation Keys 1. Add key/value in `apps/*/src/messages/en.json`. 2. Add equivalent key in `de/es/fr` JSON files. 3. Use key via translator: - Web: `useTranslations("Namespace")` or `getTranslations("Namespace")` - Admin: `useAdminT()` or server-side `translateMessage(...)` 4. Add/adjust tests for behavior where relevant. ## Translation Workflow 1. Author English source copy first. 2. Add keys in all supported locales in same change. 3. Keep semantic parity across locales. 4. Run checks: - `bun run check` - `bun run typecheck` - `bun run test` 5. For route-level i18n behavior changes, run e2e smoke: - `bunx playwright test --grep "i18n smoke"` ## QA Checklist - Locale switch persists after refresh. - Page headings and navigation labels translate correctly. - Metadata (`Seo`) strings resolve per locale. - No missing-key placeholders visible in UI. ## Related Files - `apps/web/src/i18n/request.ts` - `apps/web/src/i18n/routing.ts` - `apps/admin/src/i18n/server.ts` - `apps/admin/src/i18n/messages.ts` - `packages/i18n/src/index.ts`