"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; type MessageBundle = Record; 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(null); export function I18nProvider({ initialLocale = "de", children, }: { initialLocale?: Locale; children: ReactNode; }) { const [locale, setLocale] = useState(initialLocale); const value = useMemo( () => ({ locale, setLocale, t: (key: string, fallback?: string) => registry[locale][key] ?? fallback ?? key, }), [locale], ); return {children}; } export function useI18n(): I18nContextValue { const ctx = useContext(I18nContext); if (!ctx) { throw new Error("useI18n muss innerhalb von aufgerufen werden"); } return ctx; }