**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>
248 lines
5.4 KiB
Markdown
248 lines
5.4 KiB
Markdown
# 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
|
|
|
|
```html
|
|
<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:
|
|
|
|
```html
|
|
<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`:
|
|
|
|
```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
|
|
|
|
```json
|
|
{
|
|
"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
|
|
|
|
```javascript
|
|
const currentLang = i18n.currentLang; // 'fr' or 'en'
|
|
```
|
|
|
|
### Change Language Programmatically
|
|
|
|
```javascript
|
|
await i18n.setLanguage('en'); // Switch to English
|
|
```
|
|
|
|
### Get Translation in JavaScript
|
|
|
|
```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:
|
|
|
|
```javascript
|
|
// 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
|
|
|
|
```javascript
|
|
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:
|
|
|
|
```html
|
|
<input type="text" data-i18n="form.emailPlaceholder" placeholder="Email...">
|
|
```
|
|
|
|
### Button Values
|
|
|
|
For input buttons, the translation applies to the `value` attribute:
|
|
|
|
```html
|
|
<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):
|
|
|
|
```html
|
|
<!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):
|
|
|
|
```html
|
|
<!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`:**
|
|
```json
|
|
{
|
|
"sensors": {
|
|
"pageTitle": "Capteurs",
|
|
"title": "Liste des capteurs"
|
|
},
|
|
"common": {
|
|
"getData": "Obtenir les données"
|
|
}
|
|
}
|
|
```
|
|
|
|
**Add to `lang/en.json`:**
|
|
```json
|
|
{
|
|
"sensors": {
|
|
"pageTitle": "Sensors",
|
|
"title": "Sensor List"
|
|
},
|
|
"common": {
|
|
"getData": "Get Data"
|
|
}
|
|
}
|
|
```
|
|
|
|
## Backend Integration
|
|
|
|
### Get Language Preference
|
|
|
|
```javascript
|
|
const response = await fetch('launcher.php?type=get_language');
|
|
const data = await response.json();
|
|
console.log(data.language); // 'fr' or 'en'
|
|
```
|
|
|
|
### Set Language Preference
|
|
|
|
```javascript
|
|
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
|
|
|
|
1. **Reuse common translations**: Put frequently used strings (buttons, actions, status messages) in the `common` section
|
|
2. **Keep keys descriptive**: Use `sensors.bme280.title` instead of `s1` for maintainability
|
|
3. **Test both languages**: Always verify that both French and English translations display correctly
|
|
4. **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
|