Jamdesk Documentation logo

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:

docs.json
{
  "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

CodeLanguageCodeLanguage
enEnglishkoKorean
esSpanishpt-BRPortuguese (Brazil)
frFrenchruRussian
deGermanarArabic
itItalianhiHindi
jpJapaneseidIndonesian
cnChinese (Simplified)trTurkish
zh-HantChinese (Traditional)viVietnamese
nlDutchplPolish
svSwedishcsCzech
noNorwegianroRomanian
heHebrewuaUkrainian
lvLatvianuzUzbek

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.

docs.json
{
  "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:

LanguageURL
English (default)docs.example.com/introduction
Spanishdocs.example.com/es/introduction
Frenchdocs.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.

TranslateKeep identical
info.title, info.descriptionopenapi / swagger version, servers[*].url
Per-operation summary, descriptionURL paths, HTTP methods, operationId, tags
Per-parameter descriptionParameter names (name), field names, schema property keys
Per-response descriptionStatus code keys ("200", "400", etc.)
requestBody.descriptionenum 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

1
Start with English

Write your docs in English first. This becomes your source of truth.

2
Add language directories

Create directories for each target language (es/, fr/, etc.).

3
Translate content

Copy English files to language directories and translate. Keep filenames the same.

4
Update docs.json

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.

What's Next?

Navigation Overview

Configure tabs, groups, and page structure

docs.json Reference

Full configuration options