Internationalization (i18n)
Ship multiple languages from day one — locales declared in brand.config, automatic hreflang, and per-entity content translation with AI auto-translate.
Locales
Supported languages live in brand.config.ts:
locales: ["da", "en"] as const,
defaultLocale: "da",defaultLocale is the base language — the source text stored directly in DB fields. Add a language in one place:
locales: ["da", "en", "de"] as const,i18n/routing.ts reads this, so /de/... routes and hreflang alternates appear automatically. A single-locale shop emits no hreflang (no misleading signal).
Translating content
UI strings are managed via i18nexus (messages/<locale>.json). Storefront content is translated in /admin/translations:
| Entity | Translatable fields |
|---|---|
| Product | name, description |
| Category | name, description |
| Page | title, body |
| Service | title, shortDescription, body |
| Post (blog) | title, excerpt, body |
Each entity stores { en: { title: "…" }, de: { … } } in a translations JSON field. At render, getDynamicTranslation() returns the current locale's value, falling back to the base text. Auto-translate is built into the editor.
FX auto-refresh
Refresh exchange rates daily from the ECB feed into a DB override, read as dbRate ?? staticAnchor — display and checkout always resolve the same rate.
Authentication & accounts
Every sign-in method (magic link, password, GitHub, Google), password rules, the forgot/reset flow, and the iron rule that admin is never granted via OAuth.