---
title: Internationalisierung (i18n)
order: 8
icon: phosphor-duotone:translate
tags: [anleitungen, i18n]
---

# Internationalisierung (i18n)

Übersetze deine Docs in andere Sprachen, jede mit ihrem eigenen
URL-Präfix, ihrem eigenen `<html lang dir>` und einem automatischen
Sprachumschalter - kein Plugin, kein separater Build-Schritt nötig.

## Eine Locale hinzufügen

Übersetzte Inhalte liegen unter `docs/i18n/<code>/`, wobei sie deinen
regulären `docs/`-Baum Seite für Seite spiegeln:

```text title="Project structure"
docs/
├── index.md
├── guides/
│   └── setup.md
└── i18n/
    ├── es/
    │   ├── index.md
    │   └── guides/
    │       └── setup.md
    └── ar/
        └── index.md
```

`<code>` wird sowohl zum Ordnernamen als auch zum gebauten URL-Präfix
(`docs/i18n/es/guides/setup.md` → `/es/guides/setup/`), halte ihn daher
kurz - ein reiner Sprachcode (`es`, `fr`) oder ein
Sprach-Region-Paar (`pt-BR`, `zh-Hans`) funktionieren beide, nur
Buchstaben/Ziffern/Bindestriche. Dein regulärer `docs/`-Baum ist immer die
**Standard-Locale**, unpräfixiert an der Wurzel der Website gebaut, genau
wie heute - das Hinzufügen von `docs/i18n/` ändert daran nichts.

Gib jeder Locale ein Anzeige-Label (und, für eine
Rechts-nach-links-Sprache, ihre eigene Schreibrichtung) in `bxsites.yaml`:

=== "YAML"
    ```yaml title="bxsites.yaml" linenums="1"
    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: 🇧🇷 }
    ```

=== "JSON"
    ```json title="bxsites.json" linenums="1"
    {
    	"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` muss nur gesetzt werden, wenn deine Standard-Locale nicht
Englisch ist; `locales` ist die Liste von allem anderen. Jeder
`docs/i18n/<code>/`-Ordner wird automatisch gebaut, sobald er existiert -
`locales` liefert nur sein Anzeige-Label und seine Textrichtung. Ein
Ordner ohne passenden `locales`-Eintrag wird trotzdem gebaut (mit seinem
reinen Code als eigenem Label), das ist also Metadaten, nicht das, was die
Funktion ein- oder ausschaltet.

`flag` ist optional - der Umschalter wählt für rund 40 gängige
Sprachcodes bereits von selbst ein sinnvolles Flaggen-Emoji (er prüft
zuerst einen Regionscode wie `pt-BR`, dann fällt er auf die Basissprache
`pt` zurück). Setze `flag` nur selbst, um diese Vermutung zu
überschreiben, oder für einen Code, den die eingebaute Zuordnung nicht
erkennt (in diesem Fall fällt sie auf ein schlichtes 🌐 zurück).

## Was gebaut wird

Jede Locale ist ein echter, vollständig unabhängiger Build - eigene
`search-index.json`, eigene `assets/`, alles, was ein normaler Build
erzeugt - geschrieben unter `site/<code>/` (`site/es/`, `site/ar/`).
Nichts muss pro Locale aktiviert werden: sobald `docs/i18n/es/` existiert,
übernimmt `bxSites build` das von selbst.

## Nicht übersetzte Seiten

Eine Locale muss nicht jede Seite übersetzt haben, um nutzbar zu sein.
Eine Seite, die in `docs/i18n/es/` fehlt, wird trotzdem unter ihrer
erwarteten URL gebaut - sie zeigt den Inhalt der Standard-Locale, mit
einem kleinen Hinweis oben auf der Seite, dass sie noch nicht übersetzt
wurde. Nichts liefert einen 404, nichts sieht halbfertig aus, während eine
Übersetzung noch in Arbeit ist.

