new: add multilingual support with translations for accessibility widget

This commit is contained in:
sinanisler
2025-12-22 02:29:51 +03:00
parent e1470ddfa7
commit 2d4d0f0755
+511 -35
View File
@@ -6,6 +6,393 @@
===========================================
*/
// ===========================================
// TRANSLATIONS
// ===========================================
const TRANSLATIONS = {
en: {
accessibilityMenu: 'Accessibility Menu',
closeAccessibilityMenu: 'Close Accessibility Menu',
accessibilityTools: 'Accessibility Tools',
resetAllSettings: 'Reset All Settings',
screenReader: 'Screen Reader',
voiceCommand: 'Voice Command',
textSpacing: 'Text Spacing',
pauseAnimations: 'Pause Animations',
hideImages: 'Hide Images',
dyslexiaFriendly: 'Dyslexia Friendly',
biggerCursor: 'Bigger Cursor',
lineHeight: 'Line Height',
fontSelection: 'Font Selection',
colorFilter: 'Color Filter',
textAlign: 'Text Align',
textSize: 'Text Size',
highContrast: 'High Contrast',
defaultFont: 'Default Font',
noFilter: 'No Filter',
default: 'Default',
screenReaderOn: 'Screen reader on',
screenReaderOff: 'Screen reader off',
voiceControlActivated: 'Voice control activated',
notSupportedBrowser: 'is not supported in this browser',
close: 'Close',
reset: 'Reset',
saturation: 'Saturation',
selectLanguage: 'Select Language'
},
de: {
accessibilityMenu: 'Barrierefreiheitsmenü',
closeAccessibilityMenu: 'Barrierefreiheitsmenü schließen',
accessibilityTools: 'Barrierefreiheitswerkzeuge',
resetAllSettings: 'Alle Einstellungen zurücksetzen',
screenReader: 'Screenreader',
voiceCommand: 'Sprachbefehl',
textSpacing: 'Textabstand',
pauseAnimations: 'Animationen pausieren',
hideImages: 'Bilder ausblenden',
dyslexiaFriendly: 'Legasthenie-freundlich',
biggerCursor: 'Größerer Cursor',
lineHeight: 'Zeilenhöhe',
fontSelection: 'Schriftauswahl',
colorFilter: 'Farbfilter',
textAlign: 'Textausrichtung',
textSize: 'Textgröße',
highContrast: 'Hoher Kontrast',
defaultFont: 'Standardschrift',
noFilter: 'Kein Filter',
default: 'Standard',
screenReaderOn: 'Screenreader ein',
screenReaderOff: 'Screenreader aus',
voiceControlActivated: 'Sprachsteuerung aktiviert',
notSupportedBrowser: 'wird in diesem Browser nicht unterstützt',
close: 'Schließen',
reset: 'Zurücksetzen',
saturation: 'Sättigung',
selectLanguage: 'Sprache wählen'
},
es: {
accessibilityMenu: 'Menú de Accesibilidad',
closeAccessibilityMenu: 'Cerrar Menú de Accesibilidad',
accessibilityTools: 'Herramientas de Accesibilidad',
resetAllSettings: 'Restablecer Todas las Configuraciones',
screenReader: 'Lector de Pantalla',
voiceCommand: 'Comando de Voz',
textSpacing: 'Espaciado de Texto',
pauseAnimations: 'Pausar Animaciones',
hideImages: 'Ocultar Imágenes',
dyslexiaFriendly: 'Amigable para Dislexia',
biggerCursor: 'Cursor Más Grande',
lineHeight: 'Altura de Línea',
fontSelection: 'Selección de Fuente',
colorFilter: 'Filtro de Color',
textAlign: 'Alineación de Texto',
textSize: 'Tamaño de Texto',
highContrast: 'Alto Contraste',
defaultFont: 'Fuente Predeterminada',
noFilter: 'Sin Filtro',
default: 'Predeterminado',
screenReaderOn: 'Lector de pantalla activado',
screenReaderOff: 'Lector de pantalla desactivado',
voiceControlActivated: 'Control de voz activado',
notSupportedBrowser: 'no es compatible con este navegador',
close: 'Cerrar',
reset: 'Restablecer',
saturation: 'Saturación',
selectLanguage: 'Seleccionar Idioma'
},
it: {
accessibilityMenu: 'Menu Accessibilità',
closeAccessibilityMenu: 'Chiudi Menu Accessibilità',
accessibilityTools: 'Strumenti di Accessibilità',
resetAllSettings: 'Ripristina Tutte le Impostazioni',
screenReader: 'Lettore Schermo',
voiceCommand: 'Comando Vocale',
textSpacing: 'Spaziatura Testo',
pauseAnimations: 'Pausa Animazioni',
hideImages: 'Nascondi Immagini',
dyslexiaFriendly: 'Adatto alla Dislessia',
biggerCursor: 'Cursore Più Grande',
lineHeight: 'Altezza Linea',
fontSelection: 'Selezione Font',
colorFilter: 'Filtro Colore',
textAlign: 'Allineamento Testo',
textSize: 'Dimensione Testo',
highContrast: 'Alto Contrasto',
defaultFont: 'Font Predefinito',
noFilter: 'Nessun Filtro',
default: 'Predefinito',
screenReaderOn: 'Lettore schermo attivo',
screenReaderOff: 'Lettore schermo disattivo',
voiceControlActivated: 'Controllo vocale attivato',
notSupportedBrowser: 'non è supportato in questo browser',
close: 'Chiudi',
reset: 'Ripristina',
saturation: 'Saturazione',
selectLanguage: 'Seleziona Lingua'
},
fr: {
accessibilityMenu: 'Menu Accessibilité',
closeAccessibilityMenu: 'Fermer le Menu Accessibilité',
accessibilityTools: 'Outils d\'Accessibilité',
resetAllSettings: 'Réinitialiser Tous les Paramètres',
screenReader: 'Lecteur d\'Écran',
voiceCommand: 'Commande Vocale',
textSpacing: 'Espacement du Texte',
pauseAnimations: 'Mettre en Pause les Animations',
hideImages: 'Masquer les Images',
dyslexiaFriendly: 'Convivial pour la Dyslexie',
biggerCursor: 'Curseur Plus Grand',
lineHeight: 'Hauteur de Ligne',
fontSelection: 'Sélection de Police',
colorFilter: 'Filtre de Couleur',
textAlign: 'Alignement du Texte',
textSize: 'Taille du Texte',
highContrast: 'Contraste Élevé',
defaultFont: 'Police par Défaut',
noFilter: 'Aucun Filtre',
default: 'Par Défaut',
screenReaderOn: 'Lecteur d\'écran activé',
screenReaderOff: 'Lecteur d\'écran désactivé',
voiceControlActivated: 'Contrôle vocal activé',
notSupportedBrowser: 'n\'est pas pris en charge dans ce navigateur',
close: 'Fermer',
reset: 'Réinitialiser',
saturation: 'Saturation',
selectLanguage: 'Sélectionner la Langue'
},
ru: {
accessibilityMenu: 'Меню Доступности',
closeAccessibilityMenu: 'Закрыть Меню Доступности',
accessibilityTools: 'Инструменты Доступности',
resetAllSettings: 'Сбросить Все Настройки',
screenReader: 'Программа Чтения с Экрана',
voiceCommand: 'Голосовая Команда',
textSpacing: 'Межбуквенный Интервал',
pauseAnimations: 'Приостановить Анимацию',
hideImages: 'Скрыть Изображения',
dyslexiaFriendly: 'Для Дислексии',
biggerCursor: 'Увеличенный Курсор',
lineHeight: 'Высота Строки',
fontSelection: 'Выбор Шрифта',
colorFilter: 'Цветовой Фильтр',
textAlign: 'Выравнивание Текста',
textSize: 'Размер Текста',
highContrast: 'Высокая Контрастность',
defaultFont: 'Шрифт по Умолчанию',
noFilter: 'Без Фильтра',
default: 'По Умолчанию',
screenReaderOn: 'Программа чтения включена',
screenReaderOff: 'Программа чтения выключена',
voiceControlActivated: 'Голосовое управление активировано',
notSupportedBrowser: 'не поддерживается в этом браузере',
close: 'Закрыть',
reset: 'Сбросить',
saturation: 'Насыщенность',
selectLanguage: 'Выберите Язык'
},
tr: {
accessibilityMenu: 'Erişilebilirlik Menüsü',
closeAccessibilityMenu: 'Erişilebilirlik Menüsünü Kapat',
accessibilityTools: 'Erişilebilirlik Araçları',
resetAllSettings: 'Tüm Ayarları Sıfırla',
screenReader: 'Ekran Okuyucu',
voiceCommand: 'Sesli Komut',
textSpacing: 'Metin Aralığı',
pauseAnimations: 'Animasyonları Duraklat',
hideImages: 'Resimleri Gizle',
dyslexiaFriendly: 'Disleksi Dostu',
biggerCursor: 'Daha Büyük İmleç',
lineHeight: 'Satır Yüksekliği',
fontSelection: 'Yazı Tipi Seçimi',
colorFilter: 'Renk Filtresi',
textAlign: 'Metin Hizalama',
textSize: 'Metin Boyutu',
highContrast: 'Yüksek Kontrast',
defaultFont: 'Varsayılan Yazı Tipi',
noFilter: 'Filtre Yok',
default: 'Varsayılan',
screenReaderOn: 'Ekran okuyucu açık',
screenReaderOff: 'Ekran okuyucu kapalı',
voiceControlActivated: 'Sesli kontrol etkinleştirildi',
notSupportedBrowser: 'bu tarayıcıda desteklenmiyor',
close: 'Kapat',
reset: 'Sıfırla',
saturation: 'Doygunluk',
selectLanguage: 'Dil Seçin'
},
ar: {
accessibilityMenu: 'قائمة إمكانية الوصول',
closeAccessibilityMenu: 'إغلاق قائمة إمكانية الوصول',
accessibilityTools: 'أدوات إمكانية الوصول',
resetAllSettings: 'إعادة تعيين جميع الإعدادات',
screenReader: 'قارئ الشاشة',
voiceCommand: 'الأمر الصوتي',
textSpacing: 'تباعد النص',
pauseAnimations: 'إيقاف الرسوم المتحركة مؤقتًا',
hideImages: 'إخفاء الصور',
dyslexiaFriendly: 'صديق لعسر القراءة',
biggerCursor: 'مؤشر أكبر',
lineHeight: 'ارتفاع الخط',
fontSelection: 'اختيار الخط',
colorFilter: 'مرشح الألوان',
textAlign: 'محاذاة النص',
textSize: 'حجم النص',
highContrast: 'تباين عالي',
defaultFont: 'الخط الافتراضي',
noFilter: 'بدون مرشح',
default: 'افتراضي',
screenReaderOn: 'قارئ الشاشة مفعّل',
screenReaderOff: 'قارئ الشاشة معطل',
voiceControlActivated: 'تم تفعيل التحكم الصوتي',
notSupportedBrowser: 'غير مدعوم في هذا المتصفح',
close: 'إغلاق',
reset: 'إعادة تعيين',
saturation: 'التشبع',
selectLanguage: 'اختر اللغة'
},
hi: {
accessibilityMenu: 'पहुँच मेनू',
closeAccessibilityMenu: 'पहुँच मेनू बंद करें',
accessibilityTools: 'पहुँच उपकरण',
resetAllSettings: 'सभी सेटिंग्स रीसेट करें',
screenReader: 'स्क्रीन रीडर',
voiceCommand: 'वॉयस कमांड',
textSpacing: 'टेक्स्ट स्पेसिंग',
pauseAnimations: 'एनिमेशन रोकें',
hideImages: 'चित्र छिपाएँ',
dyslexiaFriendly: 'डिस्लेक्सिया के अनुकूल',
biggerCursor: 'बड़ा कर्सर',
lineHeight: 'लाइन की ऊँचाई',
fontSelection: 'फ़ॉन्ट चयन',
colorFilter: 'रंग फ़िल्टर',
textAlign: 'टेक्स्ट संरेखण',
textSize: 'टेक्स्ट का आकार',
highContrast: 'उच्च कंट्रास्ट',
defaultFont: 'डिफ़ॉल्ट फ़ॉन्ट',
noFilter: 'कोई फ़िल्टर नहीं',
default: 'डिफ़ॉल्ट',
screenReaderOn: 'स्क्रीन रीडर चालू',
screenReaderOff: 'स्क्रीन रीडर बंद',
voiceControlActivated: 'वॉयस नियंत्रण सक्रिय',
notSupportedBrowser: 'इस ब्राउज़र में समर्थित नहीं है',
close: 'बंद करें',
reset: 'रीसेट करें',
saturation: 'संतृप्ति',
selectLanguage: 'भाषा चुनें'
},
'zh-cn': {
accessibilityMenu: '辅助功能菜单',
closeAccessibilityMenu: '关闭辅助功能菜单',
accessibilityTools: '辅助功能工具',
resetAllSettings: '重置所有设置',
screenReader: '屏幕阅读器',
voiceCommand: '语音命令',
textSpacing: '文本间距',
pauseAnimations: '暂停动画',
hideImages: '隐藏图片',
dyslexiaFriendly: '阅读障碍友好',
biggerCursor: '更大的光标',
lineHeight: '行高',
fontSelection: '字体选择',
colorFilter: '颜色滤镜',
textAlign: '文本对齐',
textSize: '文本大小',
highContrast: '高对比度',
defaultFont: '默认字体',
noFilter: '无滤镜',
default: '默认',
screenReaderOn: '屏幕阅读器已开启',
screenReaderOff: '屏幕阅读器已关闭',
voiceControlActivated: '语音控制已激活',
notSupportedBrowser: '此浏览器不支持',
close: '关闭',
reset: '重置',
saturation: '饱和度',
selectLanguage: '选择语言'
},
jp: {
accessibilityMenu: 'アクセシビリティメニュー',
closeAccessibilityMenu: 'アクセシビリティメニューを閉じる',
accessibilityTools: 'アクセシビリティツール',
resetAllSettings: 'すべての設定をリセット',
screenReader: 'スクリーンリーダー',
voiceCommand: '音声コマンド',
textSpacing: 'テキスト間隔',
pauseAnimations: 'アニメーション一時停止',
hideImages: '画像を非表示',
dyslexiaFriendly: 'ディスレクシア対応',
biggerCursor: '大きいカーソル',
lineHeight: '行の高さ',
fontSelection: 'フォント選択',
colorFilter: 'カラーフィルター',
textAlign: 'テキスト配置',
textSize: 'テキストサイズ',
highContrast: 'ハイコントラスト',
defaultFont: 'デフォルトフォント',
noFilter: 'フィルターなし',
default: 'デフォルト',
screenReaderOn: 'スクリーンリーダーがオン',
screenReaderOff: 'スクリーンリーダーがオフ',
voiceControlActivated: '音声制御が有効',
notSupportedBrowser: 'このブラウザではサポートされていません',
close: '閉じる',
reset: 'リセット',
saturation: '彩度',
selectLanguage: '言語を選択'
}
};
// Language detection and management
let currentLanguage = 'en';
function detectBrowserLanguage() {
const browserLang = (navigator.language || navigator.userLanguage).toLowerCase();
// Direct match
if (TRANSLATIONS[browserLang]) {
return browserLang;
}
// Try language code only (e.g., 'en' from 'en-US')
const langCode = browserLang.split('-')[0];
if (TRANSLATIONS[langCode]) {
return langCode;
}
// Special case for Chinese
if (browserLang.includes('zh')) {
if (browserLang.includes('cn') || browserLang.includes('hans')) {
return 'zh-cn';
}
}
// Default to English
return 'en';
}
function setLanguage(lang) {
if (TRANSLATIONS[lang]) {
currentLanguage = lang;
localStorage.setItem('accessibilityWidgetLanguage', lang);
return true;
}
return false;
}
function getTranslation(key) {
return TRANSLATIONS[currentLanguage][key] || TRANSLATIONS['en'][key] || key;
}
// Initialize language from localStorage or detect from browser
const savedLanguage = localStorage.getItem('accessibilityWidgetLanguage');
if (savedLanguage && TRANSLATIONS[savedLanguage]) {
currentLanguage = savedLanguage;
} else {
currentLanguage = detectBrowserLanguage();
localStorage.setItem('accessibilityWidgetLanguage', currentLanguage);
}
// ===========================================
// CONFIGURATION VARIABLES
// ===========================================
@@ -241,7 +628,7 @@ const widgetStyles = `
transition: background-color ${WIDGET_CONFIG.animation.transition}, border-color ${WIDGET_CONFIG.animation.transition};
line-height: ${WIDGET_CONFIG.typography.lineHeight} !important;
gap: 5px;
min-height: 110px;
min-height: 105px;
}
.snn-accessibility-option:hover {
@@ -393,6 +780,25 @@ const widgetStyles = `
padding: 20px;
}
.snn-language-selector {
width: 100%;
background: white;
color: black;
border: none;
padding: 12px;
font-size: 16px;
font-family: ${WIDGET_CONFIG.typography.fontFamily};
border-radius: 5px;
margin-bottom: 20px;
cursor: pointer;
outline: none;
}
.snn-language-selector:focus {
outline: 2px solid ${WIDGET_CONFIG.colors.primary};
outline-offset: 2px;
}
.snn-options-grid {
display: grid;
grid-template-columns: ${WIDGET_CONFIG.gridLayout.columns};
@@ -835,7 +1241,7 @@ function createAccessibilityButton() {
const button = document.createElement('button');
button.id = 'snn-accessibility-button';
button.innerHTML = icons.buttonsvg;
button.setAttribute('aria-label', WIDGET_CONFIG.lang.accessibilityMenu);
button.setAttribute('aria-label', getTranslation('accessibilityMenu'));
button.addEventListener('click', function () {
toggleMenu();
@@ -962,7 +1368,7 @@ function createToggleButton(
// Check if feature is supported
if (requiresFeature && !requiresFeature.isSupported) {
button.disabled = true;
button.setAttribute('title', `${buttonText} ${WIDGET_CONFIG.lang.notSupportedBrowser}`);
button.setAttribute('title', `${buttonText} ${getTranslation('notSupportedBrowser')}`);
button.style.opacity = '0.5';
return button;
}
@@ -1148,7 +1554,7 @@ function handleFontSelection() {
if (nextIndex === fonts.length) {
// Default font
localStorage.removeItem('fontSelection');
return WIDGET_CONFIG.lang.defaultFont;
return getTranslation('defaultFont');
} else {
const selectedFont = fonts[nextIndex];
localStorage.setItem('fontSelection', selectedFont);
@@ -1197,7 +1603,7 @@ function handleColorFilter() {
if (nextIndex === filters.length) {
// No filter
localStorage.removeItem('colorFilter');
return WIDGET_CONFIG.lang.noFilter;
return getTranslation('noFilter');
} else {
const selectedFilter = filters[nextIndex];
localStorage.setItem('colorFilter', selectedFilter);
@@ -1220,7 +1626,7 @@ function handleTextAlign() {
if (nextIndex === alignments.length) {
// Default alignment
localStorage.removeItem('textAlign');
return WIDGET_CONFIG.lang.default;
return getTranslation('default');
} else {
const selectedAlign = alignments[nextIndex];
localStorage.setItem('textAlign', selectedAlign);
@@ -1243,7 +1649,7 @@ function handleBiggerText() {
if (nextIndex === textSizes.length) {
// Default text size
localStorage.removeItem('biggerText');
return WIDGET_CONFIG.lang.default;
return getTranslation('default');
} else {
const selectedSize = textSizes[nextIndex];
localStorage.setItem('biggerText', selectedSize);
@@ -1266,7 +1672,7 @@ function handleHighContrast() {
if (nextIndex === contrastLevels.length) {
// Default contrast
localStorage.removeItem('highContrast');
return WIDGET_CONFIG.lang.default;
return getTranslation('default');
} else {
const selectedContrast = contrastLevels[nextIndex];
localStorage.setItem('highContrast', selectedContrast);
@@ -1289,7 +1695,7 @@ function handleTextSpacing() {
if (nextIndex === spacings.length) {
// Default
localStorage.removeItem('textSpacing');
return WIDGET_CONFIG.lang.default;
return getTranslation('default');
} else {
const selectedSpacing = spacings[nextIndex];
localStorage.setItem('textSpacing', selectedSpacing);
@@ -1312,8 +1718,8 @@ function handleLineHeight() {
if (nextIndex === heights.length) {
// Default
localStorage.removeItem('lineHeight');
return WIDGET_CONFIG.lang.default;
} else {
return getTranslation('default');
} else{
const selectedHeight = heights[nextIndex];
localStorage.setItem('lineHeight', selectedHeight);
domCache.body.classList.add(`snn-line-height-${selectedHeight}`);
@@ -1349,7 +1755,7 @@ const screenReader = {
},
toggle: function (isActive) {
if (!screenReader.isSupported) {
console.warn(`Speech synthesis ${WIDGET_CONFIG.lang.notSupportedBrowser}`);
console.warn(`Speech synthesis ${getTranslation('notSupportedBrowser')}`);
return false;
}
@@ -1359,7 +1765,7 @@ const screenReader = {
try {
if (isActive) {
document.addEventListener('focusin', screenReader.handleFocus);
const feedbackSpeech = new SpeechSynthesisUtterance(WIDGET_CONFIG.lang.screenReaderOn);
const feedbackSpeech = new SpeechSynthesisUtterance(getTranslation('screenReaderOn'));
feedbackSpeech.lang = 'en-US';
feedbackSpeech.onerror = function (event) {
console.warn('Speech synthesis feedback error:', event.error);
@@ -1368,7 +1774,7 @@ const screenReader = {
} else {
document.removeEventListener('focusin', screenReader.handleFocus);
window.speechSynthesis.cancel();
const feedbackSpeech = new SpeechSynthesisUtterance(WIDGET_CONFIG.lang.screenReaderOff);
const feedbackSpeech = new SpeechSynthesisUtterance(getTranslation('screenReaderOff'));
feedbackSpeech.lang = 'en-US';
feedbackSpeech.onerror = function (event) {
console.warn('Speech synthesis feedback error:', event.error);
@@ -1393,7 +1799,7 @@ const voiceControl = {
maxRetries: 3,
toggle: function (isActive) {
if (!voiceControl.isSupported) {
console.warn(`Speech Recognition API ${WIDGET_CONFIG.lang.notSupportedBrowser}`);
console.warn(`Speech Recognition API ${getTranslation('notSupportedBrowser')}`);
return false;
}
@@ -1430,7 +1836,7 @@ const voiceControl = {
voiceControl.recognition.continuous = false;
voiceControl.recognition.onstart = function () {
console.log(WIDGET_CONFIG.lang.voiceControlActivated);
console.log(getTranslation('voiceControlActivated'));
voiceControl.retryCount = 0;
};
@@ -1552,20 +1958,20 @@ function createAccessibilityMenu() {
const title = document.createElement('div');
title.classList.add('snn-title');
title.id = 'snn-accessibility-title';
title.textContent = WIDGET_CONFIG.lang.accessibilityTools;
title.textContent = getTranslation('accessibilityTools');
// Create reset button
const resetButton = document.createElement('button');
resetButton.classList.add('snn-reset-button');
resetButton.innerHTML = `${icons.resetAll}<span class="snn-tooltip">${WIDGET_CONFIG.lang.reset}</span>`;
resetButton.setAttribute('aria-label', WIDGET_CONFIG.lang.resetAllSettings);
resetButton.innerHTML = `${icons.resetAll}<span class="snn-tooltip">${getTranslation('reset')}</span>`;
resetButton.setAttribute('aria-label', getTranslation('resetAllSettings'));
resetButton.addEventListener('click', resetAccessibilitySettings);
// Create close button
const closeButton = document.createElement('button');
closeButton.className = 'snn-close';
closeButton.innerHTML = `<span class="snn-tooltip">${WIDGET_CONFIG.lang.close}</span>`;
closeButton.setAttribute('aria-label', WIDGET_CONFIG.lang.closeAccessibilityMenu);
closeButton.innerHTML = `<span class="snn-tooltip">${getTranslation('close')}</span>`;
closeButton.setAttribute('aria-label', getTranslation('closeAccessibilityMenu'));
closeButton.addEventListener('click', function () {
closeMenu();
@@ -1587,6 +1993,45 @@ function createAccessibilityMenu() {
const content = document.createElement('div');
content.classList.add('snn-content');
// Create language selector dropdown
const languageSelector = document.createElement('select');
languageSelector.classList.add('snn-language-selector');
languageSelector.setAttribute('aria-label', getTranslation('selectLanguage'));
const languages = [
{ code: 'en', name: 'English' },
{ code: 'de', name: 'Deutsch' },
{ code: 'es', name: 'Español' },
{ code: 'it', name: 'Italiano' },
{ code: 'fr', name: 'Français' },
{ code: 'ru', name: 'Русский' },
{ code: 'tr', name: 'Türkçe' },
{ code: 'ar', name: 'العربية' },
{ code: 'hi', name: 'हिन्दी' },
{ code: 'zh-cn', name: '简体中文' },
{ code: 'jp', name: '日本語' }
];
languages.forEach(lang => {
const option = document.createElement('option');
option.value = lang.code;
option.textContent = lang.name;
if (lang.code === currentLanguage) {
option.selected = true;
}
languageSelector.appendChild(option);
});
languageSelector.addEventListener('change', function(e) {
const newLang = e.target.value;
if (setLanguage(newLang)) {
// Recreate the menu with new language
updateMenuLanguage();
}
});
content.appendChild(languageSelector);
// Create grid wrapper for accessibility options
const optionsGrid = document.createElement('div');
optionsGrid.classList.add('snn-options-grid');
@@ -1601,7 +2046,7 @@ function createAccessibilityMenu() {
{
order: 1,
type: 'action',
text: WIDGET_CONFIG.lang.textSize,
text: getTranslation('textSize'),
actionFunction: handleBiggerText,
icon: icons.biggerText,
enabled: WIDGET_CONFIG.enableBiggerText,
@@ -1610,7 +2055,7 @@ function createAccessibilityMenu() {
{
order: 2,
type: 'action',
text: WIDGET_CONFIG.lang.highContrast,
text: getTranslation('highContrast'),
actionFunction: handleHighContrast,
icon: icons.highContrast,
enabled: WIDGET_CONFIG.enableHighContrast,
@@ -1619,7 +2064,7 @@ function createAccessibilityMenu() {
{
order: 3,
type: 'action',
text: WIDGET_CONFIG.lang.textAlign,
text: getTranslation('textAlign'),
actionFunction: handleTextAlign,
icon: icons.textAlign,
enabled: WIDGET_CONFIG.enableTextAlign,
@@ -1628,7 +2073,7 @@ function createAccessibilityMenu() {
{
order: 4,
type: 'action',
text: WIDGET_CONFIG.lang.colorFilter,
text: getTranslation('colorFilter'),
actionFunction: handleColorFilter,
icon: icons.colorFilter,
enabled: WIDGET_CONFIG.enableColorFilter,
@@ -1639,7 +2084,7 @@ function createAccessibilityMenu() {
{
order: 5,
type: 'action', // Changed from toggle to action
text: WIDGET_CONFIG.lang.textSpacing,
text: getTranslation('textSpacing'),
actionFunction: handleTextSpacing,
icon: icons.textSpacing,
enabled: WIDGET_CONFIG.enableTextSpacing,
@@ -1648,7 +2093,7 @@ function createAccessibilityMenu() {
{
order: 6,
type: 'action', // Changed from toggle to action
text: WIDGET_CONFIG.lang.lineHeight,
text: getTranslation('lineHeight'),
actionFunction: handleLineHeight,
icon: icons.lineHeight,
enabled: WIDGET_CONFIG.enableLineHeight,
@@ -1657,7 +2102,7 @@ function createAccessibilityMenu() {
{
order: 7,
type: 'action',
text: WIDGET_CONFIG.lang.fontSelection,
text: getTranslation('fontSelection'),
actionFunction: handleFontSelection,
icon: icons.fontSelection,
enabled: WIDGET_CONFIG.enableFontSelection,
@@ -1666,7 +2111,7 @@ function createAccessibilityMenu() {
{
order: 7.5,
type: 'action',
text: 'Saturation',
text: getTranslation('saturation'),
actionFunction: handleSaturation,
icon: icons.saturation,
enabled: true,
@@ -1675,7 +2120,7 @@ function createAccessibilityMenu() {
{
order: 8,
type: 'toggle',
text: WIDGET_CONFIG.lang.dyslexiaFriendly,
text: getTranslation('dyslexiaFriendly'),
key: 'dyslexiaFont',
className: 'snn-dyslexia-font',
icon: icons.dyslexiaFont,
@@ -1684,7 +2129,7 @@ function createAccessibilityMenu() {
{
order: 9,
type: 'toggle',
text: WIDGET_CONFIG.lang.biggerCursor,
text: getTranslation('biggerCursor'),
key: 'biggerCursor',
className: 'snn-bigger-cursor',
icon: icons.biggerCursor,
@@ -1693,7 +2138,7 @@ function createAccessibilityMenu() {
{
order: 10,
type: 'toggle',
text: WIDGET_CONFIG.lang.hideImages,
text: getTranslation('hideImages'),
key: 'hideImages',
icon: icons.hideImages,
customToggleFunction: toggleHideImages,
@@ -1704,7 +2149,7 @@ function createAccessibilityMenu() {
{
order: 11,
type: 'toggle',
text: WIDGET_CONFIG.lang.pauseAnimations,
text: getTranslation('pauseAnimations'),
key: 'pauseAnimations',
className: 'snn-pause-animations',
icon: icons.pauseAnimations,
@@ -1715,7 +2160,7 @@ function createAccessibilityMenu() {
{
order: 98,
type: 'toggle',
text: WIDGET_CONFIG.lang.screenReader,
text: getTranslation('screenReader'),
key: 'screenReader',
customToggleFunction: screenReader.toggle,
icon: icons.screenReader,
@@ -1725,7 +2170,7 @@ function createAccessibilityMenu() {
{
order: 99,
type: 'toggle',
text: WIDGET_CONFIG.lang.voiceCommand,
text: getTranslation('voiceCommand'),
key: 'voiceControl',
customToggleFunction: voiceControl.toggle,
icon: icons.voiceControl,
@@ -1769,6 +2214,37 @@ function createAccessibilityMenu() {
shadowRoot.appendChild(menu);
}
// Update menu language without recreating everything
function updateMenuLanguage() {
const menu = shadowRoot.getElementById('snn-accessibility-menu');
if (!menu) return;
const wasOpen = menu.style.display === 'block';
// Remove old menu
menu.remove();
// Clear cache
menuCache.menu = null;
menuCache.closeButton = null;
keyboardCache.focusableElements = null;
// Recreate menu
createAccessibilityMenu();
// Update button aria-label
const mainButton = shadowRoot.getElementById('snn-accessibility-button');
if (mainButton) {
mainButton.setAttribute('aria-label', getTranslation('accessibilityMenu'));
}
// Reopen if it was open
if (wasOpen) {
menuCache.init();
openMenu();
}
}
// ===========================================
// MENU MANAGEMENT
// ===========================================