/** * Language Selector Component for OxiCloud * Custom styled dropdown with flags */ // Language codes, names, and flag emojis // Uses ALL_LANGUAGES from auth.js if available, otherwise fallback function getAvailableLanguages() { if (typeof ALL_LANGUAGES !== 'undefined') { return ALL_LANGUAGES.map(l => ({ code: l.code, name: l.nativeName, flag: l.flag })); } return [ { code: 'en', name: 'English', flag: '🇬🇧' }, { code: 'es', name: 'Español', flag: '🇪🇸' }, { code: 'zh', name: '中文', flag: '🇨🇳' }, { code: 'fa', name: 'فارسی', flag: '🇮🇷' }, { code: 'fr', name: 'Français', flag: '🇫🇷' }, { code: 'de', name: 'Deutsch', flag: '🇩🇪' }, { code: 'pt', name: 'Português', flag: '🇧🇷' } ]; } // RTL languages const rtlLanguages = ['fa']; // ['fa', 'ar'] // Update HTML lang attribute and dir for RTL languages function updateHtmlAttributes(langCode) { const htmlElement = document.documentElement; // Set lang attribute htmlElement.setAttribute('lang', langCode); // Set dir attribute for RTL languages if (rtlLanguages.includes(langCode)) { htmlElement.setAttribute('dir', 'rtl'); } else { htmlElement.removeAttribute('dir'); } } /** * Creates and initializes a custom language selector component * @param {string} containerId - ID of the container element */ function createLanguageSelector(containerId = 'language-selector') { // Get or create container let container = document.getElementById(containerId); if (!container) { console.warn(`Container with ID "${containerId}" not found, creating one.`); container = document.createElement('div'); container.id = containerId; document.body.appendChild(container); } // Ensure container has the right class container.className = 'language-selector'; // Get current language const languages = getAvailableLanguages(); const currentLocale = window.i18n ? window.i18n.getCurrentLocale() : 'en'; const currentLang = languages.find(l => l.code === currentLocale) || languages[0]; // Set initial HTML attributes updateHtmlAttributes(currentLocale); // Create toggle button const toggle = document.createElement('div'); toggle.className = 'language-selector-toggle'; toggle.setAttribute('role', 'button'); toggle.setAttribute('aria-haspopup', 'listbox'); toggle.setAttribute('aria-expanded', 'false'); toggle.setAttribute('tabindex', '0'); toggle.innerHTML = ` ${currentLang.code.toUpperCase()} `; // Create dropdown menu const dropdown = document.createElement('div'); dropdown.className = 'language-selector-dropdown'; dropdown.setAttribute('role', 'listbox'); // Add language options languages.forEach(lang => { const option = document.createElement('div'); option.className = `language-option${lang.code === currentLocale ? ' active' : ''}`; option.setAttribute('role', 'option'); option.setAttribute('data-lang', lang.code); option.setAttribute('aria-selected', lang.code === currentLocale); option.innerHTML = ` ${lang.flag} ${lang.name} `; option.addEventListener('click', async (e) => { e.stopPropagation(); await selectLanguage(lang.code, container); }); dropdown.appendChild(option); }); // Clear and build container container.innerHTML = ''; container.appendChild(toggle); container.appendChild(dropdown); // Toggle dropdown on click toggle.addEventListener('click', (e) => { e.stopPropagation(); toggleDropdown(container); }); // Keyboard support toggle.addEventListener('keydown', (e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); toggleDropdown(container); } else if (e.key === 'Escape') { closeDropdown(container); } }); // Close dropdown when clicking outside document.addEventListener('click', (e) => { if (!container.contains(e.target)) { closeDropdown(container); } }); // Listen for locale changes from i18n system window.addEventListener('localeChanged', (e) => { updateSelectedLanguage(e.detail.locale, container); }); return container; } /** * Toggle dropdown open/closed */ function toggleDropdown(container) { const isOpen = container.classList.contains('open'); if (isOpen) { closeDropdown(container); } else { openDropdown(container); } } /** * Open dropdown */ function openDropdown(container) { container.classList.add('open'); const toggle = container.querySelector('.language-selector-toggle'); if (toggle) { toggle.setAttribute('aria-expanded', 'true'); } } /** * Close dropdown */ function closeDropdown(container) { container.classList.remove('open'); const toggle = container.querySelector('.language-selector-toggle'); if (toggle) { toggle.setAttribute('aria-expanded', 'false'); } } /** * Select a language */ async function selectLanguage(langCode, container) { // Update i18n if available if (window.i18n) { await window.i18n.setLocale(langCode); } // Update HTML lang attribute and dir for RTL languages updateHtmlAttributes(langCode); updateSelectedLanguage(langCode, container); closeDropdown(container); } /** * Update the UI to reflect selected language */ function updateSelectedLanguage(langCode, container) { const languages = getAvailableLanguages(); const lang = languages.find(l => l.code === langCode) || languages[0]; // Update toggle button text const langCodeSpan = container.querySelector('.lang-code'); if (langCodeSpan) { langCodeSpan.textContent = lang.code.toUpperCase(); } // Update active state on options const options = container.querySelectorAll('.language-option'); options.forEach(option => { const isActive = option.getAttribute('data-lang') === langCode; option.classList.toggle('active', isActive); option.setAttribute('aria-selected', isActive); }); } // Create language selector when DOM is ready document.addEventListener('DOMContentLoaded', () => { createLanguageSelector(); });