Die Navigation jeder Locale hat immer genau dieselbe Form wie die der
Standard-Locale - dieselben Seiten, dieselbe Reihenfolge, dieselbe
Verschachtelung (was auch immer die eigene Ordnerstruktur von `docs/`,
oder eine explizite [`nav`](../configuration.md#nav), bereits erzeugt) -
nur mit Titel/Inhalt jeder Seite, aus ihrer eigenen Übersetzung
eingesetzt, wo eine existiert. Das ist auch das, was den Sprachumschalter
funktionieren lässt: ein Sprachwechsel bringt dich auf *dieselbe Seite*,
übersetzt oder nicht, niemals auf die Startseite dieser Locale.

## Der Sprachumschalter

Sobald mehr als eine Locale existiert, rendert jedes Theme automatisch ein
Sprach-Dropdown in der Kopfzeile - nichts, wozu man sich anmelden müsste,
genau wie beim [Versionsumschalter](../configuration.md#versionierung). Es
zeigt die Flagge der aktuellen Locale als Auslöser; öffnest du es, listet
es jede Locale mit ihrer eigenen Flagge und ihrem Label, die aktuelle als
aktiv markiert. Wähle eine Locale, die du gerade nicht baust, und sie wird
einfach gar nicht gerendert.

## Versionierte und übersetzte Docs

Siehe [Versionierung](versioning.md) für `docs/versions/<name>/` selbst.
Versionen und Locales lassen sich auf einer Ebene kombinieren: lege einen
`docs/versions/<name>/i18n/<code>/`-Ordner neben die eigenen Seiten einer
Version, der die eigene Struktur dieser Version genau so spiegelt, wie
ein oberstes `docs/i18n/<code>/` `docs/` selbst spiegelt:

```text title="docs/versions/2.0/-Layout"
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
```

Das baut `site/versions/2.0/es/`. Die eigenen Seiten der Standard-Locale
einer Version (`site/versions/2.0/`) erhalten ebenfalls einen
Sprachumschalter, der nur die Locales listet, für die *diese Version
selbst* Übersetzungen hat - eine Version ohne eigenen
`i18n/`-Unterordner wird genau so gerendert wie vorher, kein Umschalter
gezeigt. Ein Versionswechsel fällt immer auf die eigene Standard-Locale
dieser Version zurück (nimmt nie an, dass die Zielversion dieselbe
Übersetzung hat); ein Locale-Wechsel bleibt immer auf der aktuellen
Version.

## Theme-Chrome (UI-Texte)

Der gesamte umgebende UI-Text jedes eingebauten Themes - der
Suchplatzhalter, "Auf dieser Seite", "Diese Seite bearbeiten", "Zuletzt
aktualisiert", die 404-Seite, der Titel der Tags-Seite und der Hinweis
"noch nicht übersetzt" selbst - wird jetzt auch pro Locale übersetzt,
nicht nur dein eigener Seiteninhalt.

Vier Locales bringen von Haus aus eine eingebaute Übersetzung mit: `de`,
`es`, `it` und `ja`. Baust du einen dieser Locale-Codes, erhältst du
automatisch vollständig übersetztes Theme-Chrome - nichts zu
konfigurieren. Jeder andere Locale-Code (oder eine Seite, die du lieber
anders formulieren möchtest) fällt auf Englisch zurück - überschreibe nur
die Schlüssel, die dich interessieren, mit deinen eigenen `strings` im
jeweiligen Locale-Eintrag in `bxsites.yaml`:

=== "YAML"
    ```yaml title="bxsites.yaml" linenums="1"
    i18n:
      defaultLocale: { code: en, label: English }
      locales:
        - code: fr
          label: Français
          strings:
            searchPlaceholder: Rechercher dans la documentation...
            onThisPage: Sur cette page
        - code: es
          label: Español
          strings: { toggleDarkMode: Cambiar a modo nocturno }
    ```

=== "JSON"
    ```json title="bxsites.json" linenums="1"
    {
    	"i18n": {
    		"defaultLocale": { "code": "en", "label": "English" },
    		"locales": [
    			{
    				"code": "fr",
    				"label": "Français",
    				"strings": {
    					"searchPlaceholder": "Rechercher dans la documentation...",
    					"onThisPage": "Sur cette page"
    				}
    			},
    			{
    				"code": "es",
    				"label": "Español",
    				"strings": { "toggleDarkMode": "Cambiar a modo nocturno" }
    			}
    		]
    	}
    }
    ```

Eine Locale mit eingebauter Übersetzung (wie `es` oben) kann trotzdem
einzelne Schlüssel genauso überschreiben - deine eigenen `strings`
gewinnen immer sowohl gegen die eingebaute Übersetzung als auch gegen den
englischen Standardwert. Auch `defaultLocale` selbst kann eigene
`strings` tragen, für ein Projekt, dessen Standard-Locale nicht Englisch
ist und das eigene Chrome-Formulierungen möchte, statt auf den englischen
Standardwert zurückzufallen.

Die vollständige Liste überschreibbarer Schlüssel: `searchPlaceholder`,
`searchAriaLabel`, `searchNoResults`, `toggleDarkMode`,
`toggleNavigation`, `toggleSection` (Platzhalter `{title}`),
`repository`, `version`, `language` (Platzhalter `{label}`), `onThisPage`,
`editThisPage`, `downloadMarkdown`, `lastUpdated`, `notTranslatedNotice`
(Platzhalter `{locale}`), `notFoundTitle`, `notFoundBody`, `tagsTitle` und
`pageNavigation`.

## Was (vorerst) außen vor bleibt

- **Blog-Chrome bleibt auf Englisch.** Die eigenen UI-Texte des
  Blog-Subsystems ("Categories", "Archive", "Read more", "N min read",
  ...) werden vom `strings`-Override oben noch nicht erfasst - nur der
  Rest des Theme-Chromes ist es.
- **Der Datumswert von `lastUpdated` ist nicht locale-abhängig.** Sein
  Label wird übersetzt; der Datums-/Uhrzeitwert selbst wird unabhängig
  von der Locale weiterhin gleich formatiert.
- **Die RTL-Layout-Spiegelung ist grundlegend, nicht pixelgenau.**
  `dir="rtl"` wird korrekt gesetzt, und Sidebar/Kopfzeile spiegeln sich
  tatsächlich, aber ein paar dekorative Details (etwa die Seite des
  Akzentbalkens einer Admonition) drehen sich noch nicht mit.
- **Keine automatische Übersetzung.** Jede Datei unter
  `docs/i18n/<code>/` wird von Hand verfasst, genau wie jede andere
  Markdown-Seite - es gibt keinen maschinellen Übersetzungsschritt.

## Eigene Icons und Includes

Ein `custom:`-Icon-Verweis und ein `::: include` lösen beide gegen die
eigenen `docs/assets/`/wiederverwendbaren Inhalte deines Projekts auf,
unabhängig davon, welche Locale gerade gebaut wird - das sind gemeinsam
genutzte Assets, nichts, was ein Übersetzer pro Locale duplizieren müsste.

## SEO

Die Seiten jeder Locale sind neben denen der Standard-Locale in
`sitemap.xml` und `llms.txt` enthalten, genauso wie es
[versionierte](../configuration.md#versionierung) Seiten sind.
