- Content Studio UI — every label, button, tooltip, dialog
- AI responses — the
summary_for_user,change_log, andsuggested_next_actionscome back in the user’s language
- Block type names —
Hero,CTA,FAQAccordionare code identifiers, not user-facing copy - Model and provider names —
gpt-4o,Claude,OpenAIare vendor brands - Field AI suggestion pills — these are sent as prompts to the LLM and must stay in English
- Preview adapter overlay labels — currently English-only (separate package, needs postMessage protocol extension)
How it works
Two independent layers: Editor UI — A customLocaleProvider + useT() hook (no i18n library). The active locale lives in localStorage("editor-locale"). Translations are typed Record<LocaleKeys, string> so missing keys are compile errors.
AI responses — The editor sends locale on every /chat and /chat/start request. The orchestrator’s localeInstruction() helper injects a language directive into the LLM system prompt, so the structured response fields come back in the right language.
Switching language
In the Content Studio: Settings (gear icon) → Language dropdown → English / Deutsch. The choice persists inlocalStorage and applies to both UI strings and AI responses immediately — no reload.
Adding a new language
Adding French as a worked example. The same recipe works for any language.1. Create the dictionary
apps/editor/src/i18n/fr.ts:
LocaleKeys is derived from en.ts, so TypeScript will complain about every key you haven’t translated yet. pnpm typecheck is your checklist.
2. Register the locale in the provider
apps/editor/src/i18n/index.tsx:
3. Teach the orchestrator the language name
apps/orchestrator/src/chat/prompts.ts:
4. Verify
Files at a glance
| Layer | File |
|---|---|
| Source of truth (English) | apps/editor/src/i18n/en.ts |
| German translation | apps/editor/src/i18n/de.ts |
Provider + useT() hook | apps/editor/src/i18n/index.tsx |
| Orchestrator prompt helper | apps/orchestrator/src/chat/prompts.ts (localeInstruction(), LOCALE_NAMES) |
| Request shape | apps/orchestrator/src/nlp/intent-detection.ts (locale on ChatRequestBody) |
Using translations in code
In React components:{{name}} interpolation syntax is built into useT().
In pure (non-React) functions — pass t as a parameter rather than calling a hook:
Why no i18n library?
The project deliberately doesn’t depend oni18next, react-intl, or similar. Reasons:
- The key set is small and stable (a few hundred strings).
- Compile-time enforcement (
Record<LocaleKeys, string>) catches drift without ICU message format complexity. - One fewer dependency to upgrade.
- The
{{name}}interpolation pattern covers every actual use case the editor has.
i18next — useT()’s signature is small enough to back with anything.
See also
- Quickstart — boot the stack and try the language switcher
- AI Providers — the chat pipeline that consumes the
localefield