'use client'; import React, { createContext, useContext, useState, useCallback, ReactNode, useEffect } from 'react'; import en from '@/i18n/en.json'; import es from '@/i18n/es.json'; import ptBR from '@/i18n/pt-BR.json'; type Locale = 'en' | 'es' | 'pt-BR'; interface I18nContextType { locale: Locale; setLocale: (locale: Locale) => void; t: (key: string, params?: Record) => string; } const dictionaries: Record> = { en: en as Record, es: es as Record, 'pt-BR': ptBR as Record, }; const VALID_LOCALES: Locale[] = ['en', 'es', 'pt-BR']; const I18nContext = createContext(null); const localeStorageKey = 'locale'; const normalizeLocale = (language: string): Locale => { if (language.startsWith('pt')) return 'pt-BR'; if (language.startsWith('es')) return 'es'; return 'en'; }; const getInitialLocale = (): Locale => { if (typeof window === 'undefined') return 'pt-BR'; try { const storedLocale = localStorage.getItem(localeStorageKey); if (storedLocale && VALID_LOCALES.includes(storedLocale as Locale)) { return storedLocale as Locale; } } catch { // localStorage might be blocked } if (typeof navigator !== 'undefined' && navigator.language) { return normalizeLocale(navigator.language); } return 'pt-BR'; }; function resolveKey(dict: Record, key: string): string | undefined { const keys = key.split('.'); let value: unknown = dict; for (const k of keys) { if (value && typeof value === 'object' && value !== null && k in value) { value = (value as Record)[k]; } else { return undefined; } } return typeof value === 'string' ? value : undefined; } export function I18nProvider({ children }: { children: ReactNode }) { const [locale, setLocaleState] = useState('pt-BR'); useEffect(() => { let initialLocale: Locale = 'pt-BR'; try { const storedLocale = localStorage.getItem(localeStorageKey); if (storedLocale && VALID_LOCALES.includes(storedLocale as Locale)) { initialLocale = storedLocale as Locale; } else if (navigator.language) { initialLocale = normalizeLocale(navigator.language); } } catch { // localStorage might be blocked } setLocaleState(initialLocale); }, []); const setLocale = (newLocale: Locale) => { setLocaleState(newLocale); }; const t = useCallback((key: string, params?: Record) => { // Try current locale first, then fallback to pt-BR, then en const dict = dictionaries[locale] || dictionaries['pt-BR']; let result = resolveKey(dict, key); // Fallback chain: try pt-BR if not found, then en if (result === undefined && locale !== 'pt-BR') { result = resolveKey(dictionaries['pt-BR'], key); } if (result === undefined && locale !== 'en') { result = resolveKey(dictionaries['en'], key); } if (result === undefined) { return key; } if (params) { return Object.entries(params).reduce( (str, [paramKey, paramValue]) => str.replace(new RegExp(`\\{${paramKey}\\}`, 'g'), String(paramValue)), result ); } return result; }, [locale]); useEffect(() => { if (typeof window === 'undefined') return; try { localStorage.setItem(localeStorageKey, locale); } catch { // localStorage might be blocked } document.documentElement.lang = locale; }, [locale]); return ( {children} ); } export function useI18n() { const context = useContext(I18nContext); if (!context) { throw new Error('useI18n must be used within an I18nProvider'); } return context; } export function useTranslation() { const { t, locale, setLocale } = useI18n(); return { t, locale, setLocale }; } export const locales: { code: Locale; name: string; flag: string }[] = [ { code: 'en', name: 'English', flag: '🇺🇸' }, { code: 'es', name: 'Español', flag: '🇪🇸' }, { code: 'pt-BR', name: 'Português', flag: '🇧🇷' }, ];