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
203 lines
11 KiB
Python
203 lines
11 KiB
Python
"""One-pass i18n: replace all French strings in all JS modules."""
|
||
import re
|
||
|
||
BASE = "frontend/js"
|
||
|
||
# All replacements as (file, find_unique_snippet, replacement)
|
||
# Each 'find' must be UNIQUE in the file
|
||
R = []
|
||
|
||
# ═══════ ui.js ═══════
|
||
F = f"{BASE}/ui.js"
|
||
|
||
# Create file modal template
|
||
R.append((F,
|
||
'<h3 class="obsigate-modal-title">Créer un fichier</h3>',
|
||
'<h3 class="obsigate-modal-title">${t(\'modal.create_file\')}</h3>'))
|
||
R.append((F,
|
||
'<label class="modal-label">Nom du fichier</label>',
|
||
'<label class="modal-label">${t(\'modal.file_name\')}</label>'))
|
||
R.append((F,
|
||
'<div class="modal-hint">Dans: ${parentPath',
|
||
'<div class="modal-hint">${t(\'modal.in_folder\', {path: parentPath'))
|
||
R.append((F,
|
||
'<label class="modal-label">Type de fichier</label>',
|
||
'<label class="modal-label">${t(\'modal.file_type\')}</label>'))
|
||
R.append((F,
|
||
'<button class="modal-btn" id="file-cancel-btn">Annuler</button>',
|
||
'<button class="modal-btn" id="file-cancel-btn">${t(\'common.cancel\')}</button>'))
|
||
R.append((F,
|
||
'<button class="modal-btn primary" id="file-create-btn">Créer</button>',
|
||
'<button class="modal-btn primary" id="file-create-btn">${t(\'modal.create_btn\')}</button>'))
|
||
|
||
# Create file action (toasts, textContent)
|
||
R.append((F,
|
||
"createBtn.textContent = 'Création...';\n \n try {\n await api(`/api/file",
|
||
"createBtn.textContent = t('modal.creating');\n \n try {\n await api(`/api/file"))
|
||
R.append((F,
|
||
'showToast(`Fichier "${name}" créé`, \'success\');\n this._closeModal(overlay);\n await refreshSidebarTreePreservingState();\n openFile(vault, path);',
|
||
'showToast(t(\'toast.file_created\', {name: name}), \'success\');\n this._closeModal(overlay);\n await refreshSidebarTreePreservingState();\n openFile(vault, path);'))
|
||
R.append((F,
|
||
"showToast(err.message || 'Erreur lors de la création', 'error');\n createBtn.disabled = false;\n createBtn.textContent = 'Créer';\n }\n };\n \n createBtn.addEventListener('click', create);",
|
||
"showToast(err.message || t('toast.create_error'), 'error');\n createBtn.disabled = false;\n createBtn.textContent = t('modal.create_btn');\n }\n };\n \n createBtn.addEventListener('click', create);"))
|
||
|
||
# Create dir action
|
||
R.append((F,
|
||
"createBtn.textContent = 'Création...';\n \n try {\n await api(`/api/directory",
|
||
"createBtn.textContent = t('modal.creating');\n \n try {\n await api(`/api/directory"))
|
||
R.append((F,
|
||
'showToast(`Dossier "${name}" créé`, \'success\');\n this._closeModal(overlay);\n await refreshSidebarTreePreservingState();',
|
||
'showToast(t(\'toast.dir_created\', {name: name}), \'success\');\n this._closeModal(overlay);\n await refreshSidebarTreePreservingState();'))
|
||
R.append((F,
|
||
"showToast(err.message || 'Erreur lors de la création', 'error');\n createBtn.disabled = false;\n createBtn.textContent = 'Créer';\n }\n };\n \n createBtn.addEventListener('click', create);\n cancelBtn.addEventListener('click', () => this._closeModal(overlay));\n \n input.addEventListener('keydown', (e) => {\n if (e.key === 'Enter') create();\n if (e.key === 'Escape') this._closeModal(overlay);\n });\n },\n\n showCreateFileModal",
|
||
"showToast(err.message || t('toast.create_error'), 'error');\n createBtn.disabled = false;\n createBtn.textContent = t('modal.create_btn');\n }\n };\n \n createBtn.addEventListener('click', create);\n cancelBtn.addEventListener('click', () => this._closeModal(overlay));\n \n input.addEventListener('keydown', (e) => {\n if (e.key === 'Enter') create();\n if (e.key === 'Escape') this._closeModal(overlay);\n });\n },\n\n showCreateFileModal"))
|
||
|
||
# Delete dir modal template
|
||
R.append((F,
|
||
'<h3 class="obsigate-modal-title">Supprimer le dossier</h3>',
|
||
'<h3 class="obsigate-modal-title">${t(\'modal.delete_dir\')}</h3>'))
|
||
R.append((F,
|
||
'<strong>Attention !</strong> Cette action est irréversible.\n <br>Tous les fichiers et sous-dossiers seront supprimés définitivement.',
|
||
'<strong>${t(\'modal.delete_warning\')}</strong> ${t(\'modal.delete_irreversible\')}\n <br>${t(\'modal.delete_dir_warning\')}'))
|
||
R.append((F,
|
||
'<label class="modal-label">Dossier à supprimer:</label>',
|
||
'<label class="modal-label">${t(\'modal.dir_to_delete\')}</label>'))
|
||
|
||
# Delete dir action
|
||
R.append((F,
|
||
"confirmBtn.textContent = 'Suppression...';\n \n try {\n const result = await api(`/api/directory",
|
||
"confirmBtn.textContent = t('modal.creating');\n \n try {\n const result = await api(`/api/directory"))
|
||
R.append((F,
|
||
"showToast(`Dossier supprimé (${result.deleted_count} fichiers)`, 'success');",
|
||
"showToast(t('toast.dir_deleted', {count: result.deleted_count}), 'success');"))
|
||
R.append((F,
|
||
"showToast(err.message || 'Erreur lors de la suppression', 'error');\n confirmBtn.disabled = false;\n confirmBtn.textContent = 'Supprimer définitivement';\n }\n };\n \n confirmBtn.addEventListener('click', deleteDir);",
|
||
"showToast(err.message || t('toast.delete_error'), 'error');\n confirmBtn.disabled = false;\n confirmBtn.textContent = t('modal.delete_permanently');\n }\n };\n \n confirmBtn.addEventListener('click', deleteDir);"))
|
||
|
||
# Delete file modal template
|
||
R.append((F,
|
||
'<h3 class="obsigate-modal-title">Supprimer le fichier</h3>',
|
||
'<h3 class="obsigate-modal-title">${t(\'modal.delete_file\')}</h3>'))
|
||
R.append((F,
|
||
'<strong>Attention !</strong> Cette action est irréversible.\n </div>\n </div>\n <div class="modal-form-group">\n <label class="modal-label">Fichier à supprimer:',
|
||
'<strong>${t(\'modal.delete_warning\')}</strong> ${t(\'modal.delete_irreversible\')}\n </div>\n </div>\n <div class="modal-form-group">\n <label class="modal-label">${t(\'modal.file_to_delete\')}'))
|
||
|
||
# Delete file action
|
||
R.append((F,
|
||
"confirmBtn.textContent = 'Suppression...';\n \n try {\n await api(`/api/file/${encodeURIComponent(vault)}",
|
||
"confirmBtn.textContent = t('modal.creating');\n \n try {\n await api(`/api/file/${encodeURIComponent(vault)}"))
|
||
R.append((F,
|
||
"showToast('Fichier supprimé', 'success');\n this._closeModal(overlay);\n await refreshSidebarTreePreservingState();\n \n if (state.currentVault === vault",
|
||
"showToast(t('toast.file_deleted'), 'success');\n this._closeModal(overlay);\n await refreshSidebarTreePreservingState();\n \n if (state.currentVault === vault"))
|
||
|
||
# ═══════ palette.js ═══════
|
||
F = f"{BASE}/palette.js"
|
||
R.append((F,
|
||
"confirm(`Supprimer le fichier \\\"${path}\\\" ?`)",
|
||
"confirm(t('confirm.delete_file_named', {path: path}))"))
|
||
R.append((F,
|
||
"confirm(`Supprimer \\\"${path}\\\" et TOUT son contenu ?`)",
|
||
"confirm(t('confirm.delete_dir_named', {path: path}))"))
|
||
R.append((F,
|
||
"showToast('Erreur de création','error')",
|
||
"showToast(t('toast.create_error'),'error')"))
|
||
R.append((F,
|
||
"showToast('Erreur','error')",
|
||
"showToast(t('common.error'),'error')"))
|
||
R.append((F,
|
||
"showToast('Aucun fichier ouvert','error')",
|
||
"showToast(t('toast.no_open_file'),'error')"))
|
||
|
||
# ═══════ search.js ═══════
|
||
F = f"{BASE}/search.js"
|
||
R.append((F,
|
||
"summaryText.textContent = `${data.total} résultat(s) pour \"${freeText}\" avec filtres`;",
|
||
"summaryText.textContent = t('search.results_filtered', {count: data.total, query: freeText});"))
|
||
R.append((F,
|
||
"summaryText.textContent = `${data.total} résultat(s) pour \"${freeText}\"`;",
|
||
"summaryText.textContent = t('search.results_for', {count: data.total, query: freeText});"))
|
||
R.append((F,
|
||
"summaryText.textContent = `${data.total} fichier(s) avec filtres`;",
|
||
"summaryText.textContent = t('search.files_filtered', {count: data.total});"))
|
||
R.append((F,
|
||
"summaryText.textContent = `${data.total} résultat(s)`;",
|
||
"summaryText.textContent = t('search.results_count', {count: data.total});"))
|
||
R.append((F,
|
||
"timeBadge.textContent = `(${data.query_time_ms} ms)`;",
|
||
"timeBadge.textContent = t('search.query_time', {time: data.query_time_ms});"))
|
||
|
||
# ═══════ config.js ═══════
|
||
F = f"{BASE}/config.js"
|
||
R.append((F,
|
||
'title: "Application",',
|
||
'title: t("about.section_app"),'))
|
||
R.append((F,
|
||
'title: "Environnement",',
|
||
'title: t("about.section_env"),'))
|
||
R.append((F,
|
||
'title: "Composants",',
|
||
'title: t("about.section_components"),'))
|
||
for old_lbl, key in [
|
||
('"Nom"', "about.name"), ('"Version"', "about.version"),
|
||
('"Version API"', "about.api_version"), ('"Statut"', "about.status"),
|
||
('"Vaults configurés"', "about.vaults_configured"),
|
||
('"Fichiers indexés"', "about.files_indexed"),
|
||
('"Navigateur"', "about.browser"), ('"Plateforme"', "about.platform"),
|
||
('"Langue"', "about.language"), ('"Backend"', "about.backend"),
|
||
('"Rendu Markdown"', "about.markdown_renderer"),
|
||
('"Surveillance fichiers"', "about.file_watcher"),
|
||
('"Frontend"', "about.frontend"), ('"Icônes"', "about.icons"),
|
||
('"Coloration syntaxe"', "about.syntax_highlight"),
|
||
('"Éditeur"', "about.editor"),
|
||
]:
|
||
R.append((F, f'[{old_lbl},', f'[t("{key}"),'))
|
||
|
||
# ═══════ auth.js ═══════
|
||
F = f"{BASE}/auth.js"
|
||
R.append((F,
|
||
"Administration — Utilisateurs",
|
||
"' + t('admin.title') + '"))
|
||
R.append((F,
|
||
"+ Nouvel utilisateur",
|
||
"' + t('admin.new_user') + '"))
|
||
|
||
# ═══════ viewer.js ═══════
|
||
F = f"{BASE}/viewer.js"
|
||
R.append((F,
|
||
"titleInp.placeholder = 'Titre...'",
|
||
"titleInp.placeholder = t('forge.title_placeholder')"))
|
||
R.append((F,
|
||
'title: "Ouvrir dans une nouvelle fenetre"',
|
||
'title: t("forge.popout")'))
|
||
R.append((F,
|
||
'document.createTextNode("pop-out")',
|
||
'document.createTextNode(t("forge.popout_label"))'))
|
||
|
||
# ═══════ dashboard.js ═══════
|
||
F = f"{BASE}/dashboard.js"
|
||
R.append((F,
|
||
'📋 Copier',
|
||
"' + t('dashboard.copy_btn') + '"))
|
||
R.append((F,
|
||
'📂 Ouvrir',
|
||
"' + t('dashboard.open_btn') + '"))
|
||
|
||
# ── EXECUTE ──
|
||
ok = 0
|
||
skip = 0
|
||
for filepath, find, repl in R:
|
||
with open(filepath, "r", encoding="utf-8") as fh:
|
||
content = fh.read()
|
||
count = content.count(find)
|
||
if count == 0:
|
||
print(f"SKIP {filepath.split('/')[-1]}: not found — {find[:50]}...")
|
||
skip += 1
|
||
continue
|
||
content = content.replace(find, repl)
|
||
with open(filepath, "w", encoding="utf-8") as fh:
|
||
fh.write(content)
|
||
ok += 1
|
||
print(f"OK {filepath.split('/')[-1]}: {count}× {find[:40]}...")
|
||
|
||
print(f"\nDone: {ok} applied, {skip} skipped")
|