Implement lightweight offline i18n system with French/English support
**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>
This commit is contained in:
@@ -49,11 +49,11 @@
|
||||
</aside>
|
||||
<!-- Main content -->
|
||||
<main class="col-md-10 ms-sm-auto col-lg-11 offset-md-2 offset-lg-1 px-md-4">
|
||||
<h1 class="mt-4">Les sondes de mesure</h1>
|
||||
<p>Votre capteur NebuleAir est équipé de une ou plusieurs sondes qui permettent de mesurer certaines variables environnementales. La mesure
|
||||
<h1 class="mt-4" data-i18n="sensors.title">Les sondes de mesure</h1>
|
||||
<p data-i18n="sensors.description">Votre capteur NebuleAir est équipé de une ou plusieurs sondes qui permettent de mesurer certaines variables environnementales. La mesure
|
||||
est automatique mais vous pouvez ici vous assurer de leur bon fonctionnement.
|
||||
</p>
|
||||
<div class="row mb-3" id="card-container"></div>
|
||||
<div class="row mb-3" id="card-container"></div>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
@@ -64,6 +64,8 @@
|
||||
<script src="assets/jquery/jquery-3.7.1.min.js"></script>
|
||||
<!-- Link Bootstrap JS and Popper.js locally -->
|
||||
<script src="assets/js/bootstrap.bundle.js"></script>
|
||||
<!-- i18n translation system -->
|
||||
<script src="assets/js/i18n.js"></script>
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
@@ -313,13 +315,13 @@ error: function(xhr, status, error) {
|
||||
const cardHTML = `
|
||||
<div class="col-sm-3">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<div class="card-header" data-i18n="sensors.npm.headerUart">
|
||||
Port UART
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">NextPM</h5>
|
||||
<p class="card-text">Capteur particules fines.</p>
|
||||
<button class="btn btn-primary" onclick="getNPM_values('ttyAMA5')">Get Data</button>
|
||||
<h5 class="card-title" data-i18n="sensors.npm.title">NextPM</h5>
|
||||
<p class="card-text" data-i18n="sensors.npm.description">Capteur particules fines.</p>
|
||||
<button class="btn btn-primary" onclick="getNPM_values('ttyAMA5')" data-i18n="common.getData">Get Data</button>
|
||||
<br>
|
||||
<div id="loading_ttyAMA5" class="spinner-border spinner-border-sm" style="display: none;" role="status"></div>
|
||||
<table class="table table-striped-columns">
|
||||
@@ -337,13 +339,13 @@ error: function(xhr, status, error) {
|
||||
const i2C_BME_HTML = `
|
||||
<div class="col-sm-3">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<div class="card-header" data-i18n="sensors.bme280.headerI2c">
|
||||
Port I2C
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">BME280 Temp/Hum sensor</h5>
|
||||
<p class="card-text">Capteur température et humidité sur le port I2C.</p>
|
||||
<button class="btn btn-primary mb-1" onclick="getBME280_values()">Get Data</button>
|
||||
<h5 class="card-title" data-i18n="sensors.bme280.title">BME280 Temp/Hum sensor</h5>
|
||||
<p class="card-text" data-i18n="sensors.bme280.description">Capteur température et humidité sur le port I2C.</p>
|
||||
<button class="btn btn-primary mb-1" onclick="getBME280_values()" data-i18n="common.getData">Get Data</button>
|
||||
<br>
|
||||
<div id="loading_BME280" class="spinner-border spinner-border-sm" style="display: none;" role="status"></div>
|
||||
<table class="table table-striped-columns">
|
||||
@@ -352,7 +354,7 @@ error: function(xhr, status, error) {
|
||||
</div>
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
|
||||
container.innerHTML += i2C_BME_HTML; // Add the I2C card if condition is met
|
||||
}
|
||||
|
||||
@@ -361,16 +363,16 @@ error: function(xhr, status, error) {
|
||||
const i2C_HTML = `
|
||||
<div class="col-sm-3">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<div class="card-header" data-i18n="sensors.noise.headerI2c">
|
||||
Port I2C
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">Decibel Meter</h5>
|
||||
<p class="card-text">Capteur bruit sur le port I2C.</p>
|
||||
<button class="btn btn-primary mb-1" onclick="getNoise_values()">Get Data</button>
|
||||
<h5 class="card-title" data-i18n="sensors.noise.title">Decibel Meter</h5>
|
||||
<p class="card-text" data-i18n="sensors.noise.description">Capteur bruit sur le port I2C.</p>
|
||||
<button class="btn btn-primary mb-1" onclick="getNoise_values()" data-i18n="common.getData">Get Data</button>
|
||||
<br>
|
||||
<button class="btn btn-success" onclick="startNoise()">Start recording</button>
|
||||
<button class="btn btn-danger" onclick="stopNoise()">Stop recording</button>
|
||||
<button class="btn btn-success" onclick="startNoise()" data-i18n="common.startRecording">Start recording</button>
|
||||
<button class="btn btn-danger" onclick="stopNoise()" data-i18n="common.stopRecording">Stop recording</button>
|
||||
<div id="loading_noise" class="spinner-border spinner-border-sm" style="display: none;" role="status"></div>
|
||||
<table class="table table-striped-columns">
|
||||
<tbody id="data-table-body_noise"></tbody>
|
||||
@@ -378,7 +380,7 @@ error: function(xhr, status, error) {
|
||||
</div>
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
|
||||
container.innerHTML += i2C_HTML; // Add the I2C card if condition is met
|
||||
}
|
||||
|
||||
@@ -409,8 +411,8 @@ error: function(xhr, status, error) {
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">Sonde Envea ${name}</h5>
|
||||
<p class="card-text">Capteur gas.</p>
|
||||
<button class="btn btn-primary" onclick="getENVEA_values('${port}','${name}')">Get Data</button>
|
||||
<p class="card-text" data-i18n="sensors.envea.description">Capteur gas.</p>
|
||||
<button class="btn btn-primary" onclick="getENVEA_values('${port}','${name}')" data-i18n="common.getData">Get Data</button>
|
||||
<div id="loading_envea${name}" class="spinner-border spinner-border-sm" style="display: none;" role="status"></div>
|
||||
<table class="table table-striped-columns">
|
||||
<tbody id="data-table-body_envea${name}"></tbody>
|
||||
@@ -420,6 +422,9 @@ error: function(xhr, status, error) {
|
||||
</div>`;
|
||||
container.innerHTML += cardHTML; // Ajouter la carte au conteneur
|
||||
});
|
||||
|
||||
// Apply translations to dynamically created Envea cards
|
||||
i18n.applyTranslations();
|
||||
|
||||
|
||||
|
||||
@@ -432,6 +437,9 @@ error: function(xhr, status, error) {
|
||||
|
||||
}//end if envea
|
||||
|
||||
// Apply translations to all dynamically created sensor cards
|
||||
i18n.applyTranslations();
|
||||
|
||||
} // end createSensorCards function
|
||||
|
||||
//get local RTC
|
||||
|
||||
Reference in New Issue
Block a user