Internationalization (i18n)
On this page
Internationalization (i18n)
Translate your docs into other languages, each getting its own URL prefix,
its own <html lang dir>, and an automatic language switcher - no plugin,
no separate build step.
Adding a locale
Translated content lives in docs/i18n/<code>/, mirroring your regular
docs/ tree page-for-page:
docs/
โโโ index.md
โโโ guides/
โ โโโ setup.md
โโโ i18n/
โโโ es/
โ โโโ index.md
โ โโโ guides/
โ โโโ setup.md
โโโ ar/
โโโ index.md
<code> becomes both the folder name and the built URL prefix
(docs/i18n/es/guides/setup.md โ /es/guides/setup/), so keep it short -
a bare language code (es, fr) or a language-region pair (pt-BR,
zh-Hans) both work, letters/digits/hyphens only. Your regular docs/
tree is always the default locale, built unprefixed at the site root
exactly as it is today - adding docs/i18n/ doesn't change anything about
it.
Give each locale a display label (and, for a right-to-left language, its
own direction) in bxsites.json:
{
"i18n": {
"defaultLocale": { "code": "en", "label": "English" },
"locales": [
{ "code": "es", "label": "Espaรฑol" },
{ "code": "ar", "label": "ุงูุนุฑุจูุฉ", "dir": "rtl" },
{ "code": "pt-BR", "label": "Portuguรชs (Brasil)", "flag": "๐ง๐ท" }
]
}
}
defaultLocale only needs setting if your default locale isn't English;
locales is the list of everything else. Every docs/i18n/<code>/
folder builds automatically once it exists - locales just supplies its
display label and text direction. A folder with no matching locales
entry still builds (using its bare code as its own label), so this is
metadata, not what turns the feature on or off.
flag is optional - the switcher already picks a sensible flag emoji for
~40 common language codes on its own (checking a region code like pt-BR
first, then falling back to the base language pt). Set flag yourself
only to override that guess, or for a code the built-in lookup doesn't
recognize (it falls back to a plain ๐ in that case).
What gets built
Each locale is a real, fully independent build - its own search-index.json,
its own assets/, everything a normal build produces - written under
site/<code>/ (site/es/, site/ar/). Nothing needs enabling per-locale:
once docs/i18n/es/ exists, bxSites build picks it up on its own.
Untranslated pages
A locale doesn't need every page translated before it's usable. A page
missing from docs/i18n/es/ still builds at its expected URL - showing
the default locale's own content, with a small notice at the top of the
page saying it hasn't been translated yet. Nothing 404s, nothing looks
half-built while a translation is in progress.
Every locale's nav is always the exact same shape as the default locale's
own - same pages, same order, same nesting (whatever docs/'s own folder
structure, or an explicit nav, already
produces) - just with each page's title/content swapped in from its own
translation where one exists. This is also what makes the language
switcher work: switching languages lands you on the same page, translated
or not, never that locale's homepage.
The language switcher
Once more than one locale exists, every theme renders a flag-icon language dropdown in the header automatically - nothing to opt into, same as the version switcher. It shows the current locale's flag as a trigger; opening it lists every locale with its own flag and label, the current one marked active. Pick a locale you're not currently building and it simply won't render at all.
Versioned and translated docs
See Versioning for docs/versions/<name>/ itself.
Versions and locales compose one level: put a docs/versions/<name>/i18n/<code>/
folder next to a version's own pages, mirroring that version's own
structure exactly the same way a top-level docs/i18n/<code>/ mirrors
docs/ itself:
docs/
versions/
2.0/
index.md
guides/
setup.md
i18n/
es/
index.md # translated
guides/
setup.md # untranslated pages still fall back, same as top-level i18n
This builds site/versions/2.0/es/. A version's own default-locale pages
(site/versions/2.0/) get a language switcher too, listing only the
locales that version itself has translations for - a version with no
i18n/ subfolder of its own renders exactly as it did before this
existed, no switcher shown. Switching version always drops back to that
version's own default locale (never assumes the target version has the
same translation); switching locale always stays on the current version.
What's out of scope (for now)
- Theme chrome stays in English. "Edit this page," "Last updated," the search placeholder, and similar UI strings aren't translated per locale yet - only your own page content is. A locale's actual reading experience is fully translated; the surrounding theme furniture isn't.
- RTL layout mirroring is baseline, not pixel-perfect.
dir="rtl"is set correctly, and the sidebar/header genuinely mirror, but a few decorative details (an admonition's accent-bar side, for instance) don't flip yet. - No automated translation. Every
docs/i18n/<code>/file is authored by hand, the same as any other markdown page - there's no machine-translation step.
Custom icons and includes
A custom: icon reference and a ::: include both resolve against your
project's own docs/assets//reusable-content, regardless of which locale
is being built - these are shared assets, not something a translator needs
to duplicate per locale.
SEO
Every locale's pages are included in sitemap.xml and llms.txt
alongside the default locale's own, the same way versioned
pages are.