Files
bedolaga-cabinet/src/i18n.ts
c0mrade 0caba3dffb feat(a11y,i18n): more modal focus-traps + Telegram theme/language sync
Modal a11y:
- focus-trap + role=dialog/aria-modal/aria-labelledby on AdminRoles delete confirm
  and AdminBanSystem user-detail modal (+ close aria-label)

Telegram theme/language sync (first run only, explicit choice always wins):
- getTelegramColorScheme() / getTelegramLanguageCode() helpers in useTelegramSDK
- useTheme initial state follows the Telegram client color scheme when no stored theme
- applyTelegramLanguage() adopts the Telegram client language when no stored choice,
  called from main.tsx after SDK init (launch params unavailable before init)
2026-05-25 23:31:38 +03:00

84 lines
2.3 KiB
TypeScript

import i18n, { type ResourceLanguage } from 'i18next';
import { initReactI18next } from 'react-i18next';
import LanguageDetector from 'i18next-browser-languagedetector';
import { getTelegramLanguageCode } from './hooks/useTelegramSDK';
const localeLoaders: Record<string, () => Promise<{ default: ResourceLanguage }>> = {
ru: () => import('./locales/ru.json'),
en: () => import('./locales/en.json'),
zh: () => import('./locales/zh.json'),
fa: () => import('./locales/fa.json'),
};
const SUPPORTED_LANGS = Object.keys(localeLoaders);
const FALLBACK_LNG = 'ru';
const LANGUAGE_STORAGE_KEY = 'cabinet_language';
const loadedLanguages = new Set<string>();
async function loadLanguage(lng: string): Promise<void> {
if (loadedLanguages.has(lng)) return;
const loader = localeLoaders[lng];
if (!loader) return;
const mod = await loader();
i18n.addResourceBundle(lng, 'translation', mod.default, true, true);
loadedLanguages.add(lng);
}
i18n
.use(LanguageDetector)
.use(initReactI18next)
.init({
fallbackLng: FALLBACK_LNG,
supportedLngs: SUPPORTED_LANGS,
partialBundledLanguages: true,
detection: {
order: ['localStorage', 'navigator'],
caches: ['localStorage'],
lookupLocalStorage: 'cabinet_language',
},
interpolation: {
escapeValue: false,
},
react: {
useSuspense: false,
},
showSupportNotice: false,
});
// Load detected language + fallback on startup
const detectedLng = i18n.language?.split('-')[0] || FALLBACK_LNG;
const langsToLoad = [FALLBACK_LNG, ...(detectedLng !== FALLBACK_LNG ? [detectedLng] : [])];
Promise.all(langsToLoad.map(loadLanguage));
// Lazy-load on language change
i18n.on('languageChanged', (lng: string) => {
const code = lng.split('-')[0];
loadLanguage(code);
});
/**
* On first run inside Telegram (no explicit stored choice), adopt the user's
* Telegram client language. Must be called after the Telegram SDK is initialised
* (e.g. from main.tsx), since launch params are unavailable before init().
*/
export function applyTelegramLanguage(): void {
try {
if (localStorage.getItem(LANGUAGE_STORAGE_KEY)) return; // explicit choice wins
} catch {
return;
}
const code = getTelegramLanguageCode();
if (code && SUPPORTED_LANGS.includes(code) && i18n.language?.split('-')[0] !== code) {
i18n.changeLanguage(code);
}
}
export default i18n;