import React, { createContext, useContext, useState, useEffect, ReactNode, useCallback } from 'react'; import { Language, TranslationKeys, loadLanguage, fetchAvailableLanguageCodes, I18nCodeInfo } from '../../locales'; import { getUserDataCache } from '../../utils/userCache'; export type { Language }; type TranslateParams = Record; interface LanguageContextType { currentLanguage: Language; setLanguage: (language: Language) => void; t: (key: string, params?: TranslateParams) => string; isLoading: boolean; reloadLanguage: () => Promise; availableLanguages: I18nCodeInfo[]; refreshAvailableLanguages: () => Promise; } const LanguageContext = createContext(undefined); interface LanguageProviderProps { children: ReactNode; } export const LanguageProvider: React.FC = ({ children }) => { const [currentLanguage, setCurrentLanguage] = useState('de'); const [translations, setTranslations] = useState({}); const [isLoading, setIsLoading] = useState(true); const [availableLanguages, setAvailableLanguages] = useState([]); const loadAndSetLanguage = async (language: Language) => { setIsLoading(true); try { const targetKeys = await loadLanguage(language); setTranslations(targetKeys); setCurrentLanguage(language); } catch (error) { console.error('Failed to load language:', error); } finally { setIsLoading(false); } }; useEffect(() => { const initializeLanguage = async () => { let initialLanguage: Language = 'de'; const userData = getUserDataCache(); if (userData?.language && String(userData.language).trim()) { initialLanguage = String(userData.language).trim() as Language; await loadAndSetLanguage(initialLanguage); return; } const browserLang = navigator.language.split('-')[0] as Language; try { const codes = await fetchAvailableLanguageCodes(); const codeSet = new Set(codes.map((c) => c.code)); if (codeSet.has(browserLang) && browserLang !== 'xx') { initialLanguage = browserLang; } } catch { // keep default } await loadAndSetLanguage(initialLanguage); }; initializeLanguage(); const handleUserUpdate = async () => { const userData = getUserDataCache(); if (userData?.language && String(userData.language).trim()) { const userLanguage = String(userData.language).trim() as Language; if (userLanguage !== currentLanguage) { loadAndSetLanguage(userLanguage); } } try { const list = await fetchAvailableLanguageCodes(); setAvailableLanguages(list.filter((l) => l.code !== 'xx')); } catch { // silent } }; window.addEventListener('userInfoUpdated', handleUserUpdate); return () => { window.removeEventListener('userInfoUpdated', handleUserUpdate); }; }, []); const setLanguage = async (language: Language) => { await loadAndSetLanguage(language); }; const reloadLanguage = async () => { await loadAndSetLanguage(currentLanguage); }; const refreshAvailableLanguages = useCallback(async () => { try { const list = await fetchAvailableLanguageCodes(); setAvailableLanguages(list.filter((l) => l.code !== 'xx')); } catch (e) { console.error('Failed to load language codes:', e); } }, []); useEffect(() => { refreshAvailableLanguages(); }, [refreshAvailableLanguages]); const _applyParams = (template: string, params?: TranslateParams): string => { if (!params) return template; let out = template; for (const [paramKey, rawVal] of Object.entries(params)) { if (rawVal === undefined || rawVal === null) continue; out = out.split(`{${paramKey}}`).join(String(rawVal)); } return out; }; const t = (key: string, params?: TranslateParams): string => { const resolved = translations[key] ?? `[${key}]`; return _applyParams(resolved, params); }; const contextValue: LanguageContextType = { currentLanguage, setLanguage, t, isLoading, reloadLanguage, availableLanguages, refreshAvailableLanguages, }; return ( {children} ); }; export const useLanguage = (): LanguageContextType => { const context = useContext(LanguageContext); if (!context) { throw new Error('useLanguage must be used within a LanguageProvider'); } return context; };