SignalMod / frontend /src /i18n /I18nContext.tsx
Ruperth's picture
feat: add i18n context with es and en dictionaries
21e8095
Raw
History Blame Contribute Delete
1.69 kB
import {
createContext,
useCallback,
useContext,
useEffect,
useMemo,
useState,
type ReactNode,
} from "react";
import { DICTIONARIES, type Dict, type Locale } from "./locales";
const STORAGE_KEY = "signalmod.locale";
type I18nValue = {
locale: Locale;
setLocale: (l: Locale) => void;
toggleLocale: () => void;
t: Dict;
};
const I18nContext = createContext<I18nValue | null>(null);
function detectInitialLocale(): Locale {
if (typeof window === "undefined") return "es";
const stored = window.localStorage.getItem(STORAGE_KEY);
if (stored === "es" || stored === "en") return stored;
const nav = window.navigator.language?.toLowerCase() ?? "";
return nav.startsWith("es") ? "es" : "en";
}
export function I18nProvider({ children }: { children: ReactNode }) {
const [locale, setLocaleState] = useState<Locale>(() => detectInitialLocale());
useEffect(() => {
if (typeof document !== "undefined") {
document.documentElement.lang = locale;
}
if (typeof window !== "undefined") {
window.localStorage.setItem(STORAGE_KEY, locale);
}
}, [locale]);
const setLocale = useCallback((l: Locale) => setLocaleState(l), []);
const toggleLocale = useCallback(
() => setLocaleState((prev) => (prev === "es" ? "en" : "es")),
[]
);
const value = useMemo<I18nValue>(
() => ({ locale, setLocale, toggleLocale, t: DICTIONARIES[locale] }),
[locale, setLocale, toggleLocale]
);
return <I18nContext.Provider value={value}>{children}</I18nContext.Provider>;
}
export function useI18n() {
const ctx = useContext(I18nContext);
if (!ctx) throw new Error("useI18n must be used within I18nProvider");
return ctx;
}