Files
soroushasadi/lib/i18n/locale-context.tsx
T
soroush.asadi add78d8460
ci / build (push) Failing after 23s
deploy / deploy (push) Failing after 10m12s
first commit
2026-05-31 12:47:02 +03:30

96 lines
2.2 KiB
TypeScript

'use client';
import {
createContext,
useContext,
useEffect,
useMemo,
useState,
type ReactNode,
} from 'react';
import { DEFAULT_LOCALE, dict, type Dict, type Locale } from './dictionaries';
type Direction = 'rtl' | 'ltr';
type Ctx = {
locale: Locale;
dir: Direction;
t: Dict;
setLocale: (l: Locale) => void;
toggle: () => void;
};
const LocaleContext = createContext<Ctx | null>(null);
const STORAGE_KEY = 'sa.locale';
function dirFor(locale: Locale): Direction {
return locale === 'fa' ? 'rtl' : 'ltr';
}
export function LocaleProvider({
initialLocale,
content,
children,
}: {
initialLocale?: Locale;
/**
* Server-resolved content (dict defaults merged with admin overrides).
* When omitted we fall back to the in-code dictionary, so the provider keeps
* working in isolation (tests, storybook, etc.).
*/
content?: { fa: Dict; en: Dict };
children: ReactNode;
}) {
const [locale, setLocaleState] = useState<Locale>(initialLocale ?? DEFAULT_LOCALE);
const source = content ?? (dict as unknown as { fa: Dict; en: Dict });
// Hydrate from localStorage on the client.
useEffect(() => {
try {
const saved = window.localStorage.getItem(STORAGE_KEY) as Locale | null;
if (saved === 'fa' || saved === 'en') {
setLocaleState(saved);
}
} catch {
/* noop */
}
}, []);
// Reflect locale + direction on the html element without a reload.
useEffect(() => {
const html = document.documentElement;
html.lang = locale;
html.dir = dirFor(locale);
html.dataset.locale = locale;
}, [locale]);
const setLocale = (l: Locale) => {
setLocaleState(l);
try {
window.localStorage.setItem(STORAGE_KEY, l);
} catch {
/* noop */
}
};
const value = useMemo<Ctx>(
() => ({
locale,
dir: dirFor(locale),
t: source[locale],
setLocale,
toggle: () => setLocale(locale === 'fa' ? 'en' : 'fa'),
}),
[locale, source],
);
return <LocaleContext.Provider value={value}>{children}</LocaleContext.Provider>;
}
export function useLocale() {
const ctx = useContext(LocaleContext);
if (!ctx) throw new Error('useLocale must be used inside <LocaleProvider>');
return ctx;
}