/** * Language Selector Component for OxiCloud * Custom styled dropdown with flags */ // Language codes, names, and flag emojis const languages = [ { code: 'en', name: 'English', flag: '🇬🇧' }, { code: 'es', name: 'Español', flag: '🇪🇸' }, { code: 'zh', name: '中文', flag: '🇨🇳' }, { code: 'fa', name: 'فارسی', flag: '🦁' } ]; /** * 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 currentLocale = window.i18n ? window.i18n.getCurrentLocale() : 'en'; const currentLang = languages.find(l => l.code === currentLocale) || languages[0]; // 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); } updateSelectedLanguage(langCode, container); closeDropdown(container); } /** * Update the UI to reflect selected language */ function updateSelectedLanguage(langCode, container) { 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(); });