Multi-Language Support
Serve documentation in multiple languages with a language switcher. Each language gets its own navigation structure and translated content.
If your docs need to reach users in more than one language, you can define separate navigation trees per locale and let readers switch with a dropdown in the top bar.
Jamdesk does not translate content for you. You provide the translated MDX files — Jamdesk handles routing, navigation, the language switcher, and RTL styling. Bring translations from your own workflow (human translators, machine translation, or an LLM) and drop them into language-prefixed directories.
Configuration
Wrap your navigation in a languages array, with each language containing its own navigation structure:
{
"navigation": {
"languages": [
{
"language": "en",
"tabs": [
{
"tab": "Documentation",
"groups": [
{
"group": "Getting Started",
"pages": ["introduction", "quickstart"]
}
]
}
]
},
{
"language": "es",
"tabs": [
{
"tab": "Documentación",
"groups": [
{
"group": "Comenzar",
"pages": ["es/introduction", "es/quickstart"]
}
]
}
]
}
]
}
}Supported Languages
| Code | Language | Code | Language |
|---|---|---|---|
en | English | ko | Korean |
es | Spanish | pt-BR | Portuguese (Brazil) |
fr | French | ru | Russian |
de | German | ar | Arabic |
it | Italian | hi | Hindi |
jp | Japanese | id | Indonesian |
cn | Chinese (Simplified) | tr | Turkish |
zh-Hant | Chinese (Traditional) | vi | Vietnamese |
nl | Dutch | pl | Polish |
sv | Swedish | cs | Czech |
no | Norwegian | ro | Romanian |
he | Hebrew | ua | Ukrainian |
lv | Latvian | uz | Uzbek |
Directory Structure
Organize translated content in language-prefixed directories:
my-docs/
├── docs.json
├── introduction.mdx # English (default)
├── quickstart.mdx
├── es/
│ ├── introduction.mdx # Spanish
│ └── quickstart.mdx
├── fr/
│ ├── introduction.mdx # French
│ └── quickstart.mdx
└── de/
├── introduction.mdx # German
└── quickstart.mdx
Reference pages in navigation using their full path (with the language prefix):
{
"language": "es",
"tabs": [
{
"tab": "Documentación",
"groups": [
{
"group": "Comenzar",
"pages": ["es/introduction", "es/quickstart"]
}
]
}
]
}
Language-Specific Settings
Each language can have its own configuration:
{
"navigation": {
"languages": [
{
"language": "en",
"banner": {
"content": "Version 2.0 is now live! See our [changelog](/changelog).",
"dismissible": true
},
"tabs": [...]
},
{
"language": "es",
"banner": {
"content": "¡La versión 2.0 está disponible! Ver [registro de cambios](/es/changelog).",
"dismissible": true
},
"tabs": [...]
}
]
}
}
Translating Navbar Labels
Top-nav links and the primary CTA accept an optional labels object with per-language overrides. When the reader is on a language-prefixed URL (e.g. /fr/...), the matching override is used; otherwise the default label is shown.
{
"navbar": {
"links": [
{
"label": "Blog",
"labels": { "fr": "Blog", "es": "Blog" },
"href": "/blog"
},
{
"label": "Pricing",
"labels": { "fr": "Tarifs", "es": "Precios" },
"href": "/pricing"
}
],
"primary": {
"type": "button",
"label": "Dashboard",
"labels": { "fr": "Tableau de bord", "es": "Panel" },
"href": "https://app.example.com"
}
}
}Built-in UI strings — the Search button, Ask AI button, and the More tabs dropdown — are translated automatically for every supported language. You don't need to configure those.
Default Language
The first language in the array is the default. Users landing on your docs see this language first. The language switcher allows them to change.
URL Structure
Language prefixes appear in URLs:
| Language | URL |
|---|---|
| English (default) | docs.example.com/introduction |
| Spanish | docs.example.com/es/introduction |
| French | docs.example.com/fr/introduction |
Partial Translations
You don't need to translate every page. If a page doesn't exist in a language, users see a fallback message with a link to the English version.
For pages that shouldn't be translated (like API reference), you can reference the same page across languages:
{
"language": "es",
"tabs": [
{
"tab": "API",
"groups": [
{
"group": "Endpoints",
"pages": ["api/users", "api/posts"] // Same as English
}
]
}
]
}
Translating OpenAPI Specs
OpenAPI-driven endpoint pages (those with an openapi: frontmatter directive) render content from a YAML or JSON spec file. To translate the endpoint summary, descriptions, parameter hints, and schema field descriptions, provide a language-specific spec file alongside your English one.
File naming
Place the translated spec next to the source, inserting the language code before the extension:
openapi/
├── api.yaml # English (default)
├── api.fr.yaml # French
├── api.es.yaml # Spanish
└── api.zh.yaml # Simplified Chinese
No configuration or docs.json change is required. Jamdesk resolves the matching spec at render time based on the URL's language prefix. A page at /fr/api-reference/create-ticket looks for api.fr.yaml first, falling back to api.yaml if no translation exists.
What to translate in the spec
Translate human-readable prose. Keep every structural value identical across languages.
| Translate | Keep identical |
|---|---|
info.title, info.description | openapi / swagger version, servers[*].url |
Per-operation summary, description | URL paths, HTTP methods, operationId, tags |
Per-parameter description | Parameter names (name), field names, schema property keys |
Per-response description | Status code keys ("200", "400", etc.) |
requestBody.description | enum values (low, normal, high), type, format |
Schema description, property description | $ref pointers, example / examples payload contents |
Fallback behavior
If a page is requested under a localized URL but no translated spec exists, Jamdesk renders the English spec inside the translated page shell. Users see a mixed-language endpoint block rather than a 404. This matches the broader partial-translations behavior for MDX pages.
The jamdesk CLI's local preview (jamdesk dev) resolves OpenAPI specs by filename only. It doesn't yet apply the language suffix lookup. When iterating on translations locally, use the production Jamdesk preview URL (<project>.jamdesk.app/<lang>/...) or swap the source file temporarily. This only affects local development; hosted and ISR renders pick the translated spec correctly.
Removing a language-specific spec
Deleting api.<lang>.yaml causes the page to fall back to the English spec on the next render (no rebuild required). To retranslate from scratch, delete the file and regenerate. No docs.json change is needed — spec resolution is purely filename-based.
Example
Source openapi/tickets.yaml:
info:
title: Tickets API
description: Manage support tickets.
paths:
/tickets:
post:
summary: Create a ticket
description: Create a new support ticket.
operationId: createTicket
French translation openapi/tickets.fr.yaml:
info:
title: API Tickets
description: Gérez les tickets de support.
paths:
/tickets:
post:
summary: Créer un ticket
description: Créer un nouveau ticket de support.
operationId: createTicket
Notice that /tickets, post, and createTicket stay identical. Only the prose changes.
Translation Workflow
Write your docs in English first. This becomes your source of truth.
Create directories for each target language (es/, fr/, etc.).
Copy English files to language directories and translate. Keep filenames the same.
Add language entries to your navigation config.
RTL Languages
Right-to-left languages like Arabic and Hebrew are supported. Jamdesk automatically applies RTL styling when these languages are active.
