feat: i18n system - French/English internationalization
CI / lint (push) Failing after 20s
CI / test (push) Has been skipped
CI / build (push) Has been skipped
CI / e2e (push) Has been skipped
CI / security (push) Failing after 9s

Core: i18n.js module with t(), setLocale(), initI18n()
- Flat key→string JSON locale files (fr.json + en.json, 165 keys)
- data-i18n / data-i18n-placeholder / data-i18n-attr DOM attributes
- Live locale switch via setLocale() - no page reload
- Language detection: localStorage > navigator.language > 'fr'
- Fallback: missing key → French → raw key

Wired into:
- app.js: await initI18n() early in init()
- config.js: profile save → await setLocale(), toasts + confirm via t()
- auth.js: network error toast via t()
- index.html: data-i18n on login form, profile section, search placeholder
This commit is contained in:
2026-06-18 13:31:11 -04:00
parent daab917ed1
commit f85482214e
7 changed files with 718 additions and 24 deletions
+10 -8
View File
@@ -5,6 +5,7 @@ import { el, icon, openFile } from './viewer.js';
import { syncVaultSelectors, setSelectedVaultContext, refreshSidebarForContext, loadVaults, loadVaultSettings, loadTags, TagFilterService, refreshSidebarTreePreservingState } from './sidebar.js';
import { escapeHtml, safeCreateIcons } from './utils.js';
import { showToast, closeHeaderMenu, closeMobileSidebar } from './ui.js';
import { t, setLocale, getLocale } from './i18n.js';
let _recentTimestampTimer = null;
let _recentFilesCache = [];
@@ -769,7 +770,7 @@ function saveFrontendConfig() {
search_timeout_ms: _getFieldNum("cfg-timeout", 30000),
};
localStorage.setItem(_FRONTEND_CONFIG_KEY, JSON.stringify(cfg));
showToast("Paramètres client sauvegardés", "success");
showToast(t("toast.config_client_saved"), "success");
}
async function saveBackendConfig() {
@@ -793,7 +794,7 @@ async function saveBackendConfig() {
body: JSON.stringify(body),
});
if (res.ok) {
showToast("Configuration backend sauvegardée", "success");
showToast(t("toast.config_saved"), "success");
} else {
const errorData = await res.json().catch(() => ({}));
showToast(errorData.detail || "Erreur de sauvegarde", "error");
@@ -812,7 +813,7 @@ async function forceReindex() {
}
try {
await api("/api/index/reload");
showToast("Réindexation terminée", "success");
showToast(t("toast.reindex_done"), "success");
loadDiagnostics();
await Promise.all([loadVaults(), loadTags()]);
} catch (err) {
@@ -853,7 +854,7 @@ async function resetConfigDefaults() {
console.error("Reset config error:", err);
}
loadConfigFields();
showToast("Configuration réinitialisée", "success");
showToast(t("toast.config_reset"), "success");
}
async function loadDiagnostics() {
@@ -1549,15 +1550,16 @@ function initProfile() {
// Load saved profile
try {
var lang = localStorage.getItem('obsigate-lang') || 'fr';
var lang = getLocale();
var name = localStorage.getItem('obsigate-name') || '';
if (langSel) langSel.value = lang;
if (nameInp) nameInp.value = name;
} catch(e) {}
saveBtn.addEventListener('click', function() {
saveBtn.addEventListener('click', async function() {
try {
localStorage.setItem('obsigate-lang', langSel.value);
var newLang = langSel.value;
await setLocale(newLang); // Live-reload all translations
if (nameInp) localStorage.setItem('obsigate-name', nameInp.value.trim());
if (savedEl) { savedEl.style.display = 'inline'; setTimeout(function() { savedEl.style.display = 'none'; }, 2000); }
} catch(e) {}
@@ -1566,7 +1568,7 @@ function initProfile() {
// Logout button
if (logoutBtn) {
logoutBtn.addEventListener('click', function() {
if (confirm('Voulez-vous vraiment vous deconnecter ?')) {
if (confirm(t('confirm.logout'))) {
fetch('/api/auth/logout', { method: 'POST' })
.then(function() { window.location.href = '/'; })
.catch(function() { window.location.href = '/'; });