Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
59 lines
1.9 KiB
TypeScript
59 lines
1.9 KiB
TypeScript
import { createContext, useContext, useEffect, useMemo, useState, type ReactNode } from 'react';
|
|
import { createT, formatAmd, formatDateTime, isLocale, type Locale, type TFunction } from '@barev/i18n';
|
|
import { reportClientError } from './telegram.ts';
|
|
|
|
interface I18nValue {
|
|
locale: Locale;
|
|
t: TFunction;
|
|
setLocale: (l: Locale) => void;
|
|
amd: (n: number) => string;
|
|
dt: (d: Date, opts?: Intl.DateTimeFormatOptions) => string;
|
|
}
|
|
|
|
const Ctx = createContext<I18nValue | null>(null);
|
|
|
|
export function pickLocale(...candidates: (string | null | undefined)[]): Locale {
|
|
for (const c of candidates) {
|
|
const base = c?.split('-')[0]?.toLowerCase();
|
|
if (isLocale(base)) return base;
|
|
}
|
|
return 'ru';
|
|
}
|
|
|
|
export function I18nProvider({ initial, children }: { initial: Locale; children: ReactNode }) {
|
|
const [locale, setLocale] = useState<Locale>(initial);
|
|
useEffect(() => setLocale(initial), [initial]);
|
|
useEffect(() => {
|
|
document.documentElement.lang = locale;
|
|
}, [locale]);
|
|
const value = useMemo<I18nValue>(
|
|
() => ({
|
|
locale,
|
|
t: createT(locale),
|
|
setLocale,
|
|
amd: (n) => formatAmd(locale, n),
|
|
dt: (d, opts) => formatDateTime(locale, d, opts),
|
|
}),
|
|
[locale],
|
|
);
|
|
return <Ctx.Provider value={value}>{children}</Ctx.Provider>;
|
|
}
|
|
|
|
export function useI18n(): I18nValue {
|
|
const v = useContext(Ctx);
|
|
if (!v) throw new Error('I18nProvider missing');
|
|
return v;
|
|
}
|
|
|
|
/** Текст ошибки API: errors.<код бизнес-ошибки> или общий */
|
|
export function errorText(t: TFunction, err: unknown): string {
|
|
reportClientError(err, 'errorText');
|
|
const e = err as { data?: { domainCode?: string | null; code?: string }; message?: string } | null;
|
|
const code = e?.data?.domainCode ?? e?.message;
|
|
if (code && /^[A-Z0-9_]+$/.test(code)) {
|
|
const s = t(`errors.${code}`);
|
|
if (s !== `errors.${code}`) return s;
|
|
}
|
|
return t('errors.generic');
|
|
}
|