/* * 언어 컨텍스트 — 부팅 시 저장 언어 로드 + 언어 전환 API 제공. * i18n 초기화 자체는 lib/i18n(동기)에서 끝나므로 렌더를 막지 않는다(로딩 게이트 불필요). * react-i18next가 languageChanged에 재렌더를 트리거하므로 useTranslation 사용부는 자동 갱신된다. */ import React, { createContext, useContext, useEffect, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { loadStoredLanguage, normalizeLang, setAppLanguage } from '../lib/i18n'; import { SUPPORTED_LANGS, type Lang } from '../lib/i18n/types'; interface LanguageContextValue { lang: Lang; setLanguage: (lang: Lang) => Promise; supported: readonly Lang[]; } const LanguageContext = createContext(undefined); export function LanguageProvider({ children }: { children: React.ReactNode }) { const { i18n } = useTranslation(); const [lang, setLang] = useState(normalizeLang(i18n.language)); // 부팅 시 저장된 사용자 선택 언어 반영(없으면 디바이스 언어 유지). useEffect(() => { let cancelled = false; loadStoredLanguage().then((l) => { if (!cancelled) setLang(l); }); return () => { cancelled = true; }; }, []); // 외부(예: 다른 탭)에서 언어가 바뀌어도 로컬 상태 동기화. useEffect(() => { const onChanged = (l: string) => setLang(normalizeLang(l)); i18n.on('languageChanged', onChanged); return () => { i18n.off('languageChanged', onChanged); }; }, [i18n]); const value = useMemo( () => ({ lang, supported: SUPPORTED_LANGS, setLanguage: async (l: Lang) => { await setAppLanguage(l); setLang(l); }, }), [lang], ); return {children}; } export function useLanguage(): LanguageContextValue { const ctx = useContext(LanguageContext); if (!ctx) throw new Error('useLanguage must be used within LanguageProvider'); return ctx; }