Zum Inhalt springen

Locale auswählen

Die Locale wird bestimmt, indem eine Liste von Quellen durchlaufen wird; der erste Treffer gewinnt. Quellen, die auf der aktuellen Seite nicht zutreffen, werden übersprungen, sodass dieselbe Liste auf dem Server und im Browser funktioniert.

Die Standardreihenfolge #

strategy: ['url', 'cookie', 'lang', 'header', 'navigator', 'default'];

url bewirkt nur etwas, wenn Routing aktiv ist. header (Accept-Language) gibt es nur auf dem Server. lang (<html lang>) und navigator gibt es nur im Browser, wobei lang ignoriert wird, wenn es defaultLocale entspricht — eine prerenderte Seite nennt immer die Standard-Locale, dieser Wert trägt also keine Entscheidung.

Die Reihenfolge ändern #

Kürze oder ordne die Liste nach Bedarf um — rate etwa nie anhand des Browsers, sondern nimm nur das Cookie oder den Standard:

createDialect({
	strategy: ['cookie', 'default'],
	// …
});

Caching #

Wenn header mitspielt, tragen die Antworten Vary: Accept-Language, und wenn cookie mitspielt, Vary: Cookie — damit ein geteilter Cache nie die Sprache eines Besuchers an einen anderen ausliefert.

Request-Isolation #

Auf dem Server wird jeder Request in seinem eigenen AsyncLocalStorage-Kontext aufgelöst, sodass gleichzeitige Requests mit unterschiedlichen Sprachen einander nicht beeinflussen können — t() und getLocale() sehen während eines Renders immer die Locale genau dieses Requests. Laufzeiten ohne async_hooks greifen stattdessen auf einen einzigen gemeinsamen Slot zurück.

Zur Laufzeit bestimmte Locales #

createDialect läuft beim Modulimport, bevor die Einstellungstabelle eines Betreibers oder eine beim Start gelesene Umgebungsvariable erreichbar ist. configure ersetzt die Menge der Locales und die Standard-Locale nachträglich, und jeder nachgelagerte Leser — Aushandlung, setLocale, reroute, localizeHref — übernimmt die neuen Werte.

Rufe es einmal auf dem Server beim Start auf, vor dem ersten Request, und noch einmal auf dem Client bei der App-Initialisierung mit dem, was der Server über Page Data geliefert hat — configure ist idempotent, ein erneuter Aufruf bei jeder Navigation kostet also nichts.

src/hooks.server.ts — top-level await runs before the first request
import { configure, handle as dialect } from '#lib/i18n.js';

const settings = await loadLocaleSettings();
configure({ defaultLocale: settings.defaultLocale, locales: settings.locales });

export const handle = dialect;
src/routes/+layout.ts
import { configure } from '#lib/i18n.js';
import type { LayoutLoad } from './$types';

export const load: LayoutLoad = async ({ data }) => {
	configure(data.i18n);
	return data;
};

locales ersetzt die Menge vollständig, statt sie zu ergänzen, und defaultLocale muss zur resultierenden Menge gehören; ein Aufruf, der eine der beiden Regeln verletzt, warnt und ändert nichts. reroute ist der einzige Teil, der nicht darauf warten kann — SvelteKit ruft ihn synchron vor jeder Load-Funktion auf —, daher ist ein Präfix, das configure entfernt, überhaupt keine Route mehr.