/** * Task H4 – i18n-Grundlage: zentrale Strings-Maps de/en. * * t(key) liefert den Text der aktuellen Sprache; unbekannte Keys * fallen auf den Key zurück (im Code sichtbar, lint-bar über Tests). * Sprache persistiert in localStorage ('webcad.language', Default de). * Komponenten nutzen useT() für Live-Updates bei Sprachwechsel. * * Vollständige mechanische Ersetzung ALLER Komponenten-Texte ist * teilautomatisierbares Follow-up; hier liegt die Infrastruktur und * die Kern-UI-Texte (LayoutBar, Modus). */ import { useEffect, useState } from 'react'; export const LANGUAGES = ['de', 'en'] as const; export type Language = (typeof LANGUAGES)[number]; type StringEntry = string | ((arg?: string) => string); const STORAGE_KEY = 'webcad.language'; /** Zentrale Text-Maps (Key-Pfad → Text oder parametrisierte Funktion). */ const DE: Record = { 'layouts.title': 'Layouts', 'layouts.add': 'Layout hinzufügen', 'layouts.empty': 'Keine Layouts', 'layouts.delete': (n?: string) => 'Layout ' + (n ?? '') + ' löschen', 'layouts.print': 'Layout drucken (A4 landscape)', 'layouts.titleblock': (n?: string) => 'Titelblock für ' + (n ?? '') + ' einfügen/aktualisieren', 'layouts.zoomto': (n?: string) => 'Zoom zu ' + (n ?? ''), 'mode.toSimple': 'Zum einfachen Modus wechseln', 'mode.toPro': 'Zum Profi-Modus wechseln', }; const EN: Record = { 'layouts.title': 'Layouts', 'layouts.add': 'Add layout', 'layouts.empty': 'No layouts', 'layouts.delete': (n?: string) => 'Delete layout ' + (n ?? ''), 'layouts.print': 'Print layout (A4 landscape)', 'layouts.titleblock': (n?: string) => 'Insert/update title block for ' + (n ?? ''), 'layouts.zoomto': (n?: string) => 'Zoom to ' + (n ?? ''), 'mode.toSimple': 'Switch to simple mode', 'mode.toPro': 'Switch to pro mode', }; function mapFor(lang: Language): Record { return lang === 'en' ? EN : DE; } function readStored(): Language { try { const raw = localStorage.getItem(STORAGE_KEY); if (raw === 'de' || raw === 'en') return raw; } catch { // localStorage nicht verfügbar } return 'de'; } let current: Language = readStored(); const listeners = new Set<(lang: Language) => void>(); /** Aktuelle Sprache. */ export function getLanguage(): Language { return current; } /** Sprache wechseln und persistieren. */ export function setLanguage(lang: Language): void { current = lang; try { localStorage.setItem(STORAGE_KEY, lang); } catch { // Persistenz optional } for (const cb of listeners) cb(lang); } /** Subscribe auf Sprachwechsel; gibt Unsubscribe zurück. */ export function subscribeLanguage(cb: (lang: Language) => void): () => void { listeners.add(cb); return () => { listeners.delete(cb); }; } /** * Übersetzung: t('key') bzw. t('key', arg) für parametrisierte Texte. * Unbekannter Key → Key als Fallback (offensichtlich sichtbar). */ export function t(key: string, arg?: string): string { const entry = mapFor(current)[key]; if (typeof entry === 'function') return entry(arg); if (typeof entry === 'string') return entry; return key; } /** React-Hook: erzwingt Re-Render bei Sprachwechsel. */ export function useT(): typeof t { const [, force] = useState(0); useEffect(() => { const off = subscribeLanguage(() => force((n) => n + 1)); return off; }, []); return t; }