mirror of
https://github.com/chillpadclub/bedolaga-cabinet.git
synced 2026-07-29 18:13:47 +00:00
fix(i18n,a11y): sync <html lang> + dir with i18n language
index.html ships <html lang='ru'> and that attribute never changed at runtime, regardless of the user's selected language. Consequences: - screen readers pronounced en/zh/fa content using Russian phonics - browsers offered to translate already-translated content - the Telegram login button and an OAuth widget read documentElement.lang to set their own locale and got the wrong one LanguageSwitcher had a half-fix that only touched dir for fa, and only locally — i18n changes from anywhere else (initial detect, Telegram adopt) were not covered. Move both lang and dir updates into a single languageChanged subscriber inside i18n.ts, run once for the initial detected language, and drop the now-redundant dir-setting in LanguageSwitcher. Layout direction flips automatically for the full RTL set (fa + future ar/he/ur).
This commit is contained in:
@@ -34,15 +34,12 @@ export default function LanguageSwitcher() {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const changeLanguage = (code: string) => {
|
const changeLanguage = (code: string) => {
|
||||||
|
// i18n.ts subscribes to languageChanged and syncs <html lang> + dir
|
||||||
|
// centrally — no need to set documentElement.dir here.
|
||||||
i18n.changeLanguage(code);
|
i18n.changeLanguage(code);
|
||||||
document.documentElement.dir = code === 'fa' ? 'rtl' : 'ltr';
|
|
||||||
setIsOpen(false);
|
setIsOpen(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
document.documentElement.dir = i18n.language === 'fa' ? 'rtl' : 'ltr';
|
|
||||||
}, [i18n.language]);
|
|
||||||
|
|
||||||
if (availableLanguages.length <= 1) {
|
if (availableLanguages.length <= 1) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
19
src/i18n.ts
19
src/i18n.ts
@@ -57,10 +57,29 @@ const detectedLng = i18n.language?.split('-')[0] || FALLBACK_LNG;
|
|||||||
const langsToLoad = [FALLBACK_LNG, ...(detectedLng !== FALLBACK_LNG ? [detectedLng] : [])];
|
const langsToLoad = [FALLBACK_LNG, ...(detectedLng !== FALLBACK_LNG ? [detectedLng] : [])];
|
||||||
Promise.all(langsToLoad.map(loadLanguage));
|
Promise.all(langsToLoad.map(loadLanguage));
|
||||||
|
|
||||||
|
// Keep <html lang> + dir in sync with i18n so screen readers pronounce
|
||||||
|
// content correctly, browsers don't offer to translate it, and RTL
|
||||||
|
// languages (fa) flip layout direction. index.html ships with lang="ru"
|
||||||
|
// for the first paint; runtime updates take over from there.
|
||||||
|
const RTL_LANGS = new Set(['fa', 'ar', 'he', 'ur']);
|
||||||
|
function syncHtmlLang(lng: string): void {
|
||||||
|
const code = lng.split('-')[0];
|
||||||
|
if (typeof document === 'undefined') return;
|
||||||
|
if (document.documentElement.lang !== code) {
|
||||||
|
document.documentElement.lang = code;
|
||||||
|
}
|
||||||
|
const dir = RTL_LANGS.has(code) ? 'rtl' : 'ltr';
|
||||||
|
if (document.documentElement.dir !== dir) {
|
||||||
|
document.documentElement.dir = dir;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
syncHtmlLang(detectedLng);
|
||||||
|
|
||||||
// Lazy-load on language change
|
// Lazy-load on language change
|
||||||
i18n.on('languageChanged', (lng: string) => {
|
i18n.on('languageChanged', (lng: string) => {
|
||||||
const code = lng.split('-')[0];
|
const code = lng.split('-')[0];
|
||||||
loadLanguage(code);
|
loadLanguage(code);
|
||||||
|
syncHtmlLang(code);
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user