kintex/mobile/context/LanguageContext.tsx

63 lines
2.1 KiB
TypeScript

/*
* 언어 컨텍스트 — 부팅 시 저장 언어 로드 + 언어 전환 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<void>;
supported: readonly Lang[];
}
const LanguageContext = createContext<LanguageContextValue | undefined>(undefined);
export function LanguageProvider({ children }: { children: React.ReactNode }) {
const { i18n } = useTranslation();
const [lang, setLang] = useState<Lang>(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<LanguageContextValue>(
() => ({
lang,
supported: SUPPORTED_LANGS,
setLanguage: async (l: Lang) => {
await setAppLanguage(l);
setLang(l);
},
}),
[lang],
);
return <LanguageContext.Provider value={value}>{children}</LanguageContext.Provider>;
}
export function useLanguage(): LanguageContextValue {
const ctx = useContext(LanguageContext);
if (!ctx) throw new Error('useLanguage must be used within LanguageProvider');
return ctx;
}