/** * NebuleAir i18n - Lightweight internationalization system * Works offline with local JSON translation files * Stores language preference in SQLite database */ const i18n = { currentLang: 'fr', // Default language translations: {}, /** * Initialize i18n system * Loads language preference from server and applies translations */ async init() { try { // Load language preference from server (SQLite database) const response = await fetch('launcher.php?type=get_language'); const data = await response.json(); this.currentLang = data.language || 'fr'; } catch (error) { console.warn('Could not load language preference, using default (fr):', error); this.currentLang = 'fr'; } // Load translations and apply await this.loadTranslations(this.currentLang); this.applyTranslations(); }, /** * Load translation file for specified language * @param {string} lang - Language code (fr, en) */ async loadTranslations(lang) { try { const response = await fetch(`lang/${lang}.json`); this.translations = await response.json(); console.log(`Translations loaded for: ${lang}`); } catch (error) { console.error(`Failed to load translations for ${lang}:`, error); } }, /** * Apply translations to all elements with data-i18n attribute */ applyTranslations() { document.querySelectorAll('[data-i18n]').forEach(element => { const key = element.getAttribute('data-i18n'); const translation = this.get(key); if (translation) { // Handle different element types if (element.tagName === 'INPUT' || element.tagName === 'TEXTAREA') { if (element.type === 'button' || element.type === 'submit') { element.value = translation; } else { element.placeholder = translation; } } else { element.textContent = translation; } } else { console.warn(`Translation not found for key: ${key}`); } }); // Update HTML lang attribute document.documentElement.lang = this.currentLang; // Update language switcher dropdown const languageSwitcher = document.getElementById('languageSwitcher'); if (languageSwitcher) { languageSwitcher.value = this.currentLang; } }, /** * Get translation by key (supports nested keys with dot notation) * @param {string} key - Translation key (e.g., 'sensors.title') * @returns {string} Translated string or key if not found */ get(key) { const keys = key.split('.'); let value = this.translations; for (const k of keys) { if (value && typeof value === 'object' && k in value) { value = value[k]; } else { return key; // Return key if translation not found } } return value; }, /** * Change language and reload translations * @param {string} lang - Language code (fr, en) */ async setLanguage(lang) { if (lang === this.currentLang) return; this.currentLang = lang; // Save to server (SQLite database) try { await fetch(`launcher.php?type=set_language&language=${lang}`); } catch (error) { console.error('Failed to save language preference:', error); } // Reload translations and apply await this.loadTranslations(lang); this.applyTranslations(); // Emit custom event for other scripts to react to language change document.dispatchEvent(new CustomEvent('languageChanged', { detail: { language: lang } })); } }; // Auto-initialize when DOM is ready if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', () => i18n.init()); } else { i18n.init(); }