Documentation index for AI agents (llms.txt). Markdown versions of every page are available by appending .md to the page URL. The full corpus is at /llms-full.txt.

Languages

Translate your documentation by adding a languages.json file and one folder per language. Each language gets its own URL prefix, its own sidebar, a switcher in the sidebar, hreflang tags for search engines, and search and AI chat that stay inside the language the reader is looking at.

Languages are entirely opt-in. Without a languages.json file nothing changes: your docs live at the root of the docs folder and publish exactly as before.

Configuration

Create languages.json in your project root, next to doccupine.json:

[
  { "code": "en", "label": "English", "default": true },
  { "code": "de", "label": "Deutsch" }
]
  • code: a lowercase URL segment (letters, digits, and hyphens, such as en, de, or pt-br). It is the folder name, the URL prefix, and the value of the page's lang and hreflang attributes.
  • label: the name shown in the language switcher.
  • default: exactly one entry must be the default. Its pages live at the docs root and keep unprefixed URLs.
  • strings (optional): overrides for the site's own interface text in that language. See Interface strings.

The order of the entries is the order of the switcher.

Folder layout

The default language lives at the root of the docs folder. Every other language lives in a folder named after its code, mirroring the default layout:

docs/
  index.mdx              -> /
  guides/
    intro.mdx            -> /guides/intro
  de/
    index.mdx            -> /de
    guides/
      intro.mdx          -> /de/guides/intro

A page is the translation of another when it sits at the same path inside its language folder. Only translated pages exist in a language: a page you have not translated yet returns a 404 under that language's prefix, and the switcher takes readers to that language's home page instead.

A language folder without an index.mdx gets a generated home page that redirects to its first page.

What the generated site does

  • Switcher: the sidebar footer row, between the focus-mode and theme toggles, shows a language dropdown when at least two languages are configured: a 30px pill carrying the current language that opens a menu of every language above it (the same dropdown the Doccupine platform uses), with the arrow keys, Escape, and a click outside to close it. When a version switcher shares the row, the two pills grow from their labels' widths to fill the sidebar's free width, and on the narrow desktop rail each shows its short form: the language's code and the version's own label. Switching goes to the same page in the chosen language when it exists, otherwise to that language's home.
  • Sidebar and sections: the sidebar lists only the pages of the current language. Sections work inside every language; a section tab is hidden in a language that has no pages in it.
  • Search and AI chat: results and answers come from the current language only. The MCP server's search_docs and list_docs tools accept a language parameter and default to the default language.
  • SEO: every translated page carries alternates.languages (hreflang) links to its other languages, an x-default pointing at the default language, and an Open Graph locale. The sitemap lists the same alternates. The <main> element carries the page's lang attribute; the <html lang> attribute is the default language server-side and is corrected from the URL before the first paint.
  • Agents: each language has its own llms.txt and llms-full.txt under its prefix (/de/llms.txt), and the root files link to them.

Frontmatter navigation is built per language automatically. To define a language's sidebar by hand, key navigation.json by URL prefix: "de" for the German root, "de/api" for the German API section. See Navigation.

Interface strings

The generated site's own labels (the search box, "On this page", the previous and next links, the action bar, the footer) are English by default. Override any of them per language with a strings object:

[
  { "code": "en", "label": "English", "default": true },
  {
    "code": "de",
    "label": "Deutsch",
    "strings": {
      "searchPlaceholder": "Dokumentation durchsuchen...",
      "onThisPage": "Auf dieser Seite",
      "previous": "Zurück",
      "next": "Weiter"
    }
  }
]

Keys you leave out keep their default. The known keys are searchButton, searchDialog, searchPlaceholder, searchResults, searching, noResults, resultsAvailable (with a {count} placeholder), resultAvailable, askAi, askAiAssistant, closeSearch, onThisPage, previous, next, openNavigation, closeNavigation, expandGroup and collapseGroup (both with a {title} placeholder), enterFocusMode, exitFocusMode, focusMode, copyContent, copied, copyCode, codeVariants, rss, rssFeed, viewAsMarkdown, toggleView, poweredBy, githubLink, aiAssistant, resetChat, chatGreeting, chatPlaceholder, chatInputLabel, chatAnswering, chatError, language, and version. An unknown key is reported when the site is generated and ignored.

Rules

  • A language code must not be the slug of a section, the first folder of a section's directory, a version slug, or one of the reserved segments api, gate, mcp, and ingest. The build stops with a message naming the file when it is.
  • The default language has no folder: a docs/en/ folder on a site whose default is en stops the build.
  • When versions are configured too, the language folder comes first: docs/de/v1/.
  • The API reference generated from an OpenAPI spec is published in the default language only.