Nextra integration
Use init -t ui-nextra and docsOutput.style: "nextra" for Nextra 4 documentation sites on Next.js App Router. The preset is an alias for doc-system with an empty localeSubpath and BCP-47 locale folder names preserved (localePathLowercase defaults to false, so folders stay pt-BR, zh-Hans, etc.).
See also Documents, and the runnable examples/nextra-docs demo.
Quick start
ai-i18n-tools init -t ui-nextra [-P <provider>]
# edit ai-i18n-tools.config.json (targetLocales, providers, contentPaths)
pnpm run i18n:sync # or: ai-i18n-tools sync
pnpm run build # Next.js build (project-specific script)Enable features.translateDocs when you translate page content, _meta.ts sidebar labels, and theme dictionary modules in one sync run.
Page layout
Nextra 4 with i18n keeps English source MDX under a locale folder (typically content/en/). Translated copies are written to sibling locale folders:
content/en/index.mdx → content/pt-BR/index.mdx
content/en/guide/getting-started.mdx → content/zh-Hans/guide/getting-started.mdxConfigure one docs[] block:
{
"contentPaths": ["content/en"],
"outputDir": "content",
"docsOutput": {
"style": "nextra",
"docsRoot": "content/en",
"rewriteNextraLinks": true
}
}Point contentPaths at your English .mdx files and directories. Set docsRoot to the English locale folder inside content/.
Wire Nextra internationalization: set i18n.locales and defaultLocale in next.config, and keep targetLocales in ai-i18n-tools.config.json aligned with those locale codes and the content/{locale}/ folder names.
Theme strings
Nextra theme chrome (editLink, search placeholder, footer, and so on) is not extracted from markdown. Author English strings in a TypeScript dictionary module (for example app/_dictionaries/en.ts) and translate it inside translate-docs:
{
"features": {
"translateDocs": true
},
"docs": [
{
"contentPaths": ["content/en"],
"outputDir": "content",
"nextraDictionaryPath": "app/_dictionaries/en.ts",
"docsOutput": {
"style": "nextra",
"docsRoot": "content/en"
}
}
]
}The tool writes app/_dictionaries/{locale}.ts (default template: {dir}/{locale}.ts). Load the per-locale module in app/_dictionaries/get-dictionary.ts and pass translated strings to <Layout>, <Search>, <Footer>, and related theme components.
Do not use json[] for Nextra theme dictionary strings — that pattern is for unrelated app locale bundles only.
Sidebar labels (_meta.ts)
Nextra 3+ uses TypeScript _meta.ts / _meta.tsx files for sidebar structure and titles. When docsOutput.style is "nextra", translate-docs automatically collects _meta.ts, _meta.tsx, and _meta.js under docsRoot, translates string literals in the export default { … } meta map, and writes mirrored files under content/{locale}/**.
Recommended pattern: keep English literals inline in content/en/**/_meta.ts (same as swr-site):
content/en/_meta.ts English sidebar labels (source)
content/pt-BR/_meta.ts Translated copy (generated by translate-docs)Optional: override collection with docs[].nextraMetaGlob or restrict translatable property names with docs[].nextraMetaTranslatableKeys (default: title, display, breadcrumb).
Do not hand-author JSON sidecars (i18n/meta.en.json) or thin _meta.ts files that import translated JSON — regenerate locale _meta files with sync / translate-docs when English changes.
Example project
examples/nextra-docs — English sources at content/en/, committed pt-BR and zh-Hans page trees, inline _meta.ts files, and app/_dictionaries/{locale}.ts. Run pnpm run dev on port 3070.
Optional: t() for app/ React (hybrid)
Default: _meta.ts / _meta.tsx object-literal strings are translated inside translate-docs — no t() required.
Optional hybrid: teams may additionally use t() + translate-ui for app/ layout chrome, custom MDX components, or _meta.tsx labels that live only inside JSX component bodies (beyond object-literal extraction in v1). This does not replace translate-meta for meta files unless you explicitly refactor sidebar labels into components.
| Content | Default pipeline | Optional alternative |
|---|---|---|
| MDX page bodies | translate-docs | — |
_meta.ts / _meta.tsx object titles | translate-docs | refactor to t() in JSX (hybrid) |
app/ layout, _components/ | nextraDictionaryPath + dictionary .ts | t() + translate-ui |
Example AI agent prompt (copy into Cursor or another coding agent when migrating layout chrome to t()):
Add i18n to our Nextra 4 app/ layout using ai-i18n-tools translate-ui (optional hybrid).
Context:
- We already translate MDX pages and _meta.ts via translate-docs (default).
- We want t() in app/[lang]/layout.tsx and app/_components/ for labels not covered by nextraDictionaryPath.
- English-as-key: t("Edit this page on GitHub") in source; strings.json + locales/{locale}.json from extract + translate-ui.
- Do not move _meta.ts sidebar labels into t() unless we explicitly ask — translate-docs handles _meta object literals.
Requirements:
1. Wire getRequestConfig / i18n provider for the app router locale param.
2. Replace hard-coded layout strings with t() calls; keep structure and Nextra theme APIs unchanged.
3. Enable features.translateUIStrings, set ui.sourceRoots to app/ (and mdx-components if needed).
4. Do not duplicate dictionary.ts strings that nextraDictionaryPath already translates — pick one approach per string.
After editing: run extract, translate-ui (or sync), verify en + one target locale in dev.Link conventions
Nextra serves locale-prefixed routes via Next.js i18n (/guide/getting-started, /pt-BR/guide/getting-started). In-page links should stay locale-neutral (/guide/getting-started) so Next.js can prefix the active locale automatically.
Enable the built-in normalizer so translate-docs fixes links in every translated file automatically:
"docsOutput": {
"style": "nextra",
"docsRoot": "content/en",
"rewriteNextraLinks": true
}rewriteNextraLinks defaults to enabled when style is "nextra".
| Author in English source | After normalizer |
|---|---|
[Guide](content/en/guide/getting-started.mdx) | [Guide](/guide/getting-started) |
[Guide](../guide/getting-started.mdx) | [Guide](/guide/getting-started) |
[Demo](https://github.com/org/repo) | unchanged (full URL) |
Authoring rules
- Cross-page doc links: use locale-neutral site routes (
/guide/…) in English MDX, orcontent/en/…/ relative.mdxpaths and let the normalizer rewrite them duringsync. - Repo files outside the content tree: use full URLs.
- Do not hand-edit links in
content/<locale>/— regenerate withsync/translate-docs.
Optional locale proxy
Nextra provides a locale-detection proxy for i18n sites. Export it from proxy.ts in your project root:
export { proxy } from 'nextra/locales'
export const config = {
matcher: [
'/((?!api|_next/static|_next/image|favicon.ico|icon.svg|apple-icon.png|manifest|_pagefind).*)',
],
}Site locale codes vs sourceLocale: Nextra and Next.js use short route codes (en, pt-BR, zh-Hans) in next.config, content/{locale}/, and the NEXT_LOCALE cookie. sourceLocale in ai-i18n-tools.config.json can be a BCP-47 tag such as en-GB for translation quality — that tag is not a site route. If the browser cookie or Accept-Language resolves to a tag outside i18n.locales (for example en-GB when only en is configured), Nextra’s stock proxy can redirect in a loop. The examples/nextra-docs demo wraps nextra/locales to reset invalid cookies and paths to the default site locale before delegating.
This does not work with output: 'export' static exports. See Nextra i18n docs.
See also Configuration — docsOutput and Output layouts.