This page is about the editor’s own language — the editor’s chrome
and the language the AI answers in. If you are looking for how to edit content
that exists in several languages, that is
Multilingual content. The two are unrelated: a
German-speaking editor can work on an English-only site, and an English-speaking
one can edit a tri-lingual site.
- the editor 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 editor: more-options (…) button in the chat header → Settings → Language → 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
Two edits: widen theLocale union and add the dictionary to LOCALES.
resolveLocale() reads membership in LOCALES, so it needs no change, and
apps/editor/src/i18n/resolve-locale.test.ts fails the build if that ever
regresses to a hardcoded locale code.
apps/editor/src/i18n/index.tsx:
3. Check the orchestrator knows the language name
packages/orchestrator-core/src/chat/prompts.ts:
en entry — localeInstruction() returns nothing for en or an absent locale, because
English needs no instruction. Nine languages are already listed, so for most locales this
step is a no-op; add a row only if yours is missing.
4. Verify
Files at a glance
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