Files

105 lines
2.3 KiB
TypeScript

"use client";
import {
createContext,
useCallback,
useContext,
useEffect,
useMemo,
useState,
type ReactNode,
} from "react";
import { isChinaUser } from "is-china-user";
import { dictionaries, type Dictionary, type Locale } from "~/lib/i18n";
const STORAGE_KEY = "nomadro-locale";
type LocaleContextValue = {
locale: Locale;
isChina: boolean;
ready: boolean;
t: Dictionary;
setLocale: (locale: Locale) => void;
};
const LocaleContext = createContext<LocaleContextValue | null>(null);
function isLocale(value: string | null): value is Locale {
return value === "zh" || value === "en";
}
export function LocaleProvider({ children }: { children: ReactNode }) {
const [locale, setLocaleState] = useState<Locale>("zh");
const [ready, setReady] = useState(false);
useEffect(() => {
let cancelled = false;
try {
// Prefer auto detection; only keep a locale if user unlocked and switched.
const unlocked = window.sessionStorage.getItem("nomadro-lang-unlock") === "1";
const saved = window.localStorage.getItem(STORAGE_KEY);
if (unlocked && isLocale(saved)) {
if (!cancelled) {
setLocaleState(saved);
setReady(true);
}
return;
}
const china = Boolean(isChinaUser());
if (!cancelled) {
setLocaleState(china ? "zh" : "en");
setReady(true);
}
} catch {
if (!cancelled) {
setLocaleState("zh");
setReady(true);
}
}
return () => {
cancelled = true;
};
}, []);
useEffect(() => {
if (!ready) return;
document.documentElement.lang = dictionaries[locale].htmlLang;
}, [locale, ready]);
const setLocale = useCallback((next: Locale) => {
setLocaleState(next);
try {
window.localStorage.setItem(STORAGE_KEY, next);
} catch {
// ignore storage failures
}
}, []);
const value = useMemo<LocaleContextValue>(
() => ({
locale,
isChina: locale === "zh",
ready,
t: dictionaries[locale],
setLocale,
}),
[locale, ready, setLocale],
);
return (
<LocaleContext.Provider value={value}>{children}</LocaleContext.Provider>
);
}
export function useLocale() {
const ctx = useContext(LocaleContext);
if (!ctx) {
throw new Error("useLocale must be used within LocaleProvider");
}
return ctx;
}