Replace hardcoded French strings with i18n calls
Initialize i18n before auth so t() works when rendering auth-dependent UI, and build the command palette lazily so translations are loaded first. Bump service worker cache version.
This commit is contained in:
+51
-31
@@ -2,9 +2,9 @@
|
||||
* Vault Home Page module for ObsiGate.
|
||||
*
|
||||
* When a specific vault is selected (not "All"), replaces the dashboard
|
||||
* with a recursive file listing sorted by modification time (newest first).
|
||||
* Files from the selected directory AND all its subdirectories are shown
|
||||
* with their relative path displayed in the metadata line.
|
||||
* with a non-recursive file listing sorted by modification time (newest first).
|
||||
* Only files from the selected directory are shown (not subdirectories)
|
||||
* to keep the listing fast and lightweight.
|
||||
* The listing updates automatically when browsing into subdirectories.
|
||||
*
|
||||
* The visual style mirrors the search results (.search-result-item) for
|
||||
@@ -18,6 +18,7 @@ import { api } from './auth.js';
|
||||
import { el } from './viewer.js';
|
||||
import { getFileIcon, safeCreateIcons } from './utils.js';
|
||||
import { TabManager } from './ui.js';
|
||||
import { t, getLocale } from './i18n.js';
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// State
|
||||
@@ -77,7 +78,7 @@ export async function showVaultHome(vaultName, dirPath = '') {
|
||||
resultsArea.innerHTML = `
|
||||
<div class="tree-loading" style="padding:2rem;text-align:center">
|
||||
<div class="loading-spinner" style="width:24px;height:24px;border-width:3px;margin:0 auto"></div>
|
||||
<p style="margin-top:0.75rem;color:var(--text-muted);font-size:0.85rem">Chargement des fichiers...</p>
|
||||
<p data-i18n="vaulthome.loading" style="margin-top:0.75rem;color:var(--text-muted);font-size:0.85rem">${t('vaulthome.loading')}</p>
|
||||
</div>`;
|
||||
|
||||
await loadAndRender(vaultName, state.currentDir, resultsArea);
|
||||
@@ -110,6 +111,10 @@ export async function updateVaultHomeDir(vaultName, dirPath) {
|
||||
safeCreateIcons();
|
||||
}
|
||||
|
||||
// Remove recent section when navigating into subdirectory
|
||||
const recentSection = container.querySelector('.vault-home-recent');
|
||||
if (recentSection) recentSection.remove();
|
||||
|
||||
// Reload results
|
||||
const resultsArea = document.getElementById('vault-home-results');
|
||||
if (resultsArea) {
|
||||
@@ -125,9 +130,31 @@ export async function updateVaultHomeDir(vaultName, dirPath) {
|
||||
// Tree click delegation — update vault home on directory clicks
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
let _treeDelegationInstalled = false;
|
||||
|
||||
function _initTreeDelegation() {
|
||||
if (_treeDelegationInstalled) return;
|
||||
|
||||
const tree = document.getElementById('vault-tree');
|
||||
if (!tree) return;
|
||||
if (!tree) {
|
||||
// Retry once when DOM might not be ready — use MutationObserver as fallback
|
||||
const observer = new MutationObserver(() => {
|
||||
const t = document.getElementById('vault-tree');
|
||||
if (t) {
|
||||
observer.disconnect();
|
||||
_attachTreeListener(t);
|
||||
}
|
||||
});
|
||||
observer.observe(document.body, { childList: true, subtree: true });
|
||||
return;
|
||||
}
|
||||
|
||||
_attachTreeListener(tree);
|
||||
}
|
||||
|
||||
function _attachTreeListener(tree) {
|
||||
if (_treeDelegationInstalled) return;
|
||||
_treeDelegationInstalled = true;
|
||||
|
||||
tree.addEventListener('click', (e) => {
|
||||
if (!state.active) return;
|
||||
@@ -192,14 +219,14 @@ function buildBreadcrumbHTML(parentEl, vaultName, dirPath) {
|
||||
|
||||
async function loadAndRender(vaultName, dirPath, container) {
|
||||
try {
|
||||
const url = `/api/vault/${encodeURIComponent(vaultName)}/files?dir=${encodeURIComponent(dirPath)}&limit=200&recursive=true`;
|
||||
const url = `/api/vault/${encodeURIComponent(vaultName)}/files?dir=${encodeURIComponent(dirPath)}&limit=200&recursive=false`;
|
||||
const data = await api(url);
|
||||
|
||||
if (!data.files || data.files.length === 0) {
|
||||
container.innerHTML = `
|
||||
<div class="vault-home-empty">
|
||||
<i data-lucide="folder-open" style="width:32px;height:32px;color:var(--text-muted)"></i>
|
||||
<p>Aucun fichier dans ce répertoire</p>
|
||||
<p data-i18n="vaulthome.empty">${t('vaulthome.empty')}</p>
|
||||
</div>`;
|
||||
safeCreateIcons();
|
||||
return;
|
||||
@@ -207,21 +234,15 @@ async function loadAndRender(vaultName, dirPath, container) {
|
||||
|
||||
// Header
|
||||
const header = el('div', { class: 'vault-home-header' });
|
||||
const count = el('span', { class: 'vault-home-count' }, [
|
||||
document.createTextNode(`${data.files.length} fichier${data.files.length > 1 ? 's' : ''}`),
|
||||
const countKey = data.files.length > 1 ? 'vaulthome.file_count_plural' : 'vaulthome.file_count';
|
||||
const countStr = t(countKey, { count: data.files.length });
|
||||
const count = el('span', { class: 'vault-home-count', 'data-i18n': countKey }, [
|
||||
document.createTextNode(countStr),
|
||||
]);
|
||||
header.appendChild(count);
|
||||
|
||||
// Recursive indicator
|
||||
if (data.recursive) {
|
||||
const recursiveBadge = el('span', { class: 'vault-home-recursive-badge' }, [
|
||||
document.createTextNode('récursif'),
|
||||
]);
|
||||
header.appendChild(recursiveBadge);
|
||||
}
|
||||
|
||||
// Directory label
|
||||
const dirLabel = dirPath ? `/${dirPath}` : ' (racine)';
|
||||
const dirLabel = dirPath ? `/${dirPath}` : ` ${t('common.root')}`;
|
||||
const dirInfo = el('span', { class: 'vault-home-dir-info' }, [
|
||||
document.createTextNode(dirLabel),
|
||||
]);
|
||||
@@ -232,28 +253,26 @@ async function loadAndRender(vaultName, dirPath, container) {
|
||||
|
||||
// File list — using search-result-item style
|
||||
const list = el('div', { class: 'search-results' });
|
||||
const locale = getLocale();
|
||||
|
||||
data.files.forEach((f) => {
|
||||
const fileIconName = getFileIcon ? getFileIcon(f.name) : 'file';
|
||||
const displayName = f.name.match(/\.md$/i) ? f.name.replace(/\.md$/i, '') : f.name;
|
||||
const displayName = f.name.endsWith('.md') ? f.name.slice(0, -3) : f.name;
|
||||
const ext = f.extension || '';
|
||||
|
||||
const titleDiv = el('div', { class: 'search-result-title' }, [
|
||||
document.createTextNode(displayName),
|
||||
]);
|
||||
|
||||
// Format date
|
||||
// Format date using current locale
|
||||
const modDate = new Date(f.modified * 1000);
|
||||
const dateStr = modDate.toLocaleDateString('fr-CA', {
|
||||
const dateStr = modDate.toLocaleDateString(locale === 'fr' ? 'fr-CA' : 'en-CA', {
|
||||
year: 'numeric', month: 'short', day: 'numeric',
|
||||
hour: '2-digit', minute: '2-digit',
|
||||
});
|
||||
|
||||
// Build metadata line: rel_dir (if any) + date + size
|
||||
// Build metadata line: date + size
|
||||
const metaParts = [];
|
||||
if (f.rel_dir) {
|
||||
metaParts.push(f.rel_dir + '/');
|
||||
}
|
||||
metaParts.push(dateStr);
|
||||
metaParts.push(formatSize(f.size));
|
||||
|
||||
@@ -284,9 +303,10 @@ async function loadAndRender(vaultName, dirPath, container) {
|
||||
container.appendChild(list);
|
||||
safeCreateIcons();
|
||||
} catch (err) {
|
||||
const errMsg = t('vaulthome.error_loading') + ' ' + escapeHtml(err?.detail || err?.message || t('common.unknown'));
|
||||
container.innerHTML = `
|
||||
<div class="vault-home-error">
|
||||
<p>Erreur de chargement: ${escapeHtml(err?.detail || err?.message || 'Inconnue')}</p>
|
||||
<p data-i18n="vaulthome.error_loading">${errMsg}</p>
|
||||
</div>`;
|
||||
}
|
||||
}
|
||||
@@ -308,13 +328,13 @@ async function loadRecentSection(vaultName, container) {
|
||||
const header = el('div', { class: 'vault-home-recent-header' });
|
||||
header.innerHTML = `
|
||||
<i data-lucide="clock" style="width:14px;height:14px"></i>
|
||||
<span>Récents</span>`;
|
||||
<span data-i18n="dashboard.recent">${t('dashboard.recent')}</span>`;
|
||||
section.appendChild(header);
|
||||
|
||||
const list = el('div', { class: 'vault-home-recent-list' });
|
||||
files.forEach((f) => {
|
||||
const title = f.title || f.path.split('/').pop() || '';
|
||||
const displayTitle = title.match(/\.md$/i) ? title.replace(/\.md$/i, '') : title;
|
||||
const displayTitle = title.endsWith('.md') ? title.slice(0, -3) : title;
|
||||
const dir = f.path.includes('/') ? f.path.split('/').slice(0, -1).join('/') + '/' : '';
|
||||
|
||||
const item = el('div', { class: 'vault-home-recent-item' });
|
||||
@@ -352,9 +372,9 @@ async function loadRecentSection(vaultName, container) {
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
function formatSize(bytes) {
|
||||
if (bytes < 1024) return bytes + ' o';
|
||||
if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(1) + ' Ko';
|
||||
return (bytes / (1024 * 1024)).toFixed(1) + ' Mo';
|
||||
if (bytes < 1024) return bytes + ' ' + t('vaulthome.size_bytes');
|
||||
if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(1) + ' ' + t('vaulthome.size_kb');
|
||||
return (bytes / (1024 * 1024)).toFixed(1) + ' ' + t('vaulthome.size_mb');
|
||||
}
|
||||
|
||||
function escapeHtml(str) {
|
||||
|
||||
Reference in New Issue
Block a user