BabelizeBabelize
Features

Markdown & MDX Docs

Translate a docs site's Markdown and MDX pages with babelize translate: only the text readers see, code and JSX untouched, one paragraph at a time, in Fumadocs' i18n layout.

babelize translate translates documentation pages as well as app strings. Point it at your .md and .mdx files and it writes a translated copy of each page, translating only the text a reader sees. Code, imports, JSX and URLs are left exactly as written.

Docs translation runs locally, in the CLI and the MCP server. Cloud runs from the dashboard or the GitHub App don't translate docs yet.

Quick start

Add docs to .babelize/config.json:

.babelize/config.json
{
  "sourceLocale": "en",
  "docs": { "include": ["content/docs/**"] }
}

Then translate:

npx babelize translate --to es,ja --docs-only   # only the docs
npx babelize translate --to es,ja               # the app first, then the docs

Each page gets a translated copy next to it, and .babelize/local/docs-lock.json records every paragraph's translation. Review the result with git diff, then commit both.

content/docs/install.mdx
content/docs/install.es.mdx    ← new
content/docs/install.ja.mdx    ← new
content/docs/meta.json
content/docs/meta.es.json      ← new (sidebar labels)

What gets translated

TranslatedLeft exactly as written
Frontmatter title, description, summary, sidebarTitle, labelOther frontmatter fields (icon, custom keys)
Headings, paragraphs, list items, table cells, blockquotesCode blocks and inline code
Readable JSX props: title, description, label, alt, placeholder, aria-label, caption, summary, heading, subtitleimport / export lines, {expressions}, comments
Text inside JSX components (<Callout>…</Callout>)JSX tags and other props, URLs, HTML inside <pre>, <code>, <kbd>
meta.json sidebar labels: title, description, ---Separators---, link textPage names, icons and link URLs in meta.json

Inline formatting survives translation. Bold, italics, links and images travel to the translator as numbered tags (<1>the guide</1>), and inline code and other syntax as placeholders ({p0}). Every translation is checked on the way back. If a tag or placeholder was dropped or repeated, that paragraph keeps its English text, is listed in the summary, and is retried on the next run. Characters that would change the page's structure (<, {, }, | in tables, a leading #) are escaped, so the translated page always compiles.

The glossary, brand voice and formality from .babelize/config.json apply to docs just as they do to app strings.

Docs frameworks

FrameworkStatus
FumadocsSupported. Both of its i18n parsers: dot (page.ja.mdx, the default) and dir (ja/page.mdx). meta.json sidebars are translated.
Docusaurus, Nextra, Mintlify, VitePress, Starlight, plain MarkdownNot tested. The translated pages are valid Markdown/MDX, but these tools expect translations in their own folder layout (Docusaurus: i18n/<locale>/docusaurus-plugin-content-docs/…). Set "anchors": false (see below) unless the tool reads [#id].

For the dir layout, give the docs root:

.babelize/config.json
{
  "docs": { "include": ["content/docs/**"], "layout": "dir", "root": "content/docs" }
}

All docs keys are listed in Project Config.

Heading anchors

A translated heading would normally get a new id from its translated text, which breaks links like #installation. Babelize keeps the English heading's id instead, in Fumadocs syntax:

## Instalación [#installation]

Repeated headings are numbered the way GitHub and Fumadocs number them (-1, -2), and headings that already have an id keep it. Turn this off with "anchors": false for docs tools that don't read [#id].

Re-runs and the lockfile

.babelize/local/docs-lock.json stores each paragraph's translation, keyed by its English text. Commit it.

  • Edit a sentence and only that paragraph is translated again. Unchanged paragraphs come from the lockfile, which costs no credits or tokens.
  • Text that no page contains any more is dropped from the lockfile at the end of a run. Translations into languages you didn't pass this run are kept.
  • Nothing to translate means no changes. A run that finds every paragraph in the lockfile reports No changes: every page is up to date.
  • Edits to a translated page are overwritten on the next run, because the page is rebuilt from the English page and the lockfile. To correct a translation, change its entry in docs-lock.json.
  • A page where nothing could be translated is not written, for example when the translator is down. Your docs framework falls back to the English page, and the next run retries.

Formatting

Translated pages and meta.*.json files are formatted with your project's own Prettier or Biome, using your config and ignore file, before they're compared with what's on disk. They pass your format check, and an unchanged page isn't rewritten on every run.

Choosing a translator

Docs are long-form prose, so the translator matters more than for UI strings.

Your own LLM key (BYOK)Babelize-hosted
Quality on proseNatural. Keeps markup reliably.Basic machine translation. Some paragraphs lose markup and stay in English.
SpeedFastAbout 4 s per short string, 8 s per paragraph
Best forDocsUI strings

When the docs run on Babelize-hosted translation, the CLI says so and suggests a key. Set one of ANTHROPIC_API_KEY, OPENAI_API_KEY, GEMINI_API_KEY, GROQ_API_KEY or OPENROUTER_API_KEY (see BYOK).

Linking between languages

Babelize translates the text of links, not their targets: a link to /docs/install on the Japanese page still points to /docs/install. To keep readers in their language, prefix internal links with the page's locale when you render them. In Fumadocs, override the MDX a (and Card) components and map the page tree's URLs. babelize-elements does this in src/lib/localized-href.ts.

Limitations

  • Translator quality. With Babelize-hosted translation, expect some paragraphs to stay in English and plainer wording. Use your own LLM key for docs.
  • Fumadocs layouts only. Other docs tools need their own folder structure (see Docs frameworks).
  • Line-based parsing. Babelize reads Markdown line by line, not as a full syntax tree. Ordinary docs work, but very unusual structure can be skipped or kept in English rather than broken: raw HTML blocks, deeply nested lists, JSX props split over several lines. Line breaks inside a paragraph are joined in the translation.
  • Same-script text in another language (a French quote in English docs) is translated like the rest. Text in another script, such as an Arabic or Japanese sample, is left as written in app code, not in docs.
  • Local only. Cloud runs and GitHub pull requests don't translate docs yet.

Example

elements.babelize.co is translated this way. Its docs in Spanish, Japanese and Arabic are translated from 13 MDX pages and 9 sidebar files, with Fumadocs i18n, per-language search and hreflang. The source is babelize/babelize-elements.

Last updated: 2026-11-01

How is this guide?

On this page