**Core System:** - Add i18n.js translation library with data-attribute support - Create translation files (fr.json, en.json) with offline support - Store language preference in SQLite config_table - Add backend endpoints for get/set language **UI Features:** - Add language switcher dropdown to topbar (🇫🇷 FR / 🇬🇧 EN) - Auto-sync language selection across all pages - Support for static HTML and dynamically created elements **Implementation:** - Migrate sensors.html as working example - Add data-i18n attributes to all UI elements - Support for buttons, inputs, and dynamic content - Comprehensive README documentation in html/lang/ **Technical Details:** - Works completely offline (local JSON files) - No external dependencies - Database-backed user preference - Event-based language change notifications - Automatic translation on page load Next steps: Gradually migrate other pages (admin, wifi, index, etc.) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
130 lines
4.1 KiB
JavaScript
130 lines
4.1 KiB
JavaScript
/**
|
|
* 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();
|
|
}
|