import { useCallback, useEffect, useState, type ReactNode } from 'react'; import { useLocalStorage } from '@/hooks/useLocalStorage'; import { LanguageContext, getNestedValue, interpolate, translations, type Language, } from './language'; import { setDateLocale } from '@/lib/dateFnsI18n'; interface LanguageProviderProps { children: ReactNode; } /** * Pick the starting language from the visitor's browser on their FIRST visit. * A device set to Spanish (`es-ES`, `es-MX`, …) lands in Castellano; anything * else falls back to English. Once the user taps the language toggle their * choice is stored and always wins. */ function detectLanguage(): Language { if (typeof navigator === 'undefined') return 'en'; const tags = [navigator.language, ...(navigator.languages ?? [])]; return tags.some((tag) => tag?.toLowerCase().startsWith('es')) ? 'es' : 'en'; } /** * Provides language state + translation. The context, types and hooks live in * `./language` so this file exports only the component (React Fast Refresh). */ export function LanguageProvider({ children }: LanguageProviderProps) { const [storedLanguage, setStoredLanguage] = useLocalStorage('app-language', detectLanguage()); const [language, setLanguageState] = useState(storedLanguage); // Sync with localStorage useEffect(() => { setLanguageState(storedLanguage); }, [storedLanguage]); const setLanguage = useCallback((lang: Language) => { setLanguageState(lang); setStoredLanguage(lang); // Update HTML lang attribute for accessibility document.documentElement.lang = lang; }, [setStoredLanguage]); // Set initial HTML lang attribute useEffect(() => { document.documentElement.lang = language; }, [language]); const t = useCallback((key: string, params?: Record): string => { // Try to get translation in current language const translation = getNestedValue(translations[language], key); if (translation) { return interpolate(translation, params); } // Fallback to English if (language !== 'en') { const fallback = getNestedValue(translations.en, key); if (fallback) { return interpolate(fallback, params); } } // Return key if no translation found (helps identify missing translations) console.warn(`Missing translation for key: ${key}`); return key; }, [language]); // Currently no RTL languages supported, but ready for future const isRTL = false; // Keep date-fns + native toLocale* formatting in the app language. Called // during render (before children) so the first paint is already in-language; // it is an idempotent module assignment, not React state. setDateLocale(language); return ( {children} ); }