fix: i18n for dashboard widgets + shortcuts — no more hardcoded French
CI / lint (push) Failing after 23s
CI / test (push) Has been skipped
CI / build (push) Has been skipped
CI / e2e (push) Has been skipped
CI / security (push) Failing after 12s

Replaced all hardcoded French text in dashboard.js with t() calls:
- 'Chargement...' → t('common.loading')
- 'Erreur:' → t('dashboard.error_prefix')
- Size units ('o','Ko','Mo','Go') → t('dashboard.size_*')
- 'fichier(s)' → t('dashboard.count_files*')
- '0 fichiers' → t('dashboard.zero_files')
- 'Conflits de synchronisation' → t('dashboard.conflicts_title')
- 'Garder l\'original'/'Garder le conflit' → t('dashboard.keep_*')
- 'Ajouter/Retirer des bookmarks' → t('dashboard.bookmark_*')
- 'à l\'instant'/'il y a X min/h/j' → t('dashboard.*_ago', {n})
- 'Afficher'/'Masquer' → t('dashboard.show/hide')
- 'Erreur de chargement' → t('dashboard.load_error')

Added 11 shortcut i18n keys + 22 dashboard i18n keys to en.json/fr.json.

Verified: '4 files' count, English shortcuts, English empty states.
This commit is contained in:
2026-06-21 10:48:14 -04:00
parent 2de9f91671
commit 98a6dd8a2c
3 changed files with 87 additions and 19 deletions
+19 -19
View File
@@ -21,18 +21,18 @@ const DashboardStatsWidget = {
async load() {
const grid = document.getElementById("dashboard-stats-grid");
if (!grid) return;
grid.innerHTML = '<div class="dashboard-stats-loading">Chargement...</div>';
grid.innerHTML = `<div class="dashboard-stats-loading">${t('common.loading')}</div>`;
try {
const data = await api("/api/dashboard");
this.render(data);
} catch (err) {
grid.innerHTML = `<div class="dashboard-recent-empty">Erreur: ${escapeHtml(err.message)}</div>`;
grid.innerHTML = `<div class="dashboard-recent-empty">${t('dashboard.error_prefix')}${escapeHtml(err.message)}</div>`;
}
},
render(data) {
const grid = document.getElementById("dashboard-stats-grid");
if (!grid) return;
const fmtSize = (bytes) => bytes < 1024 ? `${bytes} o` : bytes < 1048576 ? `${(bytes/1024).toFixed(1)} Ko` : bytes < 1073741824 ? `${(bytes/1048576).toFixed(1)} Mo` : `${(bytes/1073741824).toFixed(1)} Go`;
const fmtSize = (bytes) => bytes < 1024 ? `${bytes}${t('dashboard.size_bytes')}` : bytes < 1048576 ? `${(bytes/1024).toFixed(1)}${t('dashboard.size_kb')}` : bytes < 1073741824 ? `${(bytes/1048576).toFixed(1)}${t('dashboard.size_mb')}` : `${(bytes/1073741824).toFixed(1)}${t('dashboard.size_gb')}`;
const items = [
{ icon: "files", label: t('dashboard.stats_files'), value: data.total_files.toLocaleString() },
{ icon: "tags", label: t('dashboard.stats_tags'), value: data.total_tags.toLocaleString() },
@@ -120,7 +120,7 @@ const DashboardConflictsWidget = {
<div class="dashboard-header">
<div class="dashboard-title-row">
<i data-lucide="alert-triangle" class="dashboard-icon" style="color:var(--accent-orange)"></i>
<h2>Conflits de synchronisation</h2>
<h2>${t('dashboard.conflicts_title')}</h2>
<span class="dashboard-badge" style="background:var(--accent-orange)">${conflicts.length}</span>
</div>
</div>
@@ -130,11 +130,11 @@ const DashboardConflictsWidget = {
<div class="conflict-info">
<span class="conflict-vault">${escapeHtml(c.vault)}</span>
<span class="conflict-name">${escapeHtml(c.conflict_path.split("/").pop())}</span>
<span class="conflict-date">Conflit du ${c.conflict_date.replace(/(\d{4})(\d{2})(\d{2})-(\d{2})(\d{2})/, "$3/$2/$1 $4:$5")}</span>
<span class="conflict-date">${t('dashboard.conflict_date')}${c.conflict_date.replace(/(\d{4})(\d{2})(\d{2})-(\d{2})(\d{2})/, "$3/$2/$1 $4:$5")}</span>
</div>
<div class="conflict-actions">
<button class="conflict-btn keep-local" data-vault="${escapeHtml(c.vault)}" data-conflict="${escapeHtml(c.conflict_path)}" data-original="${escapeHtml(c.original_path)}">Garder l'original</button>
<button class="conflict-btn keep-conflict" data-vault="${escapeHtml(c.vault)}" data-conflict="${escapeHtml(c.conflict_path)}" data-original="${escapeHtml(c.original_path)}">Garder le conflit</button>
<button class="conflict-btn keep-local" data-vault="${escapeHtml(c.vault)}" data-conflict="${escapeHtml(c.conflict_path)}" data-original="${escapeHtml(c.original_path)}">${t('dashboard.keep_original')}</button>
<button class="conflict-btn keep-conflict" data-vault="${escapeHtml(c.vault)}" data-conflict="${escapeHtml(c.conflict_path)}" data-original="${escapeHtml(c.original_path)}">${t('dashboard.keep_conflict')}</button>
</div>
</div>
`).join("")}
@@ -149,7 +149,7 @@ const DashboardConflictsWidget = {
await api("/api/conflicts/resolve", { method: "POST", body: JSON.stringify({ vault: d.vault, conflict_path: d.conflict, original_path: d.original, action }) });
showToast(t("dashboard.conflict_resolved"), "success");
this.load();
} catch (err) { showToast("Erreur: " + err.message, "error"); }
} catch (err) { showToast(t('dashboard.error_prefix') + err.message, "error"); }
}
};
@@ -234,7 +234,7 @@ const DashboardRecentWidget = {
}
if (empty) empty.classList.add("hidden");
if (count) count.textContent = `${this._cache.length} fichier${this._cache.length > 1 ? "s" : ""}`;
if (count) count.textContent = `${this._cache.length} ${this._cache.length > 1 ? t('dashboard.count_files_plural') : t('dashboard.count_files')}`;
if (!grid) return;
grid.innerHTML = "";
@@ -275,7 +275,7 @@ const DashboardRecentWidget = {
const bookmarkBtn = document.createElement("button");
bookmarkBtn.className = `dashboard-card-bookmark-btn ${file.bookmarked ? "active" : ""}`;
bookmarkBtn.title = file.bookmarked ? "Retirer des bookmarks" : "Ajouter aux bookmarks";
bookmarkBtn.title = file.bookmarked ? t('dashboard.bookmark_remove') : t('dashboard.bookmark_add');
bookmarkBtn.innerHTML = `<i data-lucide="${file.bookmarked ? "bookmark" : "bookmark-plus"}" style="width:14px;height:14px"></i>`;
bookmarkBtn.addEventListener("click", (e) => {
@@ -347,7 +347,7 @@ const DashboardRecentWidget = {
if (grid) grid.innerHTML = "";
if (loading) loading.classList.remove("active");
if (empty) empty.classList.remove("hidden");
if (count) count.textContent = "0 fichiers";
if (count) count.textContent = t('dashboard.zero_files');
safeCreateIcons();
},
@@ -356,17 +356,17 @@ const DashboardRecentWidget = {
const empty = document.getElementById("dashboard-recent-empty");
if (empty) {
const msg = empty.querySelector("span");
if (msg) msg.textContent = "Erreur de chargement";
if (msg) msg.textContent = t('dashboard.load_error');
}
},
_humanizeDelta(mtime) {
const delta = Date.now() / 1000 - mtime;
if (delta < 60) return "à l'instant";
if (delta < 3600) return `il y a ${Math.floor(delta / 60)} min`;
if (delta < 86400) return `il y a ${Math.floor(delta / 3600)} h`;
if (delta < 604800) return `il y a ${Math.floor(delta / 86400)} j`;
return new Date(mtime * 1000).toLocaleDateString("fr-FR", { day: "numeric", month: "short", year: "numeric" });
if (delta < 60) return t('dashboard.just_now');
if (delta < 3600) return t('dashboard.min_ago', { n: Math.floor(delta / 60) });
if (delta < 86400) return t('dashboard.hour_ago', { n: Math.floor(delta / 3600) });
if (delta < 604800) return t('dashboard.day_ago', { n: Math.floor(delta / 86400) });
return new Date(mtime * 1000).toLocaleDateString(getLocale() === 'fr' ? 'fr-FR' : 'en-US', { day: "numeric", month: "short", year: "numeric" });
},
populateVaultFilter() {
@@ -405,12 +405,12 @@ const DashboardRecentWidget = {
if (localStorage.getItem('obsigate-qh-collapsed') === '1') {
qhBody.classList.add('collapsed');
qhToggle.textContent = '+';
qhToggle.title = 'Afficher';
qhToggle.title = t('dashboard.show');
}
qhToggle.addEventListener('click', function() {
var collapsed = qhBody.classList.toggle('collapsed');
qhToggle.textContent = collapsed ? '+' : '\u2212';
qhToggle.title = collapsed ? 'Afficher' : 'Masquer';
qhToggle.title = collapsed ? t('dashboard.show') : t('dashboard.hide');
localStorage.setItem('obsigate-qh-collapsed', collapsed ? '1' : '0');
});
}
+34
View File
@@ -447,12 +447,29 @@
"conflict.resolve_conflict": "Keep conflict version",
"conflict.resolve_local": "Keep local version",
"conflict.resolved": "Conflict resolved",
"dashboard.all_vaults": "All vaults",
"dashboard.bookmark_add": "Add to bookmarks",
"dashboard.bookmark_remove": "Remove from bookmarks",
"dashboard.bookmarks": "Bookmarks",
"dashboard.conflict_date": "Conflict of ",
"dashboard.conflict_resolved": "Conflict resolved",
"dashboard.conflicts": "Conflicts",
"dashboard.conflicts_title": "Sync conflicts",
"dashboard.copy_btn": "Copy",
"dashboard.count_files": "file",
"dashboard.count_files_plural": "files",
"dashboard.ctrl_hover_tip": "Hover over a document with Ctrl for a quick preview. Click Forge in the toolbar for the advanced editor.",
"dashboard.day_ago": "{n} d ago",
"dashboard.error_prefix": "Error: ",
"dashboard.expires": "Expires {date}",
"dashboard.hide": "Hide",
"dashboard.hour_ago": "{n} h ago",
"dashboard.just_now": "just now",
"dashboard.keep_conflict": "Keep conflict",
"dashboard.keep_original": "Keep original",
"dashboard.load_error": "Loading error",
"dashboard.loading": "Loading...",
"dashboard.min_ago": "{n} min ago",
"dashboard.no_bookmarks": "No bookmarks",
"dashboard.no_conflicts": "No conflicts",
"dashboard.no_recent": "No recent files",
@@ -465,6 +482,11 @@
"dashboard.recent_hint": "Open a file to see it appear here",
"dashboard.shared_hint": "Share a document to see it appear here",
"dashboard.shares": "Shares",
"dashboard.show": "Show",
"dashboard.size_bytes": " B",
"dashboard.size_gb": " GB",
"dashboard.size_kb": " KB",
"dashboard.size_mb": " MB",
"dashboard.stats": "Statistics",
"dashboard.stats_files": "Files",
"dashboard.stats_size": "Total size",
@@ -479,6 +501,7 @@
"dashboard.vaults_count": "Vaults",
"dashboard.views": "{count} view(s)",
"dashboard.welcome": "Welcome to ObsiGate",
"dashboard.zero_files": "0 files",
"dragdrop.cross_vault_unsupported": "Cross-vault moves are not supported",
"dragdrop.move_failed": "Move failed",
"editor.cancel": "Cancel",
@@ -1213,6 +1236,17 @@
"share.never": "Never",
"share.revoke": "Revoke",
"share.title": "Public share",
"shortcut.ai_completion": "AI inline completion (in editor)",
"shortcut.close_editor": "Close editor / modal",
"shortcut.command_palette": "Command palette",
"shortcut.editor_help": "Forge editor help",
"shortcut.file_palette": "File palette (quick nav)",
"shortcut.focus_search": "Focus search bar",
"shortcut.quick_insert": "Quick insert (Forge editor)",
"shortcut.quick_search": "Quick search (outside text field)",
"shortcut.save_file": "Save open file",
"shortcut.search_in_doc": "Search in document",
"shortcut.tip_preview": "Hover a document with **Ctrl** for a quick preview. Click **Forge** in the toolbar for the advanced editor.",
"sidebar.all_vaults": "All vaults",
"sidebar.case_sensitive": "Case sensitive",
"sidebar.clear_filter": "Clear",
+34
View File
@@ -447,12 +447,29 @@
"conflict.resolve_conflict": "Garder version conflit",
"conflict.resolve_local": "Garder version locale",
"conflict.resolved": "Conflit résolu",
"dashboard.all_vaults": "Tous les vaults",
"dashboard.bookmark_add": "Ajouter aux bookmarks",
"dashboard.bookmark_remove": "Retirer des bookmarks",
"dashboard.bookmarks": "Favoris",
"dashboard.conflict_date": "Conflit du ",
"dashboard.conflict_resolved": "Conflit résolu",
"dashboard.conflicts": "Conflits",
"dashboard.conflicts_title": "Conflits de synchronisation",
"dashboard.copy_btn": "Copier",
"dashboard.count_files": "fichier",
"dashboard.count_files_plural": "fichiers",
"dashboard.ctrl_hover_tip": "Survolez un document avec Ctrl pour un aperçu rapide. Cliquez sur Forge dans la barre d'outils pour l'éditeur avancé.",
"dashboard.day_ago": "il y a {n} j",
"dashboard.error_prefix": "Erreur: ",
"dashboard.expires": "Expire le {date}",
"dashboard.hide": "Masquer",
"dashboard.hour_ago": "il y a {n} h",
"dashboard.just_now": "à l'instant",
"dashboard.keep_conflict": "Garder le conflit",
"dashboard.keep_original": "Garder l'original",
"dashboard.load_error": "Erreur de chargement",
"dashboard.loading": "Chargement...",
"dashboard.min_ago": "il y a {n} min",
"dashboard.no_bookmarks": "Aucun favori",
"dashboard.no_conflicts": "Aucun conflit",
"dashboard.no_recent": "Aucun fichier récent",
@@ -465,6 +482,11 @@
"dashboard.recent_hint": "Ouvrez un fichier pour le voir apparaître ici",
"dashboard.shared_hint": "Partagez un document pour le voir apparaître ici",
"dashboard.shares": "Partages",
"dashboard.show": "Afficher",
"dashboard.size_bytes": " o",
"dashboard.size_gb": " Go",
"dashboard.size_kb": " Ko",
"dashboard.size_mb": " Mo",
"dashboard.stats": "Statistiques",
"dashboard.stats_files": "Fichiers",
"dashboard.stats_size": "Taille totale",
@@ -479,6 +501,7 @@
"dashboard.vaults_count": "Vaults",
"dashboard.views": "{count} vue(s)",
"dashboard.welcome": "Bienvenue dans ObsiGate",
"dashboard.zero_files": "0 fichiers",
"dragdrop.cross_vault_unsupported": "Cross-vault moves are not supported",
"dragdrop.move_failed": "Move failed",
"editor.cancel": "Annuler",
@@ -1213,6 +1236,17 @@
"share.never": "Jamais",
"share.revoke": "Révoquer",
"share.title": "Partage public",
"shortcut.ai_completion": "Completion IA inline (dans l'éditeur)",
"shortcut.close_editor": "Fermer l'éditeur / modale",
"shortcut.command_palette": "Palette de commandes",
"shortcut.editor_help": "Aide de l'éditeur Forge",
"shortcut.file_palette": "Palette de fichiers (nav. rapide)",
"shortcut.focus_search": "Focus barre de recherche",
"shortcut.quick_insert": "Insertion rapide (éditeur Forge)",
"shortcut.quick_search": "Recherche rapide (hors champ texte)",
"shortcut.save_file": "Sauvegarder le fichier ouvert",
"shortcut.search_in_doc": "Rechercher dans le document",
"shortcut.tip_preview": "Survolez un document avec **Ctrl** pour un aperçu rapide. Cliquez sur **Forge** dans la barre d'outils pour l'éditeur avancé.",
"sidebar.all_vaults": "Tous les vaults",
"sidebar.case_sensitive": "Respecter la casse",
"sidebar.clear_filter": "Effacer",