60 lines
1.5 KiB
TypeScript
60 lines
1.5 KiB
TypeScript
"use client"
|
|
|
|
import React, { createContext, useContext, useState, useCallback, useEffect } from "react"
|
|
import en from "@/locales/en.json"
|
|
import zh from "@/locales/zh.json"
|
|
|
|
type Locale = "en" | "zh"
|
|
|
|
const messages: Record<Locale, typeof en> = { en, zh }
|
|
|
|
interface I18nContextType {
|
|
locale: Locale
|
|
setLocale: (locale: Locale) => void
|
|
t: (key: string) => string
|
|
}
|
|
|
|
const I18nContext = createContext<I18nContextType | null>(null)
|
|
|
|
function getNestedValue(obj: any, path: string): string {
|
|
const value = path.split(".").reduce((acc, part) => acc?.[part], obj)
|
|
return typeof value === "string" ? value : Array.isArray(value) ? value.join(", ") : path
|
|
}
|
|
|
|
export function I18nProvider({ children }: { children: React.ReactNode }) {
|
|
const [locale, setLocaleState] = useState<Locale>("zh")
|
|
|
|
useEffect(() => {
|
|
const saved = localStorage.getItem("locale") as Locale | null
|
|
if (saved && (saved === "en" || saved === "zh")) {
|
|
setLocaleState(saved)
|
|
}
|
|
}, [])
|
|
|
|
const setLocale = useCallback((l: Locale) => {
|
|
setLocaleState(l)
|
|
localStorage.setItem("locale", l)
|
|
}, [])
|
|
|
|
const t = useCallback(
|
|
(key: string) => getNestedValue(messages[locale], key),
|
|
[locale]
|
|
)
|
|
|
|
return (
|
|
<I18nContext.Provider value={{ locale, setLocale, t }}>
|
|
{children}
|
|
</I18nContext.Provider>
|
|
)
|
|
}
|
|
|
|
export function useI18n() {
|
|
const ctx = useContext(I18nContext)
|
|
if (!ctx) throw new Error("useI18n must be used within I18nProvider")
|
|
return ctx
|
|
}
|
|
|
|
export function useTranslation() {
|
|
return useI18n()
|
|
}
|