import React, { createContext, useContext, useState, useCallback, useMemo } from 'react'; import en from './i18n/en.json'; import de from './i18n/de.json'; const translations = { en, de }; // Cookie helpers function getCookie(name) { const match = document.cookie.match(new RegExp('(^| )' + name + '=([^;]+)')); return match ? match[2] : null; } function setCookie(name, value, days = 365) { const expires = new Date(Date.now() + days * 864e5).toUTCString(); document.cookie = `${name}=${value}; expires=${expires}; path=/; SameSite=Lax`; } // Get initial language from cookie or browser preference function getInitialLanguage() { const cookieLang = getCookie('lang'); if (cookieLang && translations[cookieLang]) { return cookieLang; } // Check browser preference const browserLang = navigator.language?.slice(0, 2); if (browserLang === 'de') return 'de'; return 'en'; } const I18nContext = createContext(null); export function I18nProvider({ children }) { const [language, setLanguageState] = useState(getInitialLanguage); const setLanguage = useCallback((lang) => { if (translations[lang]) { setLanguageState(lang); setCookie('lang', lang); } }, []); // Translation function with nested key support (e.g., 'app.title') const t = useCallback((key, params = {}) => { const keys = key.split('.'); let value = translations[language]; for (const k of keys) { if (value && typeof value === 'object') { value = value[k]; } else { return key; // fallback to key if not found } } if (typeof value !== 'string') return key; // Replace {param} placeholders return value.replace(/\{(\w+)\}/g, (_, param) => params[param] !== undefined ? params[param] : `{${param}}` ); }, [language]); const value = useMemo(() => ({ language, setLanguage, t }), [language, setLanguage, t]); return ( {children} ); } export function useI18n() { const context = useContext(I18nContext); if (!context) { throw new Error('useI18n must be used within an I18nProvider'); } return context; }