52 lines
1.6 KiB
TypeScript
52 lines
1.6 KiB
TypeScript
import { createContext, useContext, useEffect, useMemo, useState, type ReactNode } from 'react'
|
|
import { UI_LOCALES, type UiLocale } from '@/config/languages'
|
|
import { zh, type LocaleDict } from './locales/zh'
|
|
import { en } from './locales/en'
|
|
import { ar } from './locales/ar'
|
|
|
|
const DICTS: Record<UiLocale, LocaleDict> = { zh, en, ar }
|
|
|
|
interface I18nContextValue {
|
|
locale: UiLocale
|
|
dir: 'ltr' | 'rtl'
|
|
setLocale: (l: UiLocale) => void
|
|
t: (key: keyof LocaleDict) => string
|
|
}
|
|
|
|
const I18nContext = createContext<I18nContextValue | null>(null)
|
|
|
|
const STORAGE_KEY = 'lx-locale'
|
|
|
|
export function I18nProvider({ children }: { children: ReactNode }) {
|
|
const [locale, setLocaleState] = useState<UiLocale>(() => {
|
|
const saved = localStorage.getItem(STORAGE_KEY)
|
|
return (saved === 'en' || saved === 'ar' || saved === 'zh' ? saved : 'zh') as UiLocale
|
|
})
|
|
|
|
const dir: 'ltr' | 'rtl' = UI_LOCALES.find((l) => l.code === locale)?.rtl ? 'rtl' : 'ltr'
|
|
|
|
useEffect(() => {
|
|
document.documentElement.lang = locale === 'zh' ? 'zh-CN' : locale
|
|
document.documentElement.dir = dir
|
|
localStorage.setItem(STORAGE_KEY, locale)
|
|
}, [locale, dir])
|
|
|
|
const value = useMemo<I18nContextValue>(
|
|
() => ({
|
|
locale,
|
|
dir,
|
|
setLocale: setLocaleState,
|
|
t: (key) => DICTS[locale][key] ?? zh[key] ?? String(key),
|
|
}),
|
|
[locale, dir],
|
|
)
|
|
|
|
return <I18nContext.Provider value={value}>{children}</I18nContext.Provider>
|
|
}
|
|
|
|
export function useI18n(): I18nContextValue {
|
|
const ctx = useContext(I18nContext)
|
|
if (!ctx) throw new Error('useI18n must be used within I18nProvider')
|
|
return ctx
|
|
}
|