---
title: Multi-Language Support
description: Add first-class multi-language support to your Thally docs site with build-time translations, automatic fallbacks, and a built-in language switcher.
url: http://localhost:3040/guides/multi-language
---

# Multi-Language Support

Add first-class multi-language support to your Thally docs site with build-time translations, automatic fallbacks, and a built-in language switcher.

Thally supports build-time multi-language documentation. Translated MDX files live in your repo alongside your primary content — fully version-controlled, statically generated, and indexed by search engines with real locale-specific URLs.

  `/es/introduction`, `/fr/guides/getting-started` — every locale gets its own indexable URL, never a client-side redirect.

  One command translates your entire docs site. Code blocks, component tags, and URLs are preserved automatically.

  Untranslated or stale pages fall back to the primary language gracefully, with informative banners instead of 404s.

  A globe icon appears automatically in the top bar as soon as you configure two or more locales.

---

## URL structure

The default locale is served without a URL prefix — fully backward-compatible with any existing links. Secondary locales are prefixed with their locale code:

| URL | Content |
|-----|---------|
| `/introduction` | English (default) |
| `/es/introduction` | Spanish |
| `/fr/introduction` | French |
| `/guides/getting-started` | English (default) |
| `/es/guides/getting-started` | Spanish |

Adding a new locale never breaks existing URLs.

---

## Setup

Open `docs.json` at your project root and add an `i18n` key:

```json
{
  "i18n": {
    "defaultLocale": "en",
    "locales": [
      { "code": "en", "label": "English" },
      { "code": "es", "label": "Español" },
      { "code": "fr", "label": "Français" }
    ]
  },
  "tabs": [...]
}
```

`defaultLocale` must match one of the locale codes. The locales array controls the order items appear in the language switcher dropdown.

Once this is saved, a globe icon appears in the top bar automatically.

Run the translate command to generate translated MDX files for a locale:

```bash
npx create-thally-docs translate --locale es
```

This calls Claude to translate every content page defined in your `docs.json` and writes the results to `src/content/es/`. You'll see a live progress list as each page completes, and a summary at the end.

**Translate specific pages only:**

```bash
npx create-thally-docs translate --locale es --pages introduction,guides/getting-started
```

**Overwrite existing translations:**

```bash
npx create-thally-docs translate --locale es --force
```

**All options:**

| Flag | Description |
|------|-------------|
| `--locale <code>` | Target locale — required. Must match a code in your `docs.json` i18n config. |
| `--pages <ids>` | Comma-separated page IDs to translate. Omit to translate all pages. |
| `--force` | Overwrite existing translation files. |
| `--api-key <key>` | Anthropic API key. Falls back to `ANTHROPIC_API_KEY` env var. |
| `--model <id>` | Claude model to use. Default: `claude-sonnet-4-6`. |
| `--yes`, `-y` | Skip the confirmation prompt. |

The translate command calls Claude via the Anthropic API. Set `ANTHROPIC_API_KEY` in your environment or pass `--api-key` directly. Translating a typical docs page costs a fraction of a cent.

After translation, your content directory will look like this:

```
src/content/
├── introduction.mdx            ← primary (English, unchanged)
├── guides/
│   └── getting-started.mdx
├── es/                         ← Spanish translations
│   ├── introduction.mdx
│   └── guides/
│       └── getting-started.mdx
└── fr/                         ← French translations
    └── introduction.mdx
```

Translated files are plain MDX — you can edit them just like any other content. Commit them to your repo and deploy as normal.

---

## Translation quality

Claude translates with these rules applied automatically:

- **Translates:** all prose text, headings, frontmatter `title`, `description`, and `keywords`
- **Preserves:** MDX component tags verbatim (``, ``, ``, etc.)
- **Preserves:** all code blocks and inline code exactly as written
- **Preserves:** all URLs, file paths, and import statements
- **Preserves:** all frontmatter structure and non-text fields

If a translation needs adjusting, edit the file in `src/content/{locale}/` directly. You can also re-run the command for specific pages:

```bash
npx create-thally-docs translate --locale es --pages guides/getting-started --force
```

---

## Fallback behavior

Thally never returns a 404 for a missing translation. Instead, it serves the primary-language content with an informative banner:

**Missing translation** — the page hasn't been translated for that locale yet:

> ⚠️ This page hasn't been translated to **Español** yet. Showing the original **English** version.

**Stale translation** — the primary-language file was updated after the translation was last generated:

> This translation may be outdated. [View original →]

Both banners appear only on secondary-locale routes. The primary route is never affected.

To fix a stale or missing translation, run the translate command for that page:

```bash
npx create-thally-docs translate --locale es --pages guides/getting-started
```

---

## Language switcher

Once you have two or more locales configured, a **globe icon** appears in the top bar automatically. Clicking it opens a dropdown listing all configured locales.

Selecting a locale navigates to the same page in that locale — for example, switching from `/es/guides/getting-started` to French takes you directly to `/fr/guides/getting-started`. The switcher always keeps you on the current page rather than sending you to a home page.

---

## API Reference

The API Reference tab is auto-generated from your OpenAPI spec. Its content is always in the primary language — OpenAPI specs define machine-readable contracts, not prose content.

When navigating to the API Reference tab from a locale page, the URL prefix is preserved so you remain in the locale context (`/es/api/...`). The sidebar and tab bar stay locale-aware; only the operation content itself comes from the spec unchanged.

To change the language of API Reference descriptions and summaries, update them directly in your OpenAPI spec file.

---

## SEO

When an `i18n` config is present, Thally automatically adds `hreflang` and `canonical` link tags to every page:

```html
<link rel="canonical" href="https://docs.example.com/es/introduction" />
<link rel="alternate" hreflang="en" href="https://docs.example.com/introduction" />
<link rel="alternate" hreflang="es" href="https://docs.example.com/es/introduction" />
<link rel="alternate" hreflang="fr" href="https://docs.example.com/fr/introduction" />
<link rel="alternate" hreflang="x-default" href="https://docs.example.com/introduction" />
```

Each translated page is self-canonical. Reciprocal `hreflang` links connect every locale, and `x-default` points to the default-language page. Set `THALLY_SITE_URL` so the correct domain appears in these tags:

```bash
THALLY_SITE_URL=https://docs.example.com
```

---

## Using the MCP tool

If you use the Thally MCP server, you can translate docs directly from your AI assistant:

```json
{
  "name": "translate_docs",
  "arguments": {
    "projectDir": "/path/to/my-docs",
    "locale": "es",
    "force": false
  }
}
```

The MCP tool accepts the same options as the CLI. See [MCP Server](/guides/mcp-server) for setup instructions.

---

## Scaffold with i18n

When creating a new project, you can enable multi-language support from the start:

```bash
npx create-thally-docs my-docs
```

When asked **"Enable multi-language support?"**, enter `y` and provide your locale codes (e.g. `es,fr,de`). The `i18n` block is written to `docs.json` automatically.

---

  Full reference for `translate` and all other create-thally-docs commands.

  Set up the Thally MCP server to translate docs from your AI assistant.