How Mintlify handles languages
Each language is a standalone navigation tree undernavigation.languages in docs.json. Pages live at <lang>/<page>.mdx (no prefix for the default language). Mintlify renders a language switcher in the topbar automatically when more than one language is present.
docs.json uses the simpler navigation.tabs shape (English only). Adding a second language means migrating the existing config into the languages block and adding a parallel one.
Add a language end-to-end
1
Migrate docs.json to the languages shape
Move the existing Keep the
navigation.tabs array under a single en language object, then add an empty zh (or your target) language alongside it:zh tree small at first — Mintlify is happy to ship a partial translation. Pages that don’t exist in the target language fall back to the URL the user had open, but no automatic English-fallback render: the language switcher only offers languages where the current page exists.2
Create the parallel folder
Mirror the file paths under a top-level Frontmatter
zh/:title and description should be in the target language; the rest of the page is whatever you’d write in markdown.3
Translate brand-neutral terms only
Mirror the policy from the main app: brand names (privacytracker, App Store, Apple Configurator, ToS;DR, PrivacySpy), shell commands, file paths, and code identifiers stay in English. Headings, paragraph text, table headers, and component captions translate.Code blocks should stay byte-identical to the English versions — readers copy-paste them.
4
Translate links carefully
Internal links inside not:inside translated pages.
zh/ should point at zh/-prefixed paths so the language stays consistent across navigation. Mintlify rewrites links automatically only when they’re relative; absolute paths (like /develop/architecture) stay literal. Use:5
Update the OpenAPI tab carefully
The auto-generated API Reference pages (under the
Reference group) read from api-reference/openapi.yaml. Currently they’re rendered once. To translate them, you’d either maintain a parallel api-reference/openapi.zh.yaml (and reference it under the zh tab) or accept that endpoint reference pages stay in English in every locale. The latter is the standard choice for OSS projects — endpoint summaries are usually short enough that locale-mixing is tolerable.6
Run the parity check
There’s no built-in equivalent of Empty diff = parity. Anything in only the first list is missing a translation.
npm run lint:i18n for the docs (yet) — the analogous check is “every English page has a counterpart under zh/”:7
Preview locally
navigation.languages has more than one entry.Recommended translation workflow
We don’t currently use Crowdin for the docs site (the main app uses it for UI strings). Two options work well: Manual via PRs. Translators fork the docs repo, work inzh/<page>.mdx, and open a PR per page or per group. Lower coordination cost; suits small docs sites and infrequent updates.
Crowdin for docs. Crowdin supports MDX. If translation activity warrants it, a crowdin.yml mirroring the app’s setup but pointing at the docs repo would put both flows in the same tool. For the volume of docs we ship today, the manual path is simpler.
Keeping translations in sync with English
When the English source changes, the correspondingzh/ page is now out of date. Two safety nets:
- A
last-reviewedfrontmatter field that translators bump when they confirm a page matches the current English version. The link-check workflow can be extended to flag pages where the English file has been modified since the translation was last reviewed. - A “translation status” page in each language (e.g.
zh/translation-status.mdx) listing which pages are up-to-date, stale, or missing. Tedious to maintain by hand; trivial to generate fromgit logif you want a CI script.
Adding a third language
Oncezh is set up, adding ja or es is purely additive: another entry in navigation.languages, another <lang>/ folder, more PRs from translators. Mintlify imposes no upper limit.
What not to translate
- Frontmatter keys (
title,description) — translate the values, never the keys. - Component names (
<Card>,<Steps>,<AccordionGroup>) — these are JSX and must stay literal. - File names and paths —
/develop/architectureis a URL, not English prose. docs.jsonstructure — the language-specific config goes insidelanguages[i], but the surrounding shape is fixed.