---
title: "Language"
description: "Choose the editor language once per app, override single messages, and keep user-facing strings in the German and English catalogs."
canonical_url: "https://skaile-ai.github.io/forge-editor/guides/language"
---
# Language

> Choose the editor language once per app, override single messages, and keep user-facing strings in the German and English catalogs.

## Catalogs

Every string the editor shows (toolbar, slash menu, Block actions, settings, AI task names, comments, notices) comes from the catalogs `src/i18n/{de,en}/*.ts`, keyed by stable dotted ids (`toolbar.bold`, `comments.tab`, `ai.task.translate.label`); `de` is the default and the source of the key set, `en` has exactly the same keys (a missing one is a type error). Catalog helpers are exported by `/core`: `createTranslator`, `CATALOGS`, `LOCALES`, `DEFAULT_LOCALE`, `isLocale`, `Locale`, `MessageKey`, `Messages`, `Translate`.

## Choosing the language

Choose the language once per app: `createEditorContext({ locale: 'en', messages: { 'toolbar.bold': 'Strong' } })` (React and framework-free hosts), `app.use(createEditor({ locale, messages }))` (Vue), or the Nuxt module options `tiptapEditor: { locale: 'en', messages: { … } }` (JSON-serialisable; forwarded to the client plugin). `messages` is a partial catalog applied on top of `locale`; `{name}` placeholders take `params`. An unknown locale throws. The locale is fixed for the life of the context: a host that switches languages at run time recreates the context (Vue: remount the app or key it; React: new `EditorContextProvider` value).

## Messages in features and components

Features read `ctx.t` (`FeatureContext.t`, `EditorContext.t`, `ctx.locale`) in their factories: toolbar items, slash commands, Block actions, conversion labels (`ConversionProvider.label(target, t)`) and `settingsTabs(ctx)` are built in the context's language. Components call `useEditorI18n()` (`/vue` and `/react`) and get `{ t, locale }`. A feature of your own adds its keys by passing its own strings or by extending the `messages` override; the shipped keys are the `MessageKey` union.

Setting values stored as ids (`'margin'`, `'Deutsch'`, `'flat'`, …) never change with the language: only their display label comes from the catalog (`*_KEYS` tables mapping an id to a key; `authorStyleItems(t)`, `defaultBlockTypes(t)`).

## Markdown

Markdown: the source view of `MarkdownSurface` (aria label, Apply, Cancel; keys `markdown.source.*`; override through `messages`) and the `message` of every `MarkdownDiagnostic` (`markdown.diagnostic.*`) are in the context's language. `markdownToContent(editor, body, { policy, t })` and `createSourceMode({ getEditor, t })` take an explicit translator; without one they use the editor's registered surface, else the default locale.

## The i18n check

`bun run check:editor-i18n` (`packages/editor/checks/i18n.ts`) fails when a user-facing literal (label/description/placeholder/aria/alt/title properties and attributes, JSX and template text, German words) appears in `src` outside `src/i18n`, or when a catalog key is unused or missing in `en`. Text that is not UI (model prompts, stored ids, developer errors) is marked `// i18n-ignore: <reason>`; the run lists every honoured pragma.


## Sitemap

See the full [sitemap](https://skaile-ai.github.io/forge-editor/sitemap.md) for all pages.
