- app.json: version 0.2.0 / versionCode 2, drop expo-screen-capture plugin - add lib/i18n (ko/en/ja/zh), lib/roleTrack, LanguageContext + LanguageSelector - profile/security hardening across screens (login/register/forgot/profile/tabs) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
63 lines
2.1 KiB
TypeScript
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;
|
|
}
|