## Kontext

Die Ebene übersetzte ihre Oberfläche in sieben Locales und lieferte allen
denselben Satz Seiten aus: `useDuxtPath()` entfernte das Locale-Segment vor
jedem Content-Lookup, `/de-DE/guides/deploying` und `/guides/deploying` lösten
also auf dieselbe Datei auf. Content v3 kennt keine Locale — eine Collection ist
ein Baum —, übersetzte Seiten brauchten also eine Entscheidung und keine
Einstellung.

Was die vergleichbaren Generatoren tun, wurde nachgelesen statt angenommen.
Starlight, VitePress, Docusaurus und MkDocs legen Übersetzungen allesamt in
einen Ordner je Sprache; nur Starlight hat einen dokumentierten Rückfall für
eine Seite, die einer Sprache fehlt. Ab einer gewissen Größe verlässt die
Übersetzung das Werkzeug ganz: React betreibt `de.react.dev` als eigenes
Repository, Vue eine ganze `vuejs-translations`-Organisation, weil Übersetzer
nach eigenem Zeitplan und eigenem Review arbeiten. OpenCode baute einen Agenten,
der die Doku in CI übersetzte, ließ ihn laufen und schaltete ihn ab; siebzehn
Sprachen stehen seither still.

Die Kosten wurden gemessen, bevor die Form gewählt wurde: Builds über 1 … 200
Collections skalieren linear mit rund 2,2 s und 0,63 MB Datenbank je Collection,
ohne Knick. Die Matrix hat keine Decke, die dem Entwurf die Hand führen würde.

## Entscheidung

Eine Quelle bekommt eine `locales`-Liste, und ein Ref ebenso, aufgelöst wie
`status` es bereits wird (`ref.locales ?? source.locales`). Eine Zeichenkette
ist ein Ordner innerhalb des `path` der Quelle; ein Objekt verschiebt diese
Sprache in einen eigenen Ordner, ein eigenes Repository oder einen eigenen Ref.

**Die Standard-Locale ist der Baum in `path` selbst, ohne Ordner**, das
Hinzufügen des Schlüssels verschiebt also keine URL, die eine Seite bereits
ausliefert.

**Die Locale gehört nicht in den Content-Pfad.** Sie gehört i18ns Routing, das
sie dem Pfad ohnehin voranstellt. Original und Übersetzung liegen deshalb unter
identischen Content-Pfaden in getrennten Collections.

Eine Seite, die einer Sprache fehlt, fällt entlang einer Kette zurück — die
Locale, ihre Basissprache, eine Geschwisterregion, `fallbackLocale` aus vue-i18n,
das unübersetzte Original — und dem Leser wird in einem Banner gesagt, welche
Sprache er zu sehen bekommt.

## Konsequenzen

Jeder Pfadvergleich im Theme bleibt unberührt: Navigation, Weiterleitungen, der
Breadcrumb, die Bewertung der nächsten Seite im 404 und der Sprachumschalter
arbeiten allesamt weiter auf einem Pfad, der nie eine Locale trug. Der Rückfall
ist eine weitere Abfrage auf denselben Pfad statt einer Weiterleitung oder eines
zweiten Auflösungsschemas.

`useDuxtNavigation` und die Suche folgen `useDuxtCollection`, beide wurden also
locale-bewusst, ohne geändert zu werden.

Eine Seite, die nichts setzt, bekommt genau das, was sie hatte: eine Collection
namens `docs`, einen Eintrag im Manifest, eine Abfrage je Seite.

Zwei Konfigurationen können nun so auseinandergehen, dass eine leere Seite dabei
herauskommt statt eines Fehlers — `content.config.ts` löst die Standard-Locale
ohne Zugriff auf die Nuxt-Konfiguration auf. Das duxt-Modul prüft deshalb
`sourceOptions.defaultLocale` gegen `i18n.defaultLocale` und lässt den Build
scheitern, wenn sie abweichen, statt das eine in das andere zu injizieren und
Content die andere Antwort ausrechnen zu lassen.

Übersetzungen vervielfachen Collections, und der Build zahlt für jede linear.
Die Zahl gehört in die Dokumentation, denn über die Matrix entscheidet ein
Konsument.

Partials folgen den Seiten. `_partials/` ist eine Collection JE SPRACHE, benannt
wie die Seiten-Collections — `duxt_partials` für das Original, `duxt_partials_de`
daneben —, und `:partial{name}` durchläuft dieselbe Fallback-Kette wie die Seite.
Es muss dieselbe Kette sein: Dass eine Seite und die Blöcke, die sie einbindet,
auf unterschiedliche Sprachen zurückfallen, ist genau der Weg zu einer halb
übersetzten Seite, auf der nichts davon erzählt.

Die erste Fassung dieser Entscheidung teilte EINE unübersetzte Partials-Collection
über alle Sprachen hinweg. Das war vertretbar, solange nichts ein Partial
übersetzte, und hörte in dem Moment auf vertretbar zu sein, in dem ein
Übersetzungsdurchlauf `docs/de/_partials/` erzeugte — Dateien, die keine
Collection las, und eine deutsche Seite, die einen englischen Installationshinweis
rendert, ohne dass irgendetwas darauf hinweist.

## Erwogene Alternativen

**Ein Locale-Segment im Content-Präfix.** Symmetrisch zu `repo` und `version`,
und es hätte jeden Pfadvergleich im Theme gezwungen, Locales zu lernen — für
eine URL, der i18n ohnehin ein Präfix voranstellt und die dann zweimal
geschrieben stünde.

**Ein Dateisuffix — `installation.de-DE.md` neben dem Original.** Keine
Vervielfachung von Collections, und es scheitert an dem Fall, den die großen
Projekte tatsächlich haben: Es zwingt die Übersetzung in dasselbe Repository und
denselben Ref wie das Original.

**Ein 404 für eine fehlende Übersetzung.** Was VitePress durch Auslassung tut.
Es bestraft den Leser für eine Lücke, die der Schreibende hinterlassen hat, und
es verbirgt vor allen anderen, dass die Übersetzung unvollständig ist.