76 lines
2.3 KiB
TypeScript
76 lines
2.3 KiB
TypeScript
"use client";
|
|
|
|
// i18n-Rahmen (mind. Deutsch/Englisch) — SHL-01 Akzeptanzkriterium 5.
|
|
// Liefert nur den Umschaltmechanismus + Basis-Komponenten-Texte.
|
|
// Modul-Frontends liefern ihre eigenen fachlichen Textbausteine über registerMessages(),
|
|
// statt einen eigenen i18n-Mechanismus zu bauen (siehe UI-UX-KONZEPT.md Abschnitt 4).
|
|
|
|
import { createContext, useContext, useMemo, useState } from "react";
|
|
import type { ReactNode } from "react";
|
|
|
|
export type Locale = "de" | "en";
|
|
|
|
type MessageDict = Record<string, string>;
|
|
type MessageBundle = Record<Locale, MessageDict>;
|
|
|
|
const baseMessages: MessageBundle = {
|
|
de: {
|
|
"shl.dialog.close": "Schließen",
|
|
"shl.toast.dismiss": "Meldung schließen",
|
|
"shl.table.noRows": "Keine Einträge vorhanden",
|
|
"shl.theme.toggleToLight": "Helles Erscheinungsbild",
|
|
"shl.theme.toggleToDark": "Dunkles Erscheinungsbild",
|
|
},
|
|
en: {
|
|
"shl.dialog.close": "Close",
|
|
"shl.toast.dismiss": "Dismiss message",
|
|
"shl.table.noRows": "No entries",
|
|
"shl.theme.toggleToLight": "Switch to light theme",
|
|
"shl.theme.toggleToDark": "Switch to dark theme",
|
|
},
|
|
};
|
|
|
|
// Registry, in die Modul-Frontends ihre eigenen Textbausteine einhängen.
|
|
const registry: MessageBundle = { de: { ...baseMessages.de }, en: { ...baseMessages.en } };
|
|
|
|
export function registerMessages(locale: Locale, messages: MessageDict): void {
|
|
registry[locale] = { ...registry[locale], ...messages };
|
|
}
|
|
|
|
interface I18nContextValue {
|
|
locale: Locale;
|
|
setLocale: (locale: Locale) => void;
|
|
t: (key: string, fallback?: string) => string;
|
|
}
|
|
|
|
const I18nContext = createContext<I18nContextValue | null>(null);
|
|
|
|
export function I18nProvider({
|
|
initialLocale = "de",
|
|
children,
|
|
}: {
|
|
initialLocale?: Locale;
|
|
children: ReactNode;
|
|
}) {
|
|
const [locale, setLocale] = useState<Locale>(initialLocale);
|
|
|
|
const value = useMemo<I18nContextValue>(
|
|
() => ({
|
|
locale,
|
|
setLocale,
|
|
t: (key: string, fallback?: string) => registry[locale][key] ?? fallback ?? key,
|
|
}),
|
|
[locale],
|
|
);
|
|
|
|
return <I18nContext.Provider value={value}>{children}</I18nContext.Provider>;
|
|
}
|
|
|
|
export function useI18n(): I18nContextValue {
|
|
const ctx = useContext(I18nContext);
|
|
if (!ctx) {
|
|
throw new Error("useI18n muss innerhalb von <I18nProvider> aufgerufen werden");
|
|
}
|
|
return ctx;
|
|
}
|