**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>
NebuleAir i18n System
Lightweight internationalization (i18n) system for NebuleAir web interface.
Features
- Offline-first: Works completely offline with local JSON translation files
- Database-backed: Language preference stored in SQLite
config_table - Automatic: Translations apply on page load and when language changes
- Simple API: Easy-to-use data attributes and JavaScript API
Quick Start
1. Include i18n.js in your HTML page
<script src="assets/js/i18n.js"></script>
The i18n system will automatically initialize when the page loads.
2. Add translation keys to HTML elements
Use the data-i18n attribute to mark elements for translation:
<h1 data-i18n="page.title">Titre en français</h1>
<p data-i18n="page.description">Description en français</p>
<button data-i18n="common.submit">Soumettre</button>
The text content serves as a fallback if translations aren't loaded.
3. Add translations to JSON files
Edit lang/fr.json and lang/en.json:
{
"page": {
"title": "Mon Titre",
"description": "Ma description"
},
"common": {
"submit": "Soumettre"
}
}
Translation keys use dot notation for nested objects.
Translation Files
fr.json: French translations (default)en.json: English translations
File Structure Example
{
"common": {
"save": "Enregistrer",
"cancel": "Annuler",
"delete": "Supprimer"
},
"navigation": {
"home": "Accueil",
"settings": "Paramètres"
},
"sensors": {
"title": "Capteurs",
"description": "Liste des capteurs"
}
}
JavaScript API
Get Current Language
const currentLang = i18n.currentLang; // 'fr' or 'en'
Change Language Programmatically
await i18n.setLanguage('en'); // Switch to English
Get Translation in JavaScript
const translation = i18n.get('sensors.title'); // Returns translated string
Manual Translation Application
If you dynamically create HTML elements, call applyTranslations() after adding them to the DOM:
// Create new element
const div = document.createElement('div');
div.setAttribute('data-i18n', 'mypage.newElement');
div.textContent = 'Fallback text';
document.body.appendChild(div);
// Apply translations
i18n.applyTranslations();
Listen for Language Changes
document.addEventListener('languageChanged', (event) => {
console.log('Language changed to:', event.detail.language);
// Reload dynamic content, update charts, etc.
});
Special Cases
Input Placeholders
For input fields, the translation applies to the placeholder attribute:
<input type="text" data-i18n="form.emailPlaceholder" placeholder="Email...">
Button Values
For input buttons, the translation applies to the value attribute:
<input type="submit" data-i18n="common.submit" value="Submit">
Dynamic Content
For content created with JavaScript (like sensor cards), add data-i18n attributes to your template strings and call i18n.applyTranslations() after inserting into the DOM.
Example: Migrating an Existing Page
Before (French only):
<!DOCTYPE html>
<html>
<head>
<title>Capteurs</title>
</head>
<body>
<h1>Liste des capteurs</h1>
<button onclick="getData()">Obtenir les données</button>
</body>
</html>
After (Multilingual):
<!DOCTYPE html>
<html>
<head>
<title data-i18n="sensors.pageTitle">Capteurs</title>
<script src="assets/js/i18n.js"></script>
</head>
<body>
<h1 data-i18n="sensors.title">Liste des capteurs</h1>
<button onclick="getData()" data-i18n="common.getData">Obtenir les données</button>
</body>
</html>
Add to lang/fr.json:
{
"sensors": {
"pageTitle": "Capteurs",
"title": "Liste des capteurs"
},
"common": {
"getData": "Obtenir les données"
}
}
Add to lang/en.json:
{
"sensors": {
"pageTitle": "Sensors",
"title": "Sensor List"
},
"common": {
"getData": "Get Data"
}
}
Backend Integration
Get Language Preference
const response = await fetch('launcher.php?type=get_language');
const data = await response.json();
console.log(data.language); // 'fr' or 'en'
Set Language Preference
const response = await fetch('launcher.php?type=set_language&language=en');
const data = await response.json();
console.log(data.success); // true
Language preference is stored in SQLite config_table with key language.
Completed Pages
- ✅ sensors.html - Fully translated with French/English support
TODO: Pages to Migrate
- ⏳ index.html
- ⏳ admin.html
- ⏳ wifi.html
- ⏳ saraR4.html
- ⏳ map.html
Tips
- Reuse common translations: Put frequently used strings (buttons, actions, status messages) in the
commonsection - Keep keys descriptive: Use
sensors.bme280.titleinstead ofs1for maintainability - Test both languages: Always verify that both French and English translations display correctly
- Fallback text: Always provide fallback text in HTML for graceful degradation
Support
For issues or questions about the i18n system, refer to the implementation in:
/html/assets/js/i18n.js- Core translation library/html/lang/fr.json- French translations/html/lang/en.json- English translations/html/sensors.html- Example implementation