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
+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();
}