feat: complete i18n coverage — all dynamic content translated
CI / test (push) Has been cancelled
CI / security (push) Has been cancelled
CI / build (push) Has been cancelled
CI / e2e (push) Has been cancelled
CI / lint (push) Has been cancelled

All remaining French strings converted to t() calls:
- ui.js: create/delete file/dir modals, rename/copy/bookmark toasts
- palette.js: confirm dialogs, error toasts
- search.js: result summary text with interpolation
- config.js: about/diagnostics section labels
- auth.js: admin panel title
- viewer.js: forge popout labels
- dashboard.js: shared widget buttons, date locale-aware

57 new locale keys for dynamic content (modals, toasts, admin, about)
Total: 597 keys per language

All 26 modules validated — 163 exports, 0 errors
This commit is contained in:
2026-06-18 14:18:25 -04:00
parent f91a386566
commit 0403be4516
12 changed files with 796 additions and 147 deletions
+2 -2
View File
@@ -354,7 +354,7 @@ const AdminPanel = {
this._modal.innerHTML = `
<div class="editor-container">
<div class="editor-header">
<div class="editor-title">⚙️ Administration — Utilisateurs</div>
<div class="editor-title">⚙️ ' + t('admin.title') + '</div>
<div class="editor-actions">
<button class="editor-btn" id="admin-close" title="Fermer">
<i data-lucide="x" style="width:16px;height:16px"></i>
@@ -363,7 +363,7 @@ const AdminPanel = {
</div>
<div class="editor-body" id="admin-body">
<div class="admin-toolbar">
<button class="btn-login" id="admin-add-user" style="font-size:0.85rem;padding:6px 16px;">+ Nouvel utilisateur</button>
<button class="btn-login" id="admin-add-user" style="font-size:0.85rem;padding:6px 16px;">' + t('admin.new_user') + '</button>
</div>
<div id="admin-users-list" class="admin-users-list"></div>
</div>
+20 -20
View File
@@ -875,7 +875,7 @@ function renderDiagnostics(container, data) {
{
title: "Index",
rows: [
["Fichiers indexés", data.index.total_files],
[t("about.files_indexed"), data.index.total_files],
["Tags uniques", data.index.total_tags],
["Vaults", Object.keys(data.index.vaults).join(", ")],
],
@@ -927,34 +927,34 @@ function loadAbout() {
const sections = [
{
title: "Application",
title: t("about.section_app"),
rows: [
["Nom", "ObsiGate"],
["Version", state.APP_VERSION],
["Version API", health.version || "—"],
["Statut", health.status || "—"],
[t("about.name"), "ObsiGate"],
[t("about.version"), state.APP_VERSION],
[t("about.api_version"), health.version || "—"],
[t("about.status"), health.status || "—"],
],
},
{
title: "Environnement",
title: t("about.section_env"),
rows: [
["Vaults configurés", health.vaults || "—"],
["Fichiers indexés", health.total_files || "—"],
["Navigateur", navigator.userAgent.split(" ").pop()],
["Plateforme", navigator.platform || "—"],
["Langue", navigator.language || "—"],
[t("about.vaults_configured"), health.vaults || "—"],
[t("about.files_indexed"), health.total_files || "—"],
[t("about.browser"), navigator.userAgent.split(" ").pop()],
[t("about.platform"), navigator.platform || "—"],
[t("about.language"), navigator.language || "—"],
],
},
{
title: "Composants",
title: t("about.section_components"),
rows: [
["Backend", "FastAPI (Python)"],
["Rendu Markdown", "mistune"],
["Surveillance fichiers", "watchdog"],
["Frontend", "Vanilla JavaScript"],
["Icônes", "Lucide Icons"],
["Coloration syntaxe", "highlight.js"],
["Éditeur", "CodeMirror 6"],
[t("about.backend"), "FastAPI (Python)"],
[t("about.markdown_renderer"), "mistune"],
[t("about.file_watcher"), "watchdog"],
[t("about.frontend"), "Vanilla JavaScript"],
[t("about.icons"), "Lucide Icons"],
[t("about.syntax_highlight"), "highlight.js"],
[t("about.editor"), "CodeMirror 6"],
],
},
];
+5 -4
View File
@@ -4,6 +4,7 @@ import { state } from './state.js';
import { escapeHtml, safeCreateIcons, getFileIcon } from './utils.js';
import { icon, openFile } from './viewer.js';
import { TabManager, showToast } from './ui.js';
import { t, getLocale } from './i18n.js';
// ---------------------------------------------------------------------------
// Recent files
@@ -67,12 +68,12 @@ const DashboardSharedWidget = {
<span class="shared-card-vault">${escapeHtml(s.vault)}</span>
</div>
<div class="shared-card-meta">
<span>${s.access_count || 0} vue(s)</span>
${s.expires_at ? `<span>Expire le ${new Date(s.expires_at).toLocaleDateString("fr-FR")}</span>` : ""}
<span>${s.access_count || 0} ${t('dashboard.views', {count: s.access_count || 0})}</span>
${s.expires_at ? `<span>${t('dashboard.expires', {date: new Date(s.expires_at).toLocaleDateString(getLocale() === 'fr' ? 'fr-FR' : 'en-US')})}</span>` : ""}
</div>
<div class="shared-card-actions">
<button class="shared-copy-btn" data-url="${window.location.origin}/s/${s.token}">📋 Copier</button>
<button class="shared-open-btn">📂 Ouvrir</button>
<button class="shared-copy-btn" data-url="${window.location.origin}/s/${s.token}">${t('dashboard.copy_btn')}</button>
<button class="shared-open-btn">${t('dashboard.open_btn')}</button>
<button class="shared-revoke-btn" data-id="${s.id}">🗑</button>
</div>
</div>
+18 -17
View File
@@ -3,6 +3,7 @@ import { api } from './auth.js';
import { showToast, toggleTheme, TabManager } from './ui.js';
import { openEditor } from './utils.js';
import { state } from './state.js';
import { t } from './i18n.js';
function triggerGoHome() { const e = document.getElementById('header-logo'); if (e) e.click(); }
@@ -35,16 +36,16 @@ let _browseFilterTimer = null; // debounce for global search
// ── Commands ──
const CMDS = [
{ id:'home', label:'🏠 Accueil', desc:"Retour à l'accueil", cat:'Navigation', act:()=>{close();triggerGoHome();} },
{ id:'edit-current', label:'✏️ Éditer fichier courant', desc:'Ouvrir le fichier actif dans l\'éditeur', cat:'Fichiers', act:()=>{close();const f=getCurrentFile();if(f)openEditor(f.vault,f.path);else showToast('Aucun fichier ouvert','error');} },
{ id:'popout', label:'🪟 Pop-out', desc:'Ouvrir le document dans une fenêtre', cat:'Fichiers', act:()=>{close();const f=getCurrentFile();if(f)window.open(`/popout/${encodeURIComponent(f.vault)}/${encodeURIComponent(f.path)}`,'popout','width=1000,height=700');else showToast('Aucun fichier ouvert','error');} },
{ id:'edit-current', label:'✏️ Éditer fichier courant', desc:'Ouvrir le fichier actif dans l\'éditeur', cat:'Fichiers', act:()=>{close();const f=getCurrentFile();if(f)openEditor(f.vault,f.path);else showToast(t('toast.no_open_file'),'error');} },
{ id:'popout', label:'🪟 Pop-out', desc:'Ouvrir le document dans une fenêtre', cat:'Fichiers', act:()=>{close();const f=getCurrentFile();if(f)window.open(`/popout/${encodeURIComponent(f.vault)}/${encodeURIComponent(f.path)}`,'popout','width=1000,height=700');else showToast(t('toast.no_open_file'),'error');} },
{ id:'create-file', label:'📄 Créer fichier markdown', desc:'Choisir la vault, naviguer, puis nommer le fichier', cat:'Fichiers', act:()=>startBrowse('create-file') },
{ id:'delete-file', label:'🗑️ Supprimer fichier', desc:'Rechercher un fichier à supprimer', cat:'Fichiers', act:()=>startBrowse('delete-file') },
{ id:'delete-cur', label:'🗑️ Supprimer fichier courant', desc:'Supprimer le fichier de l\'onglet actif', cat:'Fichiers', act:async()=>{close();await delCurFile();} },
{ id:'close-cur', label:'✕ Fermer fichier courant', desc:'Fermer l\'onglet actif sans supprimer', cat:'Fichiers', act:()=>{close();const f=getCurrentFile();if(f){const tab=TabManager._tabs.find(t=>t.vault===f.vault&&t.path===f.path);if(tab)TabManager.close(tab.id);else triggerGoHome();}else{showToast('Aucun fichier ouvert','error');}} },
{ id:'close-cur', label:'✕ Fermer fichier courant', desc:'Fermer l\'onglet actif sans supprimer', cat:'Fichiers', act:()=>{close();const f=getCurrentFile();if(f){const tab=TabManager._tabs.find(t=>t.vault===f.vault&&t.path===f.path);if(tab)TabManager.close(tab.id);else triggerGoHome();}else{showToast(t('toast.no_open_file'),'error');}} },
{ id:'create-dir', label:'📁 Créer répertoire', desc:'Choisir la vault, naviguer, puis nommer le dossier', cat:'Répertoires', act:()=>startBrowse('create-dir') },
{ id:'delete-dir', label:'🗑️ Supprimer répertoire', desc:'Choisir la vault, naviguer et supprimer', cat:'Répertoires', act:()=>startBrowse('delete-dir') },
{ id:'theme', label:'🌓 Changer le thème', desc:'Basculer clair/sombre', cat:'Actions', act:()=>{close();toggleTheme();} },
{ id:'reindex', label:'🔄 Réindexer', desc:'Forcer la réindexation complète', cat:'Actions', act:async()=>{close();try{await api('/api/index/reload');showToast('Index rechargé','success');}catch{showToast('Erreur','error');}} },
{ id:'reindex', label:'🔄 Réindexer', desc:'Forcer la réindexation complète', cat:'Actions', act:async()=>{close();try{await api('/api/index/reload');showToast('Index rechargé','success');}catch{showToast(t('common.error'),'error');}} },
{ id:'help', label:'❓ Aide', desc:'Ouvrir le guide', cat:'Système', act:()=>{close();const b=document.getElementById('help-open-btn');if(b)b.click();} },
{ id:'config', label:'⚙️ Configuration', desc:'Ouvrir les paramètres', cat:'Système', act:()=>{close();const b=document.getElementById('config-open-btn');if(b)b.click();} },
];
@@ -232,20 +233,20 @@ function execNavGlobal(vault, goPath) {
}
async function execDelFileBrowsed(vault, path) {
if (!confirm(`Supprimer le fichier "${vault}/${path}" ?`)) return;
if (!confirm(t("confirm.delete_file_named", {path: vault + "/" + path}))) return;
try {
const d = await api(`/api/file/${encodeURIComponent(vault)}?path=${encodeURIComponent(path)}`, { method:'DELETE' });
if (d?.status==='ok' || d?.success) { showToast(`🗑️ ${path} supprimé`,'success'); close(); refreshSidebar(); }
else showToast('Erreur','error');
else showToast(t('common.error'),'error');
} catch(e) { showToast('Erreur : '+(e.message||e),'error'); }
}
async function execDelDirBrowsed(vault, path) {
if (!confirm(`Supprimer "${path}" et TOUT son contenu ?`)) return;
if (!confirm(t("confirm.delete_dir_named", {path: path}))) return;
try {
const d = await api(`/api/directory/${encodeURIComponent(vault)}?path=${encodeURIComponent(path)}`, { method:'DELETE' });
if (d?.status==='ok' || d?.success) { showToast(`🗑️ ${path} supprimé`,'success'); close(); refreshSidebar(); }
else showToast('Erreur','error');
else showToast(t('common.error'),'error');
} catch(e) { showToast('Erreur : '+(e.message||e),'error'); }
}
@@ -337,7 +338,7 @@ async function execCreateFile(name) {
try {
const d = await api(`/api/file/${encodeURIComponent(_browse.vault)}`, { method:'POST', headers:{'Content-Type':'application/json'}, body:JSON.stringify({path:fp, content:`# ${name.replace('.md','')}\n\n`}) });
if (d?.status==='ok' || d?.success) { showToast(`✅ ${fp} créé`,'success'); close(); openEditor(_browse.vault, fp); }
else showToast('Erreur de création','error');
else showToast(t('toast.create_error'),'error');
} catch(e) { showToast('Erreur : '+(e.message||e),'error'); }
}
@@ -346,37 +347,37 @@ async function execCreateDir(name) {
try {
const d = await api(`/api/directory/${encodeURIComponent(_browse.vault)}`, { method:'POST', headers:{'Content-Type':'application/json'}, body:JSON.stringify({path:dp}) });
if (d?.status==='ok' || d?.success) { showToast(`📁 ${dp} créé`,'success'); close(); refreshSidebar(); }
else showToast('Erreur','error');
else showToast(t('common.error'),'error');
} catch(e) { showToast('Erreur : '+(e.message||e),'error'); }
}
async function execDelDir(path) {
if (!confirm(`Supprimer "${path}" et TOUT son contenu ?`)) return;
if (!confirm(t("confirm.delete_dir_named", {path: path}))) return;
try {
const d = await api(`/api/directory/${encodeURIComponent(_browse.vault)}?path=${encodeURIComponent(path)}`, { method:'DELETE' });
if (d?.status==='ok' || d?.success) { showToast(`🗑️ ${path} supprimé`,'success'); close(); refreshSidebar(); }
else showToast('Erreur','error');
else showToast(t('common.error'),'error');
} catch(e) { showToast('Erreur : '+(e.message||e),'error'); }
}
async function execDelFile(path) {
if (!confirm(`Supprimer le fichier "${path}" ?`)) return;
if (!confirm(t("confirm.delete_file_named", {path: path}))) return;
try {
const d = await api(`/api/file/${encodeURIComponent(_browse.vault)}?path=${encodeURIComponent(path)}`, { method:'DELETE' });
if (d?.status==='ok' || d?.success) { showToast(`🗑️ ${path} supprimé`,'success'); close(); refreshSidebar(); }
else showToast('Erreur','error');
else showToast(t('common.error'),'error');
} catch(e) { showToast('Erreur : '+(e.message||e),'error'); }
}
async function delCurFile() {
const f = getCurrentFile();
if (!f) { showToast('Aucun fichier ouvert','error'); return; }
if (!confirm(`Supprimer "${f.path}" ?`)) return;
if (!f) { showToast(t('toast.no_open_file'),'error'); return; }
if (!confirm(t("confirm.delete_file_named", {path: f.path}))) return;
try {
const d = await api(`/api/file/${encodeURIComponent(f.vault)}?path=${encodeURIComponent(f.path)}`, { method:'DELETE' });
if (d?.status==='ok' || d?.success) { showToast(`🗑️ ${f.path} supprimé`,'success');
const tab = TabManager._tabs.find(t=>t.vault===f.vault&&t.path===f.path); if(tab) TabManager.close(tab.id); triggerGoHome(); refreshSidebar(); }
else showToast('Erreur','error');
else showToast(t('common.error'),'error');
} catch(e) { showToast('Erreur : '+(e.message||e),'error'); }
}
+6 -5
View File
@@ -7,6 +7,7 @@ import { searchOffline } from './offline.js';
import { _getEffective } from './config.js';
import { TabManager, showToast } from './ui.js';
import { addTagFilter, buildSearchResultsHeader, shouldDisplayPath, removeTagFilter, TagFilterService } from './sidebar.js';
import { t } from './i18n.js';
// ---------------------------------------------------------------------------
// Search History Service (localStorage, LIFO, max 50, dedup)
// ---------------------------------------------------------------------------
@@ -915,17 +916,17 @@ export function renderAdvancedSearchResults(data, query, tagFilter) {
const freeText = parsed.freeText;
if (freeText && tagFilter) {
summaryText.textContent = `${data.total} résultat(s) pour "${freeText}" avec filtres`;
summaryText.textContent = t('search.results_filtered', {count: data.total, query: freeText});
} else if (freeText) {
summaryText.textContent = `${data.total} résultat(s) pour "${freeText}"`;
summaryText.textContent = t('search.results_for', {count: data.total, query: freeText});
} else if (parsed.tags.length > 0 || tagFilter) {
summaryText.textContent = `${data.total} fichier(s) avec filtres`;
summaryText.textContent = t('search.files_filtered', {count: data.total});
} else {
summaryText.textContent = `${data.total} résultat(s)`;
summaryText.textContent = t('search.results_count', {count: data.total});
}
if (data.query_time_ms !== undefined && data.query_time_ms > 0) {
const timeBadge = el("span", { class: "search-time-badge" });
timeBadge.textContent = `(${data.query_time_ms} ms)`;
timeBadge.textContent = t('search.query_time', {time: data.query_time_ms});
summaryText.appendChild(timeBadge);
}
header.appendChild(summaryText);
+43 -43
View File
@@ -660,7 +660,7 @@ const FileOperations = {
<div class="modal-form-group">
<label class="modal-label">Nom du dossier</label>
<input type="text" class="modal-input" id="dir-name-input" placeholder="nouveau-dossier" />
<div class="modal-hint">Dans: ${parentPath || '/'}</div>
<div class="modal-hint">${t('modal.in_folder', {path: parentPath || '/'}</div>
<div class="modal-error" id="dir-error" style="display:none;"></div>
</div>
</div>
@@ -711,7 +711,7 @@ const FileOperations = {
const path = parentPath ? `${parentPath}/${name}` : name;
createBtn.disabled = true;
createBtn.textContent = 'Création...';
createBtn.textContent = t('modal.creating');
try {
await api(`/api/directory/${encodeURIComponent(vault)}`, {
@@ -720,13 +720,13 @@ const FileOperations = {
body: JSON.stringify({ path }),
});
showToast(`Dossier "${name}" créé`, 'success');
showToast(t('toast.dir_created', {name: name}), 'success');
this._closeModal(overlay);
await refreshSidebarTreePreservingState();
} catch (err) {
showToast(err.message || 'Erreur lors de la création', 'error');
showToast(err.message || t('toast.create_error'), 'error');
createBtn.disabled = false;
createBtn.textContent = 'Créer';
createBtn.textContent = t('modal.create_btn');
}
};
@@ -745,17 +745,17 @@ const FileOperations = {
modal.className = 'obsigate-modal';
modal.innerHTML = `
<div class="obsigate-modal-header">
<h3 class="obsigate-modal-title">Créer un fichier</h3>
<h3 class="obsigate-modal-title">${t('modal.create_file')}</h3>
</div>
<div class="obsigate-modal-body">
<div class="modal-form-group">
<label class="modal-label">Nom du fichier</label>
<label class="modal-label">${t('modal.file_name')}</label>
<input type="text" class="modal-input" id="file-name-input" placeholder="note.md" />
<div class="modal-hint">Dans: ${parentPath || '/'}</div>
<div class="modal-hint">${t('modal.in_folder', {path: parentPath || '/'}</div>
<div class="modal-error" id="file-error" style="display:none;"></div>
</div>
<div class="modal-form-group">
<label class="modal-label">Type de fichier</label>
<label class="modal-label">${t('modal.file_type')}</label>
<select class="modal-select" id="file-ext-select">
<option value=".md">Markdown (.md)</option>
<option value=".txt">Texte (.txt)</option>
@@ -769,8 +769,8 @@ const FileOperations = {
</div>
</div>
<div class="obsigate-modal-footer">
<button class="modal-btn" id="file-cancel-btn">Annuler</button>
<button class="modal-btn primary" id="file-create-btn">Créer</button>
<button class="modal-btn" id="file-cancel-btn">${t('common.cancel')}</button>
<button class="modal-btn primary" id="file-create-btn">${t('modal.create_btn')}</button>
</div>
`;
@@ -821,7 +821,7 @@ const FileOperations = {
const path = parentPath ? `${parentPath}/${name}` : name;
createBtn.disabled = true;
createBtn.textContent = 'Création...';
createBtn.textContent = t('modal.creating');
try {
await api(`/api/file/${encodeURIComponent(vault)}`, {
@@ -830,14 +830,14 @@ const FileOperations = {
body: JSON.stringify({ path, content: '' }),
});
showToast(`Fichier "${name}" créé`, 'success');
showToast(t('toast.file_created', {name: name}), 'success');
this._closeModal(overlay);
await refreshSidebarTreePreservingState();
openFile(vault, path);
} catch (err) {
showToast(err.message || 'Erreur lors de la création', 'error');
showToast(err.message || t('toast.create_error'), 'error');
createBtn.disabled = false;
createBtn.textContent = 'Créer';
createBtn.textContent = t('modal.create_btn');
}
};
@@ -926,10 +926,10 @@ const FileOperations = {
await focusPathInSidebar(vault, state.currentPath, { alignToTop: false });
}
showToast(type === 'directory' ? 'Dossier renommé' : 'Fichier renommé', 'success');
showToast(type === 'directory' ? t('toast.dir_renamed') : t('toast.file_renamed'), 'success');
} catch (err) {
input.disabled = false;
showToast(err.message || 'Erreur lors du renommage', 'error');
showToast(err.message || t('toast.rename_error'), 'error');
input.focus();
input.select();
return;
@@ -964,24 +964,24 @@ const FileOperations = {
modal.className = 'obsigate-modal';
modal.innerHTML = `
<div class="obsigate-modal-header">
<h3 class="obsigate-modal-title">Supprimer le dossier</h3>
<h3 class="obsigate-modal-title">${t('modal.delete_dir')}</h3>
</div>
<div class="obsigate-modal-body">
<div class="modal-warning">
<i data-lucide="alert-triangle" class="icon"></i>
<div>
<strong>Attention !</strong> Cette action est irréversible.
<br>Tous les fichiers et sous-dossiers seront supprimés définitivement.
<strong>${t('modal.delete_warning')}</strong> ${t('modal.delete_irreversible')}
<br>${t('modal.delete_dir_warning')}
</div>
</div>
<div class="modal-form-group">
<label class="modal-label">Dossier à supprimer:</label>
<label class="modal-label">${t('modal.dir_to_delete')}</label>
<div style="font-family: 'JetBrains Mono', monospace; color: var(--text-muted);">${path}</div>
</div>
</div>
<div class="obsigate-modal-footer">
<button class="modal-btn" id="del-cancel-btn">Annuler</button>
<button class="modal-btn danger" id="del-confirm-btn">Supprimer définitivement</button>
<button class="modal-btn" id="del-cancel-btn">${t('common.cancel')}</button>
<button class="modal-btn danger" id="del-confirm-btn">${t('modal.delete_permanently')}</button>
</div>
`;
@@ -996,14 +996,14 @@ const FileOperations = {
const deleteDir = async () => {
confirmBtn.disabled = true;
confirmBtn.textContent = 'Suppression...';
confirmBtn.textContent = t('modal.creating');
try {
const result = await api(`/api/directory/${encodeURIComponent(vault)}?path=${encodeURIComponent(path)}`, {
method: 'DELETE',
});
showToast(`Dossier supprimé (${result.deleted_count} fichiers)`, 'success');
showToast(t('toast.dir_deleted', {count: result.deleted_count}), 'success');
this._closeModal(overlay);
await refreshSidebarTreePreservingState();
@@ -1011,9 +1011,9 @@ const FileOperations = {
showWelcome();
}
} catch (err) {
showToast(err.message || 'Erreur lors de la suppression', 'error');
showToast(err.message || t('toast.delete_error'), 'error');
confirmBtn.disabled = false;
confirmBtn.textContent = 'Supprimer définitivement';
confirmBtn.textContent = t('modal.delete_permanently');
}
};
@@ -1027,23 +1027,23 @@ const FileOperations = {
modal.className = 'obsigate-modal';
modal.innerHTML = `
<div class="obsigate-modal-header">
<h3 class="obsigate-modal-title">Supprimer le fichier</h3>
<h3 class="obsigate-modal-title">${t('modal.delete_file')}</h3>
</div>
<div class="obsigate-modal-body">
<div class="modal-warning">
<i data-lucide="alert-triangle" class="icon"></i>
<div>
<strong>Attention !</strong> Cette action est irréversible.
<strong>${t('modal.delete_warning')}</strong> ${t('modal.delete_irreversible')}
</div>
</div>
<div class="modal-form-group">
<label class="modal-label">Fichier à supprimer:</label>
<label class="modal-label">${t('modal.file_to_delete')}</label>
<div style="font-family: 'JetBrains Mono', monospace; color: var(--text-muted);">${path}</div>
</div>
</div>
<div class="obsigate-modal-footer">
<button class="modal-btn" id="del-cancel-btn">Annuler</button>
<button class="modal-btn danger" id="del-confirm-btn">Supprimer définitivement</button>
<button class="modal-btn" id="del-cancel-btn">${t('common.cancel')}</button>
<button class="modal-btn danger" id="del-confirm-btn">${t('modal.delete_permanently')}</button>
</div>
`;
@@ -1058,14 +1058,14 @@ const FileOperations = {
const deleteFile = async () => {
confirmBtn.disabled = true;
confirmBtn.textContent = 'Suppression...';
confirmBtn.textContent = t('modal.creating');
try {
await api(`/api/file/${encodeURIComponent(vault)}?path=${encodeURIComponent(path)}`, {
method: 'DELETE',
});
showToast('Fichier supprimé', 'success');
showToast(t('toast.file_deleted'), 'success');
this._closeModal(overlay);
await refreshSidebarTreePreservingState();
@@ -1073,9 +1073,9 @@ const FileOperations = {
showWelcome();
}
} catch (err) {
showToast(err.message || 'Erreur lors de la suppression', 'error');
showToast(err.message || t('toast.delete_error'), 'error');
confirmBtn.disabled = false;
confirmBtn.textContent = 'Supprimer définitivement';
confirmBtn.textContent = t('modal.delete_permanently');
}
};
@@ -1772,7 +1772,7 @@ export const ContextMenuManager = {
// Try modern clipboard API first, fall back to execCommand for non-secure contexts
if (navigator.clipboard && navigator.clipboard.writeText) {
navigator.clipboard.writeText(path).then(() => {
showToast(`Chemin copié : ${path}`, 'success');
showToast(t('toast.path_copied', {path: path}), 'success');
}).catch(() => {
this._copyPathFallback(path);
});
@@ -1793,12 +1793,12 @@ export const ContextMenuManager = {
try {
const success = document.execCommand('copy');
if (success) {
showToast(`Chemin copié : ${path}`, 'success');
showToast(t('toast.path_copied', {path: path}), 'success');
} else {
showToast('Erreur lors de la copie', 'error');
showToast(t('toast.copy_error'), 'error');
}
} catch (e) {
showToast('Erreur lors de la copie', 'error');
showToast(t('toast.copy_error'), 'error');
}
document.body.removeChild(textarea);
},
@@ -1810,7 +1810,7 @@ export const ContextMenuManager = {
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ vault: this._targetVault, path: this._targetPath, title: this._targetPath.split("/").pop() }),
});
showToast(data.bookmarked ? "Ajouté aux bookmarks" : "Retiré des bookmarks", "success");
showToast(data.bookmarked ? t("toast.bookmark_added") : t("toast.bookmark_removed"), "success");
if (typeof DashboardBookmarkWidget !== "undefined" && DashboardBookmarkWidget.load) {
DashboardBookmarkWidget.load();
}
@@ -1835,7 +1835,7 @@ export const ContextMenuManager = {
exclude_paths: "",
}),
});
showToast("Repertoire sauvegarde dans les recherches", "success");
showToast(t("toast.dir_saved_search"), "success");
// Switch to saved searches tab
var savedTab = document.getElementById("sidebar-tab-saved");
if (savedTab) savedTab.click();
@@ -1849,7 +1849,7 @@ export const ContextMenuManager = {
import('./backups.js').then(mod => {
mod.openBackupViewer(this._targetVault, this._targetPath);
}).catch(err => {
showToast("Erreur lors du chargement du viewer: " + err.message, "error");
showToast(t("toast.viewer_error") + ": " + err.message, "error");
});
this.hide();
}
+3 -2
View File
@@ -10,6 +10,7 @@ import { renderMermaidBlocks } from './mermaid-viewer.js';
import { DashboardStatsWidget, DashboardRecentWidget, DashboardBookmarkWidget, DashboardSharedWidget, DashboardConflictsWidget } from './dashboard.js';
import { openShareDialog } from './config.js';
import { cacheViewedFile, getCachedFile } from './offline.js';
import { t } from './i18n.js';
// ---------------------------------------------------------------------------
// Outline/TOC Manager
// ---------------------------------------------------------------------------
@@ -534,7 +535,7 @@ export function renderFile(data) {
var titleInp = document.getElementById("editor-title-input");
var fileInp = document.getElementById("editor-filename-input");
if (brand) brand.textContent = "Forge";
if (titleInp) { titleInp.value = data.title || ''; titleInp.placeholder = 'Titre...'; }
if (titleInp) { titleInp.value = data.title || ''; titleInp.placeholder = t('forge.title_placeholder'); }
if (fileInp) fileInp.value = data.path.split('/').pop();
bodyEl.innerHTML = "";
bodyEl.style.overflow = "hidden";
@@ -547,7 +548,7 @@ export function renderFile(data) {
safeCreateIcons();
});
const openNewWindowBtn = el("button", { class: "btn-action", title: "Ouvrir dans une nouvelle fenetre" }, [icon("external-link", 14), document.createTextNode("pop-out")]);
const openNewWindowBtn = el("button", { class: "btn-action", title: t("forge.popout") }, [icon("external-link", 14), document.createTextNode(t("forge.popout_label"))]);
openNewWindowBtn.addEventListener("click", () => {
const popoutUrl = `/popout/${encodeURIComponent(data.vault)}/${encodeURIComponent(data.path)}`;
window.open(popoutUrl, `popout_${data.vault}_${data.path.replace(/[^a-zA-Z0-9]/g, "_")}`, "width=1000,height=700,menubar=no,toolbar=no,location=no,status=no,resizable=yes,scrollbars=no");