Skip to main content
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.
Avocado Studio ships two languages: English (default) and German. The system is built to make adding more locales a small, mechanical change — one new dictionary file, two lines of glue, one entry in a prompt helper. What’s localized:
  • the editor UI — every label, button, tooltip, dialog
  • AI responses — the summary_for_user, change_log, and suggested_next_actions come back in the user’s language
What’s not (intentionally):
  • Block type namesHero, CTA, FAQAccordion are code identifiers, not user-facing copy
  • Model and provider namesgpt-4o, Claude, OpenAI are 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 custom LocaleProvider + 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 in localStorage 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 the Locale 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:
This is what gets interpolated into the prompt: “Respond in French.” Note there is no 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

That’s the full integration. No build step, no separate translation pipeline, no external service.

Files at a glance

Using translations in code

In React components:
The {{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 on i18next, 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.
If your fork needs plurals, gender, or ICU message format, swap in i18nextuseT()’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 locale field