--- title: 'Navigation und Sprachen' description: 'Navigations-Collection, Sprachfelder, translationKey, Pfad-Regeln.' --- import { Aside, Tabs, TabItem, Steps } from '@astrojs/starlight/components'; Navigation und Sprachen hängen zusammen: Die `navigation`-Sammlung hält je Sprache einen Baum, `content` hält je Sprache einen Eintrag. Suche-Begriffe: Navigation anlegen, Sprache umschalten, translationKey. ## Navigations-Dokumente `api/collections/navigation.yml` kennt genau vier Singleton-Bäume: `type` (`header`, `footer`) mal `language` (`de`, `en`) — abgesichert per Unique-Index `type_language`. ```yaml title="navigation.yml — Felder (gekürzt)" # navigation.yml — Felder (gekürzt) fields: - name: language # de | en, Pflicht - name: type # header | footer, Pflicht - name: elements # object[]: {name, page?, external?, externalUrl?, hash?, elements?} ``` | Element-Art | Felder | Beispiel | |---|---|---| | Intern | `name` + `page` (= `content`-ID) | `{name: "Start", page: ""}` | | Extern | `name` + `external: true` + `externalUrl` | `{name: "Blog", external: true, externalUrl: "https://…"}` | | Anker | zusätzlich `hash` ohne `#`, Muster `^[a-zA-Z0-9_-]+$` | `{name: "Kontakt", page: "", hash: "kontakt"}` | | Unterpunkt | geschachtelt in `elements.elements` | Header max. 2 Ebenen, Footer max. 1 | 1. Lege je Sprache einen Header und einen Footer an, je mit `active:true` (Pflicht trotz fehlendem Schema-Feld — siehe Kasten unten). 2. Löse Seiten mit Lookup auf: ```text title="Seiten per Lookup auflösen" GET …/navigation?lookup=elements.page:content ``` 3. Baue den Link aus dem Lookup: `href` = lokalisierter `_lookup.page.path` plus `hash`. Den `/de`-Präfix nie in die DB schreiben. ## Sprachmodell: lang + translationKey `content.yml` nutzt Entry-i18n: eine Sprache je Eintrag, verbunden über den Schlüssel. ```yaml title="content.yml — i18n-Block (gekürzt)" # content.yml — i18n-Block (gekürzt) meta: i18n: entry: languageField: lang groupField: translationKey ``` | Regel | Konkret | |---|---| | `lang` | Sprachkürzel, z. B. `de`, `en` | | `translationKey` | **Pflicht**, z. B. `home`, `ueber-uns` — pro Sprache eindeutig (Unique-Index `translationKey` + `lang`) | | Zweiter Datensatz ohne Schlüssel | Kollidiert auf `null` — kein „geht schon gut" | | `path` | Ohne Sprachpräfix gespeichert (`/ueber-uns`), URL mit Präfix (`/de/ueber-uns`) | | `alternativePaths[].path` | Alte Pfade, gleiches Muster wie `path` | Die Feld-i18n (Tabs je Feld, Test-Plan `34-i18n-fields`) und Entry-i18n (eine Sprache je Eintrag, Test-Plan `35-entry-i18n`) pflegst du im tibi-admin; das Datenmodell aus Projekt-Sicht ist immer `lang` + `translationKey`. ## Pfad-Regeln ```text title="Pfad-Muster für path und alternativePaths" ^/[a-z0-9-/]*$ → gilt für path und alternativePaths[].path ``` - Nur Kleinbuchstaben, Ziffern, `-` und `/`; keine Großbuchstaben, keine Umlaute (`/ueber-uns` statt `/Über-uns`). - Unique-Index `path` + `lang`: gleicher Pfad in `de` und `en` ist okay, zweimal in `de` nicht. - SSR streift den Präfix vor der Suche ab und liefert die kanonische URL zurück (`hooks/config.js` → `ssrValidatePath`). ## active:true auch hier (Checkliste) | Sammlung | Ohne `active:true` öffentlich … | |---|---| | `content` | … filtern `filter_public` + SSR-Prüfung den Eintrag weg | | `medialib` | … bleibt `_lookup` leer, Bilder verschwinden (siehe [Dateien und Bilder](/projekt/dateien-bilder/)) | | `navigation` | … ist Header/Footer leer | ## Prüfung: anonyme SSR-Nav-Zahl ```text title="Navigation je Sprache prüfen (anonym)" GET …/navigation?filter={"language":"de"} GET …/navigation?filter={"language":"en"} # anonym (ohne Token) müssen beide je einen Header und einen Footer liefern ``` ```text title="SSR-Cache prüfen" GET …/ssr?url=/de/ # im HTML: window.__SSR_CACHE__ enthält das Ergebnis je Request (cacheKey mit data + count) — Navigation darin je Sprache mit count > 0 prüfen; die Abhängigkeiten selbst stehen separat im ssr-Dokument (Feld dependencies) ``` | Fehlerbild | Ursache | Lösung | |---|---|---| | Öffentliche Nav leer, mit Token voll | `active:true` fehlt | Auf allen vier Bäumen setzen | | `409` beim zweiten Spracheintrag | `translationKey` fehlt oder doppelt | Eindeutigen Schlüssel je Sprache vergeben | | `400` beim Speichern | Pfad mit Großbuchstaben/Umlaut | Kleinschreiben, Muster einhalten | | Falsche Sprache im SSR | `lang`-Feld passt nicht zum Präfix | `lang` und URL-Präfix angleichen | Weiter: [Lesen und suchen](/projekt/abfragen-lesen/), [Dateien und Bilder](/projekt/dateien-bilder/), [Rechte und Tokens](/projekt/rechte-tokens/). Verifiziert am e3f92ebf 2026-10-14 (tibi-svelte-starter), am 2b091d89 2026-10-13 (tibi-admin-nova, Test-Pläne 34-i18n-fields, 35-entry-i18n)