Detect locale with is-china-user and show English copy for non-Chinese visitors.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
eric
2026-08-03 08:54:39 -05:00
parent 4c2101a87a
commit af0a430864
9 changed files with 233 additions and 20 deletions

View File

@@ -0,0 +1,75 @@
"use client";
import {
createContext,
useContext,
useEffect,
useMemo,
useState,
type ReactNode,
} from "react";
import { isChinaUser } from "is-china-user";
import { dictionaries, type Dictionary, type Locale } from "~/lib/i18n";
type LocaleContextValue = {
locale: Locale;
isChina: boolean;
ready: boolean;
t: Dictionary;
};
const LocaleContext = createContext<LocaleContextValue | null>(null);
export function LocaleProvider({ children }: { children: ReactNode }) {
// Default zh to match primary audience; switch after client detection.
const [locale, setLocale] = useState<Locale>("zh");
const [ready, setReady] = useState(false);
useEffect(() => {
let cancelled = false;
try {
const china = Boolean(isChinaUser());
if (!cancelled) {
setLocale(china ? "zh" : "en");
setReady(true);
}
} catch {
if (!cancelled) {
setLocale("zh");
setReady(true);
}
}
return () => {
cancelled = true;
};
}, []);
useEffect(() => {
if (!ready) return;
document.documentElement.lang = dictionaries[locale].htmlLang;
}, [locale, ready]);
const value = useMemo<LocaleContextValue>(
() => ({
locale,
isChina: locale === "zh",
ready,
t: dictionaries[locale],
}),
[locale, ready],
);
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;
}