From 75d3984b8f80489b19724053f5ec9dcf4277ac3c Mon Sep 17 00:00:00 2001 From: Edouard Vanbelle Date: Sat, 25 Apr 2026 23:19:36 +0200 Subject: [PATCH] refactor(ui): i18n: remove unnecessary wrappers --- static/js/app/userMenu.js | 7 +- static/js/core/modal.js | 19 +- static/js/core/notifications.js | 11 +- static/js/features/files/contextMenus.js | 6 +- static/js/features/library/music.js | 225 ++++++++++------------- static/js/features/library/photos.js | 15 +- static/js/views/admin/admin.js | 153 ++++++++------- static/js/views/profile/profile.js | 59 +++--- 8 files changed, 223 insertions(+), 272 deletions(-) diff --git a/static/js/app/userMenu.js b/static/js/app/userMenu.js index c8cf7010..18360258 100644 --- a/static/js/app/userMenu.js +++ b/static/js/app/userMenu.js @@ -212,7 +212,6 @@ function showUserProfileModal() { // FIXME: use classes const barColor = percentage > 90 ? '#ef4444' : percentage > 70 ? '#f59e0b' : '#22c55e'; - const t = (key, fallback) => i18n.t(key) || fallback; const existing = document.getElementById('profile-modal-overlay'); if (existing) existing.remove(); @@ -226,11 +225,11 @@ function showUserProfileModal() {
${initials}

${username}

${email}

- ${role === 'admin' ? 'πŸ›‘οΈ Admin' : `πŸ‘€ ${t('user_menu.role_user', 'User')}`} + ${role === 'admin' ? 'πŸ›‘οΈ Admin' : `πŸ‘€ ${i18n.t('user_menu.role_user')}`}
- ${t('storage.title', 'Storage')} + ${i18n.t('storage.title')}
@@ -238,7 +237,7 @@ function showUserProfileModal() {
${percentage}% Β· ${formatFileSize(usedBytes)} / ${formatQuotaSize(quotaBytes)}
`; diff --git a/static/js/core/modal.js b/static/js/core/modal.js index 22f28522..016f623d 100644 --- a/static/js/core/modal.js +++ b/static/js/core/modal.js @@ -137,14 +137,12 @@ const Modal = { * @returns {Promise} */ promptNewFolder() { - const t = i18n.t.bind(i18n); - return this.prompt({ - title: t('dialogs.new_folder_title') || 'New folder', - label: t('dialogs.folder_name') || 'Folder name', - placeholder: t('dialogs.folder_placeholder') || 'My folder', + title: i18n.t('dialogs.new_folder_title'), + label: i18n.t('dialogs.folder_name'), + placeholder: i18n.t('dialogs.folder_placeholder'), icon: 'fa-folder-plus', - confirmText: t('actions.create') || 'Create' + confirmText: i18n.t('actions.create') }); }, @@ -155,18 +153,15 @@ const Modal = { * @returns {Promise} */ promptRename(currentName, isFolder = false) { - const t = i18n.t.bind(i18n); - - // For files, we want to select only the name part (without extension) this._selectNameOnly = !isFolder; return this.prompt({ - title: t('dialogs.rename_title') || 'Renombrar', - label: t('dialogs.new_name') || 'Nuevo nombre', + title: i18n.t('dialogs.rename_title'), + label: i18n.t('dialogs.new_name'), placeholder: '', value: currentName, icon: isFolder ? 'fa-folder' : 'fa-file', - confirmText: t('actions.rename') || 'Renombrar' + confirmText: i18n.t('actions.rename') }); }, diff --git a/static/js/core/notifications.js b/static/js/core/notifications.js index 4986653e..4fe484b8 100644 --- a/static/js/core/notifications.js +++ b/static/js/core/notifications.js @@ -149,9 +149,8 @@ const notifications = (() => { item.className = 'notif-item'; item.id = batchId; - const t = i18n.t.bind(i18n); - const uploadingText = folderName ? `πŸ“ ${t('upload.uploading')} ${_esc(folderName)}…` : t('upload.uploading'); - const filesLabel = t('upload.files'); + const uploadingText = folderName ? `πŸ“ ${i18n.t('upload.uploading')} ${_esc(folderName)}…` : i18n.t('upload.uploading'); + const filesLabel = i18n.t('upload.files'); item.innerHTML = `
@@ -254,8 +253,7 @@ const notifications = (() => { const pctEl = $(`${batchId}-pct`); const statsEl = $(`${batchId}-stats`); - const t = i18n.t.bind(i18n); - const filesLabel = t('upload.files'); + const filesLabel = i18n.t('upload.files'); if (fillEl) fillEl.style.width = `${pctVal}%`; if (pctEl) pctEl.textContent = `${pctVal}%`; @@ -282,8 +280,7 @@ const notifications = (() => { const curEl = $(`${batchId}-current`); if (curEl) curEl.textContent = ''; - const t = i18n.t.bind(i18n); - const completeText = t('upload.complete', { + const completeText = i18n.t('upload.complete', { count: successCount, total: totalFiles }); diff --git a/static/js/features/files/contextMenus.js b/static/js/features/files/contextMenus.js index d0eae153..338dd01e 100644 --- a/static/js/features/files/contextMenus.js +++ b/static/js/features/files/contextMenus.js @@ -1115,12 +1115,10 @@ const contextMenus = { }, _renderPlaylistSelect(container, playlists) { - const t = i18n.t.bind(i18n); - container.innerHTML = ''; if (playlists.length === 0) { - container.innerHTML = `
${t('music.no_playlists', 'No playlists yet. Create one first!')}
`; + container.innerHTML = `
${i18n.t('music.no_playlists')}
`; return; } @@ -1131,7 +1129,7 @@ const contextMenus = { item.innerHTML = ` ${this._escapeHtml(playlist.name)} - ${playlist.track_count || 0} ${t('music.tracks', 'tracks')} + ${playlist.track_count || 0} ${i18n.t('music.tracks')} `; item.addEventListener('click', () => { diff --git a/static/js/features/library/music.js b/static/js/features/library/music.js index f002efac..9274a067 100644 --- a/static/js/features/library/music.js +++ b/static/js/features/library/music.js @@ -87,9 +87,6 @@ const musicView = { if (!this._container) return; // FIXME should call directly - const t = (key, _fallback = '') => { - return i18n.t(key); - }; // Empty state: no playlists at all β€” show full-width centered onboarding if (this.playlists.length === 0) { @@ -98,11 +95,11 @@ const musicView = {
-

${t('music.no_playlists', 'No playlists yet')}

-

${t('music.empty_hint', 'Create your first playlist to start organizing your music')}

+

${i18n.t('music.no_playlists')}

+

${i18n.t('music.empty_hint')}

`; @@ -118,8 +115,8 @@ const musicView = {
-

${t('music.playlists', 'Playlists')}

-
@@ -128,44 +125,44 @@ const musicView = {
-

${t('music.select_playlist', 'Select a playlist')}

-

${t('music.select_hint', 'Choose a playlist from the sidebar or create a new one')}

+

${i18n.t('music.select_playlist')}

+

${i18n.t('music.select_hint')}

` @@ -461,8 +454,7 @@ const musicView = { _playTrack(idx) { if (!this.currentTracks[idx]) return; - const t = (key) => i18n.t(key); - musicPlayer.setQueue(this.currentTracks, this.currentPlaylist?.name || t('music.playlists', 'Playlist')); + musicPlayer.setQueue(this.currentTracks, this.currentPlaylist?.name || i18n.t('music.playlists')); musicPlayer.playTrack(idx); }, @@ -479,21 +471,19 @@ const musicView = { const j = Math.floor(Math.random() * (i + 1)); [shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]]; } - const t = (key) => i18n.t(key); - musicPlayer.setQueue(shuffled, this.currentPlaylist?.name || t('music.shuffle', 'Shuffle')); + musicPlayer.setQueue(shuffled, this.currentPlaylist?.name || i18n.t('music.shuffle')); musicPlayer.playTrack(0); } }, async _showCreatePlaylistDialog() { - const t = (key) => i18n.t(key); const name = await Modal.prompt({ - title: t('music.create_playlist', 'Create Playlist'), - label: t('music.playlist_name', 'Playlist name'), - placeholder: t('music.playlist_name', 'Playlist name'), + title: i18n.t('music.create_playlist'), + label: i18n.t('music.playlist_name'), + placeholder: i18n.t('music.playlist_name'), icon: 'fa-music', - confirmText: t('music.create', 'Create') + confirmText: i18n.t('music.create') }); if (!name?.trim()) return; @@ -501,7 +491,6 @@ const musicView = { }, async _createPlaylist(name) { - const t = (key) => i18n.t(key); const createBtn = document.getElementById('music-create-playlist-btn'); if (createBtn) createBtn.disabled = true; try { @@ -522,7 +511,7 @@ const musicView = { notifications.addNotification({ icon: 'fa-check-circle', iconClass: 'upload', - title: t('music.create_playlist', 'Create Playlist'), + title: i18n.t('music.create_playlist'), text: name }); } @@ -532,7 +521,7 @@ const musicView = { notifications.addNotification({ icon: 'fa-exclamation-circle', iconClass: 'error', - title: t('music.error', 'Error'), + title: i18n.t('music.error'), text: err.message }); } @@ -542,18 +531,17 @@ const musicView = { }, async _deletePlaylist() { - const t = (key) => i18n.t(key); if (!this.currentPlaylist) return; const confirmed = await new Promise((resolve) => { Modal.prompt({ - title: t('music.delete', 'Delete'), - label: t('music.confirm_delete', 'Delete this playlist?'), + title: i18n.t('music.delete'), + label: i18n.t('music.confirm_delete'), placeholder: '', value: this.currentPlaylist.name, icon: 'fa-trash', - confirmText: t('music.delete', 'Delete') + confirmText: i18n.t('music.delete') }).then((val) => resolve(val !== null)); }); if (!confirmed) return; @@ -575,7 +563,7 @@ const musicView = { this.currentTracks = []; this._renderPlaylists(); if (notifications) { - notifications.addNotification({ icon: 'fa-check-circle', iconClass: 'upload', title: t('music.delete', 'Delete'), text: deletedName }); + notifications.addNotification({ icon: 'fa-check-circle', iconClass: 'upload', title: i18n.t('music.delete'), text: deletedName }); } } catch (err) { console.error('Delete playlist error:', err); @@ -583,7 +571,7 @@ const musicView = { notifications.addNotification({ icon: 'fa-exclamation-circle', iconClass: 'error', - title: t('music.error', 'Error'), + title: i18n.t('music.error'), text: err.message }); } @@ -628,15 +616,14 @@ const musicView = { async _showEditPlaylistDialog() { if (!this.currentPlaylist) return; - const t = (key) => i18n.t(key); const newName = await Modal.prompt({ - title: t('music.edit', 'Edit'), - label: t('music.playlist_name', 'Playlist name'), - placeholder: t('music.playlist_name', 'Playlist name'), + title: i18n.t('music.edit'), + label: i18n.t('music.playlist_name'), + placeholder: i18n.t('music.playlist_name'), value: this.currentPlaylist.name, icon: 'fa-pen', - confirmText: t('actions.confirm', 'Save') + confirmText: i18n.t('actions.confirm') }); if (!newName?.trim() || newName.trim() === this.currentPlaylist.name) return; @@ -663,7 +650,7 @@ const musicView = { notifications.addNotification({ icon: 'fa-exclamation-circle', iconClass: 'error', - title: t('music.error', 'Error'), + title: i18n.t('music.error'), text: err.message }); } @@ -672,14 +659,13 @@ const musicView = { async _showSharePlaylistDialog() { if (!this.currentPlaylist) return; - const t = (key) => i18n.t(key); const userId = await Modal.prompt({ - title: t('music.share', 'Share'), - label: t('music.share_with_user', 'User ID or email'), - placeholder: t('music.share_with_user', 'User ID or email'), + title: i18n.t('music.share'), + label: i18n.t('music.share_with_user'), + placeholder: i18n.t('music.share_with_user'), icon: 'fa-share-alt', - confirmText: t('music.share', 'Share') + confirmText: i18n.t('music.share') }); if (!userId?.trim()) return; @@ -697,8 +683,8 @@ const musicView = { notifications.addNotification({ icon: 'fa-check-circle', iconClass: 'upload', - title: t('music.share', 'Share'), - text: t('music.added', 'Added!') + title: i18n.t('music.share'), + text: i18n.t('music.added') }); } } catch (err) { @@ -707,7 +693,7 @@ const musicView = { notifications.addNotification({ icon: 'fa-exclamation-circle', iconClass: 'error', - title: t('music.error', 'Error'), + title: i18n.t('music.error'), text: err.message }); } @@ -716,7 +702,6 @@ const musicView = { async _showAddTracksDialog() { if (!this.currentPlaylist) return; - const t = (key) => i18n.t(key); // ── Build modal overlay ── const overlay = document.createElement('div'); @@ -724,23 +709,23 @@ const musicView = { overlay.innerHTML = `
-

${t('music.add_tracks', 'Add Tracks')}

- +

${i18n.t('music.add_tracks')}

+
-
${t('music.loading', 'Loading…')}
+
${i18n.t('music.loading')}
@@ -770,7 +755,7 @@ const musicView = { const AUDIO_EXTENSIONS = 'mp3,ogg,flac,wav,aac,m4a,wma,opus,webm'; const fetchAudioFiles = async (query = '') => { - listEl.innerHTML = `
${t('music.loading', 'Loading…')}
`; + listEl.innerHTML = `
${i18n.t('music.loading')}
`; try { const params = new URLSearchParams({ type_filter: AUDIO_EXTENSIONS, limit: '200', recursive: 'true' }); if (query.trim()) params.set('query', query.trim()); @@ -780,13 +765,13 @@ const musicView = { renderFiles(data.files || []); } catch (err) { console.error('Audio search error:', err); - listEl.innerHTML = `
${t('music.search_error', 'Could not load audio files')}
`; + listEl.innerHTML = `
${i18n.t('music.search_error')}
`; } }; const renderFiles = (files) => { if (files.length === 0) { - listEl.innerHTML = `
${t('music.no_audio_files', 'No audio files found')}
`; + listEl.innerHTML = `
${i18n.t('music.no_audio_files')}
`; return; } listEl.innerHTML = ''; @@ -809,7 +794,7 @@ const musicView = { selectedIds.delete(file.id); row.classList.remove('selected'); } - countEl.textContent = `${selectedIds.size} ${t('music.selected', 'selected')}`; + countEl.textContent = `${selectedIds.size} ${i18n.t('music.selected')}`; addBtn.disabled = selectedIds.size === 0; }); listEl.appendChild(row); @@ -827,7 +812,7 @@ const musicView = { addBtn.addEventListener('click', async () => { if (selectedIds.size === 0) return; addBtn.disabled = true; - addBtn.innerHTML = ` ${t('music.adding', 'Adding…')}`; + addBtn.innerHTML = ` ${i18n.t('music.adding')}`; try { const resp = await fetch(`/api/playlists/${this.currentPlaylist.id}/tracks`, { @@ -842,8 +827,8 @@ const musicView = { notifications.addNotification({ icon: 'fa-check-circle', iconClass: 'upload', - title: t('music.add_tracks', 'Add Tracks'), - text: `${selectedIds.size} ${t('music.added_to_playlist', 'added to playlist')}` + title: i18n.t('music.add_tracks'), + text: `${selectedIds.size} ${i18n.t('music.added_to_playlist')}` }); } close(); @@ -854,7 +839,7 @@ const musicView = { this.currentPlaylist.track_count = playlist.track_count; this._renderPlaylistList(); const metaEl = document.getElementById('music-playlist-meta'); - if (metaEl) metaEl.textContent = `${playlist.track_count} ${t('music.tracks', 'tracks')}`; + if (metaEl) metaEl.textContent = `${playlist.track_count} ${i18n.t('music.tracks')}`; } } catch (err) { console.error('Add tracks error:', err); @@ -862,12 +847,12 @@ const musicView = { notifications.addNotification({ icon: 'fa-exclamation-circle', iconClass: 'error', - title: t('music.error', 'Error'), - text: t('music.add_error', 'Could not add tracks to playlist') + title: i18n.t('music.error'), + text: i18n.t('music.add_error') }); } addBtn.disabled = false; - addBtn.innerHTML = ` ${t('music.add', 'Add')}`; + addBtn.innerHTML = ` ${i18n.t('music.add')}`; } }); @@ -878,7 +863,6 @@ const musicView = { async _removeTrackFromPlaylist(_trackId, fileId) { if (!this.currentPlaylist) return; - const t = (key) => i18n.t(key); try { const resp = await fetch(`/api/playlists/${this.currentPlaylist.id}/tracks/${encodeURIComponent(fileId)}`, { @@ -892,8 +876,8 @@ const musicView = { notifications.addNotification({ icon: 'fa-check-circle', iconClass: 'upload', - title: t('music.remove', 'Remove'), - text: t('music.track_removed', 'Track removed') + title: i18n.t('music.remove'), + text: i18n.t('music.track_removed') }); } await this._loadPlaylistTracks(this.currentPlaylist.id); @@ -903,7 +887,7 @@ const musicView = { this.currentPlaylist.track_count = playlist.track_count; this._renderPlaylistList(); const metaEl = document.getElementById('music-playlist-meta'); - if (metaEl) metaEl.textContent = `${playlist.track_count} ${t('music.tracks', 'tracks')}`; + if (metaEl) metaEl.textContent = `${playlist.track_count} ${i18n.t('music.tracks')}`; } } catch (err) { console.error('Remove track error:', err); @@ -911,7 +895,7 @@ const musicView = { notifications.addNotification({ icon: 'fa-exclamation-circle', iconClass: 'error', - title: t('music.error', 'Error'), + title: i18n.t('music.error'), text: err.message }); } @@ -920,7 +904,6 @@ const musicView = { async _reorderTrack(fromIdx, toIdx) { if (!this.currentPlaylist) return; - const t = (key) => i18n.t(key); const tracks = [...this.currentTracks]; const [moved] = tracks.splice(fromIdx, 1); @@ -943,7 +926,7 @@ const musicView = { notifications.addNotification({ icon: 'fa-exclamation-circle', iconClass: 'error', - title: t('music.error', 'Error'), + title: i18n.t('music.error'), text: err.message }); } @@ -953,7 +936,6 @@ const musicView = { async _showManageSharesDialog() { if (!this.currentPlaylist) return; - const t = (key) => i18n.t(key); const existing = document.getElementById('music-shares-dialog'); if (existing) existing.remove(); @@ -964,19 +946,19 @@ const musicView = { dialog.innerHTML = `
-

${t('music.manage_shares', 'Manage Shares')}

+

${i18n.t('music.manage_shares')}

- +
@@ -1009,8 +991,8 @@ const musicView = { notifications.addNotification({ icon: 'fa-check-circle', iconClass: 'upload', - title: t('music.share', 'Share'), - text: t('music.added', 'Added!') + title: i18n.t('music.share'), + text: i18n.t('music.added') }); } } catch (err) { @@ -1018,7 +1000,7 @@ const musicView = { notifications.addNotification({ icon: 'fa-exclamation-circle', iconClass: 'error', - title: t('music.error', 'Error'), + title: i18n.t('music.error'), text: err.message }); } @@ -1030,7 +1012,6 @@ const musicView = { async _loadSharesList(dialog) { if (!this.currentPlaylist) return; - const t = (key) => i18n.t(key); const body = dialog.querySelector('.music-shares-body'); if (!body) return; @@ -1045,7 +1026,7 @@ const musicView = { const shares = await resp.json(); if (shares.length === 0) { - body.innerHTML = `

${t('music.no_shares', 'No shares yet')}

`; + body.innerHTML = `

${i18n.t('music.no_shares')}

`; return; } @@ -1054,8 +1035,8 @@ const musicView = { (s) => ` ` ) @@ -1075,7 +1056,6 @@ const musicView = { async _removeShare(userId, dialog) { if (!this.currentPlaylist) return; - const t = (key) => i18n.t(key); try { const resp = await fetch(`/api/playlists/${this.currentPlaylist.id}/share/${encodeURIComponent(userId)}`, { @@ -1090,7 +1070,7 @@ const musicView = { notifications.addNotification({ icon: 'fa-exclamation-circle', iconClass: 'error', - title: t('music.error', 'Error'), + title: i18n.t('music.error'), text: err.message }); } @@ -1099,7 +1079,6 @@ const musicView = { async _togglePublic() { if (!this.currentPlaylist) return; - const t = (key) => i18n.t(key); const newValue = !this.currentPlaylist.is_public; try { @@ -1120,16 +1099,16 @@ const musicView = { const btn = document.getElementById('music-toggle-public-btn'); if (btn) { - btn.title = newValue ? t('music.make_private', 'Make private') : t('music.make_public', 'Make public'); + btn.title = newValue ? i18n.t('music.make_private') : i18n.t('music.make_public'); btn.classList.toggle('active', newValue); } if (notifications) { - const status = newValue ? t('music.public', 'Public') : t('music.private', 'Private'); + const status = newValue ? i18n.t('music.public') : i18n.t('music.private'); notifications.addNotification({ icon: 'fa-check-circle', iconClass: 'upload', - title: t('music.toggle_public', 'Visibility'), + title: i18n.t('music.toggle_public'), text: status }); } @@ -1139,7 +1118,7 @@ const musicView = { notifications.addNotification({ icon: 'fa-exclamation-circle', iconClass: 'error', - title: t('music.error', 'Error'), + title: i18n.t('music.error'), text: err.message }); } @@ -1148,7 +1127,6 @@ const musicView = { async _showCoverPicker() { if (!this.currentPlaylist) return; - const t = (key) => i18n.t(key); const input = document.createElement('input'); input.type = 'file'; @@ -1198,8 +1176,8 @@ const musicView = { notifications.addNotification({ icon: 'fa-check-circle', iconClass: 'upload', - title: t('music.set_cover', 'Set cover'), - text: t('music.cover_updated', 'Cover updated') + title: i18n.t('music.set_cover'), + text: i18n.t('music.cover_updated') }); } } catch (err) { @@ -1208,7 +1186,7 @@ const musicView = { notifications.addNotification({ icon: 'fa-exclamation-circle', iconClass: 'error', - title: t('music.error', 'Error'), + title: i18n.t('music.error'), text: err.message }); } @@ -1626,14 +1604,13 @@ const musicPlayer = { console.error('Audio error:', e); this.isPlaying = false; this._updateUI(); - const t = (key) => i18n.t(key); if (notifications) { - const trackName = this.currentTrack?.title || this.currentTrack?.file_name || t('music.unknown_title', 'Unknown'); + const trackName = this.currentTrack?.title || this.currentTrack?.file_name || i18n.t('music.unknown_title'); notifications.addNotification({ icon: 'fa-exclamation-circle', iconClass: 'error', - title: t('music.error', 'Error'), - text: `${t('music.playback_error', 'Playback failed')}: ${trackName}` + title: i18n.t('music.error'), + text: `${i18n.t('music.playback_error')}: ${trackName}` }); } }, @@ -1657,10 +1634,9 @@ const musicPlayer = { } if (trackName) { - const t = (key) => i18n.t(key); trackName.textContent = this.currentTrack - ? this.currentTrack.title || this.currentTrack.file_name || t('music.unknown_title', 'Unknown') - : t('music.not_playing', 'Not playing'); + ? this.currentTrack.title || this.currentTrack.file_name || i18n.t('music.unknown_title') + : i18n.t('music.not_playing'); } if (trackArtist) { @@ -1710,13 +1686,12 @@ const musicPlayer = { const queueList = document.getElementById('player-queue-list'); if (!queueList) return; - const t = (key) => i18n.t(key); if (this.queue.length === 0) { queueList.innerHTML = `
-

${t('music.queue_empty', 'Queue is empty')}

+

${i18n.t('music.queue_empty')}

`; return; @@ -1728,8 +1703,8 @@ const musicPlayer = {
${idx + 1} - ${this._escapeHtml(track.title || track.file_name || t('music.unknown_title', 'Unknown'))} - ${this._escapeHtml(track.artist || t('music.unknown_artist', 'Unknown Artist'))} + ${this._escapeHtml(track.title || track.file_name || i18n.t('music.unknown_title'))} + ${this._escapeHtml(track.artist || i18n.t('music.unknown_artist'))} ${this._formatDuration(track.duration_secs)} diff --git a/static/js/views/admin/admin.js b/static/js/views/admin/admin.js index e06499ba..739927e5 100644 --- a/static/js/views/admin/admin.js +++ b/static/js/views/admin/admin.js @@ -8,11 +8,6 @@ let usersPage = 0; const PAGE_SIZE = 50; let totalUsers = 0; -/* ── i18n helper ── */ -function t(key, params) { - return i18n.t(key, params); -} - /** Escape a string for safe embedding inside a JS string literal within an HTML attribute. */ function _escJs(s) { if (typeof s !== 'string') return ''; @@ -52,14 +47,14 @@ function formatBytes(bytes) { } function timeAgo(dateStr) { - if (!dateStr) return t('admin.never'); + if (!dateStr) return i18n.t('admin.never'); const d = new Date(dateStr); const now = new Date(); const secs = Math.floor((now - d) / 1000); - if (secs < 60) return t('admin.just_now'); - if (secs < 3600) return t('admin.minutes_ago', { n: Math.floor(secs / 60) }); - if (secs < 86400) return t('admin.hours_ago', { n: Math.floor(secs / 3600) }); - if (secs < 2592000) return t('admin.days_ago', { n: Math.floor(secs / 86400) }); + if (secs < 60) return i18n.t('admin.just_now'); + if (secs < 3600) return i18n.t('admin.minutes_ago', { n: Math.floor(secs / 60) }); + if (secs < 86400) return i18n.t('admin.hours_ago', { n: Math.floor(secs / 3600) }); + if (secs < 2592000) return i18n.t('admin.days_ago', { n: Math.floor(secs / 86400) }); return d.toLocaleDateString(); } @@ -157,9 +152,9 @@ async function loadDashboard() { const bar = document.getElementById('ds-bar'); bar.style.width = `${Math.min(d.storage_usage_percent, 100)}%`; bar.className = `progress-fill ${d.storage_usage_percent > 90 ? 'red' : d.storage_usage_percent > 70 ? 'orange' : 'green'}`; - document.getElementById('ds-auth').textContent = d.auth_enabled ? t('admin.enabled') : t('admin.disabled'); - document.getElementById('ds-oidc').textContent = d.oidc_configured ? t('admin.active') : t('admin.off'); - document.getElementById('ds-quotas-flag').textContent = d.quotas_enabled ? t('admin.enabled') : t('admin.disabled'); + document.getElementById('ds-auth').textContent = d.auth_enabled ? i18n.t('admin.enabled') : i18n.t('admin.disabled'); + document.getElementById('ds-oidc').textContent = d.oidc_configured ? i18n.t('admin.active') : i18n.t('admin.off'); + document.getElementById('ds-quotas-flag').textContent = d.quotas_enabled ? i18n.t('admin.enabled') : i18n.t('admin.disabled'); if (typeof d.registration_enabled !== 'undefined') { document.getElementById('ds-registration').checked = d.registration_enabled; @@ -182,7 +177,7 @@ async function loadDashboard() { async function loadUsers() { const tbody = document.getElementById('users-tbody'); - tbody.innerHTML = ` ${escapeHtml(t('admin.loading_users'))}`; + tbody.innerHTML = ` ${escapeHtml(i18n.t('admin.loading_users'))}`; try { const resp = await fetch(`${API}/admin/users?limit=${PAGE_SIZE}&offset=${usersPage * PAGE_SIZE}`, { headers: headers(), @@ -191,7 +186,7 @@ async function loadUsers() { if (!resp.ok) { tbody.innerHTML = ' ' + - escapeHtml(t('admin.failed_load_users')) + + escapeHtml(i18n.t('admin.failed_load_users')) + ''; return; } @@ -199,7 +194,7 @@ async function loadUsers() { totalUsers = data.total; const users = data.users; if (users.length === 0) { - tbody.innerHTML = `${escapeHtml(t('admin.no_users_found'))}`; + tbody.innerHTML = `${escapeHtml(i18n.t('admin.no_users_found'))}`; return; } @@ -219,12 +214,12 @@ async function loadUsers() { '"> ' + escapeHtml(u.auth_provider) + '' - : `${escapeHtml(t('admin.local'))}`; + : `${escapeHtml(i18n.t('admin.local'))}`; return ( '' + '' + @@ -240,7 +235,7 @@ async function loadUsers() { '' + - (u.active ? escapeHtml(t('admin.active')) : escapeHtml(t('admin.inactive'))) + + (u.active ? escapeHtml(i18n.t('admin.active')) : escapeHtml(i18n.t('admin.inactive'))) + '' + '
' + (isOidc ? '' @@ -269,14 +264,14 @@ async function loadUsers() { '" data-uname="' + _escJs(u.username) + '" title="' + - escapeHtml(t('admin.reset_password_title')) + + escapeHtml(i18n.t('admin.reset_password_title')) + '">') + '' + @@ -329,13 +324,13 @@ async function loadUsers() { const from = usersPage * PAGE_SIZE + 1; const to = Math.min((usersPage + 1) * PAGE_SIZE, totalUsers); - document.getElementById('users-info').textContent = t('admin.showing_users', { from: from, to: to, total: totalUsers }); + document.getElementById('users-info').textContent = i18n.t('admin.showing_users', { from: from, to: to, total: totalUsers }); document.getElementById('prev-btn').disabled = usersPage === 0; document.getElementById('next-btn').disabled = (usersPage + 1) * PAGE_SIZE >= totalUsers; } catch (e) { tbody.innerHTML = ' ' + - escapeHtml(t('admin.error_network', { message: e.message })) + + escapeHtml(i18n.t('admin.error_network', { message: e.message })) + ''; } } @@ -355,7 +350,7 @@ function nextPage() { async function toggleRole(userId, currentRole) { const newRole = currentRole === 'admin' ? 'user' : 'admin'; - const ok = await showConfirm(t('admin.confirm_role_change', { role: newRole })); + const ok = await showConfirm(i18n.t('admin.confirm_role_change', { role: newRole })); if (!ok) return; try { const resp = await fetch(`${API}/admin/users/${userId}/role`, { @@ -367,15 +362,15 @@ async function toggleRole(userId, currentRole) { if (resp.ok) loadUsers(); else { const e = await resp.json(); - alert(e.message || t('admin.error_generic')); + alert(e.message || i18n.t('admin.error_generic')); } } catch (e) { - alert(t('admin.error_network', { message: e.message })); + alert(i18n.t('admin.error_network', { message: e.message })); } } async function toggleActive(userId, currentActive) { - const msg = currentActive ? t('admin.confirm_deactivate') : t('admin.confirm_activate'); + const msg = currentActive ? i18n.t('admin.confirm_deactivate') : i18n.t('admin.confirm_activate'); const ok = await showConfirm(msg); if (!ok) return; try { @@ -388,15 +383,15 @@ async function toggleActive(userId, currentActive) { if (resp.ok) loadUsers(); else { const e = await resp.json(); - alert(e.message || t('admin.error_generic')); + alert(e.message || i18n.t('admin.error_generic')); } } catch (e) { - alert(t('admin.error_network', { message: e.message })); + alert(i18n.t('admin.error_network', { message: e.message })); } } async function deleteUser(userId, username) { - const ok = await showConfirm(t('admin.confirm_delete_user', { name: username })); + const ok = await showConfirm(i18n.t('admin.confirm_delete_user', { name: username })); if (!ok) return; try { const resp = await fetch(`${API}/admin/users/${userId}`, { @@ -409,10 +404,10 @@ async function deleteUser(userId, username) { loadDashboard(); } else { const e = await resp.json(); - alert(e.message || t('admin.error_generic')); + alert(e.message || i18n.t('admin.error_generic')); } } catch (e) { - alert(t('admin.error_network', { message: e.message })); + alert(i18n.t('admin.error_network', { message: e.message })); } } @@ -446,10 +441,10 @@ async function saveQuota() { loadDashboard(); } else { const e = await resp.json(); - alert(e.message || t('admin.error_generic')); + alert(e.message || i18n.t('admin.error_generic')); } } catch (e) { - alert(t('admin.error_network', { message: e.message })); + alert(i18n.t('admin.error_network', { message: e.message })); } } @@ -480,19 +475,19 @@ async function submitCreateUser() { const errorEl = document.getElementById('cu-error'); if (username.length < 3) { - errorEl.textContent = t('admin.error_username_short'); + errorEl.textContent = i18n.t('admin.error_username_short'); errorEl.className = 'alert alert-error'; return; } if (password.length < 8) { - errorEl.textContent = t('admin.error_password_short'); + errorEl.textContent = i18n.t('admin.error_password_short'); errorEl.className = 'alert alert-error'; return; } const btn = document.getElementById('cu-submit'); btn.disabled = true; - btn.innerHTML = ` ${escapeHtml(t('admin.creating'))}`; + btn.innerHTML = ` ${escapeHtml(i18n.t('admin.creating'))}`; try { const resp = await fetch(`${API}/admin/users`, { method: 'POST', @@ -512,15 +507,15 @@ async function submitCreateUser() { loadDashboard(); } else { const e = await resp.json().catch(() => ({})); - errorEl.textContent = e.message || t('admin.error_create_user'); + errorEl.textContent = e.message || i18n.t('admin.error_create_user'); errorEl.className = 'alert alert-error'; } } catch (e) { - errorEl.textContent = t('admin.error_network', { message: e.message }); + errorEl.textContent = i18n.t('admin.error_network', { message: e.message }); errorEl.className = 'alert alert-error'; } btn.disabled = false; - btn.innerHTML = ` ${escapeHtml(t('admin.create_user'))}`; + btn.innerHTML = ` ${escapeHtml(i18n.t('admin.create_user'))}`; } let resetPwUserId = ''; @@ -541,14 +536,14 @@ async function submitResetPassword() { const password = document.getElementById('rp-password').value; const errorEl = document.getElementById('rp-error'); if (password.length < 8) { - errorEl.textContent = t('admin.error_password_short'); + errorEl.textContent = i18n.t('admin.error_password_short'); errorEl.className = 'alert alert-error'; return; } const btn = document.getElementById('rp-submit'); btn.disabled = true; - btn.innerHTML = ` ${escapeHtml(t('admin.resetting'))}`; + btn.innerHTML = ` ${escapeHtml(i18n.t('admin.resetting'))}`; try { const resp = await fetch(`${API}/admin/users/${resetPwUserId}/password`, { method: 'PUT', @@ -560,15 +555,15 @@ async function submitResetPassword() { closeResetPasswordModal(); } else { const e = await resp.json().catch(() => ({})); - errorEl.textContent = e.message || t('admin.error_generic'); + errorEl.textContent = e.message || i18n.t('admin.error_generic'); errorEl.className = 'alert alert-error'; } } catch (e) { - errorEl.textContent = t('admin.error_network', { message: e.message }); + errorEl.textContent = i18n.t('admin.error_network', { message: e.message }); errorEl.className = 'alert alert-error'; } btn.disabled = false; - btn.innerHTML = ` ${escapeHtml(t('admin.reset_btn'))}`; + btn.innerHTML = ` ${escapeHtml(i18n.t('admin.reset_btn'))}`; } async function toggleRegistration(enabled) { @@ -586,13 +581,13 @@ async function toggleRegistration(enabled) { if (!enabled) showElement('registration-warning', 'flex'); else hideElement('registration-warning'); const e = await resp.json().catch(() => ({})); - alert(e.message || t('admin.error_generic')); + alert(e.message || i18n.t('admin.error_generic')); } } catch (e) { document.getElementById('ds-registration').checked = !enabled; if (!enabled) showElement('registration-warning', 'flex'); else hideElement('registration-warning'); - alert(t('admin.error_network', { message: e.message })); + alert(i18n.t('admin.error_network', { message: e.message })); } } @@ -624,7 +619,7 @@ async function testConnection() { } const btn = document.getElementById('discover-btn'); btn.disabled = true; - btn.innerHTML = ` ${escapeHtml(t('admin.discovering'))}`; + btn.innerHTML = ` ${escapeHtml(i18n.t('admin.discovering'))}`; const resultDiv = document.getElementById('discovery-result'); try { const resp = await fetch(`${API}/admin/settings/oidc/test`, { @@ -652,13 +647,13 @@ async function testConnection() { resultDiv.innerHTML = `
Error: ${escapeHtml(e.message)}
`; } btn.disabled = false; - btn.innerHTML = ` ${escapeHtml(t('admin.auto_discover'))}`; + btn.innerHTML = ` ${escapeHtml(i18n.t('admin.auto_discover'))}`; } async function saveOidcSettings() { const btn = document.getElementById('save-btn'); btn.disabled = true; - btn.innerHTML = ` ${escapeHtml(t('admin.saving'))}`; + btn.innerHTML = ` ${escapeHtml(i18n.t('admin.saving'))}`; const body = { enabled: document.getElementById('oidc-enabled').checked, issuer_url: document.getElementById('issuer-url').value.trim(), @@ -678,18 +673,18 @@ async function saveOidcSettings() { body: JSON.stringify(body) }); if (resp.ok) { - const status = body.enabled ? t('admin.active').toLowerCase() : t('admin.disabled').toLowerCase(); - showOidcStatus(t('admin.settings_saved', { status: status }), 'success'); + const status = body.enabled ? i18n.t('admin.active').toLowerCase() : i18n.t('admin.disabled').toLowerCase(); + showOidcStatus(i18n.t('admin.settings_saved', { status: status }), 'success'); loadDashboard(); } else { const e = await resp.json().catch(() => ({})); showOidcStatus(`Error: ${e.message || resp.statusText}`, 'error'); } } catch (e) { - showOidcStatus(t('admin.error_network', { message: e.message }), 'error'); + showOidcStatus(i18n.t('admin.error_network', { message: e.message }), 'error'); } btn.disabled = false; - btn.innerHTML = ` ${escapeHtml(t('admin.save_btn'))}`; + btn.innerHTML = ` ${escapeHtml(i18n.t('admin.save_btn'))}`; } /* ── Storage tab ── */ @@ -750,7 +745,7 @@ async function loadStorage() { // Secret hints if (s.s3_access_key_set) { - document.getElementById('storage-access-key').placeholder = t('admin.storage_key_placeholder') || 'Leave empty to keep current value'; + document.getElementById('storage-access-key').placeholder = i18n.t('admin.storage_key_placeholder') || 'Leave empty to keep current value'; } if (s.s3_secret_key_set) { showElement('storage-secret-hint'); @@ -770,7 +765,7 @@ async function loadStorage() { document.getElementById('storage-total-size').textContent = s.total_bytes_stored != null ? formatBytes(s.total_bytes_stored) : 'β€”'; document.getElementById('storage-dedup-ratio').textContent = s.dedup_ratio != null ? `${s.dedup_ratio.toFixed(2)}x` : 'β€”'; } catch (e) { - showStorageStatus(t('admin.error_network', { message: e.message }), 'error'); + showStorageStatus(i18n.t('admin.error_network', { message: e.message }), 'error'); } // Also load migration status @@ -780,7 +775,7 @@ async function loadStorage() { async function saveStorageSettings() { const btn = document.getElementById('btn-save-storage'); btn.disabled = true; - btn.innerHTML = ` ${escapeHtml(t('admin.saving'))}`; + btn.innerHTML = ` ${escapeHtml(i18n.t('admin.saving'))}`; const backend = document.querySelector('input[name="storage-backend"]:checked').value; const body = { @@ -801,23 +796,23 @@ async function saveStorageSettings() { body: JSON.stringify(body) }); if (resp.ok) { - showStorageStatus(t('admin.storage_saved') || 'Storage settings saved successfully', 'success'); + showStorageStatus(i18n.t('admin.storage_saved') || 'Storage settings saved successfully', 'success'); loadStorage(); } else { const e = await resp.json().catch(() => ({})); showStorageStatus(`Error: ${e.message || resp.statusText}`, 'error'); } } catch (e) { - showStorageStatus(t('admin.error_network', { message: e.message }), 'error'); + showStorageStatus(i18n.t('admin.error_network', { message: e.message }), 'error'); } btn.disabled = false; - btn.innerHTML = ` ${escapeHtml(t('admin.storage_save') || 'Save')}`; + btn.innerHTML = ` ${escapeHtml(i18n.t('admin.storage_save') || 'Save')}`; } async function testStorageConnection() { const btn = document.getElementById('btn-test-storage'); btn.disabled = true; - btn.innerHTML = ` ${escapeHtml(t('admin.testing') || 'Testing...')}`; + btn.innerHTML = ` ${escapeHtml(i18n.t('admin.testing') || 'Testing...')}`; const backend = document.querySelector('input[name="storage-backend"]:checked').value; const body = { @@ -839,17 +834,17 @@ async function testStorageConnection() { }); const r = await resp.json(); if (r.connected) { - let msg = `${t('admin.storage_test_success') || 'Connection successful'} (${escapeHtml(r.backend_type)})`; + let msg = `${i18n.t('admin.storage_test_success') || 'Connection successful'} (${escapeHtml(r.backend_type)})`; if (r.available_bytes != null) msg += ` β€” ${formatBytes(r.available_bytes)} available`; showStorageStatus(msg, 'success'); } else { - showStorageStatus(`${t('admin.storage_test_failure') || 'Connection failed'}: ${escapeHtml(r.message)}`, 'error'); + showStorageStatus(`${i18n.t('admin.storage_test_failure') || 'Connection failed'}: ${escapeHtml(r.message)}`, 'error'); } } catch (e) { - showStorageStatus(t('admin.error_network', { message: e.message }), 'error'); + showStorageStatus(i18n.t('admin.error_network', { message: e.message }), 'error'); } btn.disabled = false; - btn.innerHTML = ` ${escapeHtml(t('admin.storage_test_connection') || 'Test Connection')}`; + btn.innerHTML = ` ${escapeHtml(i18n.t('admin.storage_test_connection') || 'Test Connection')}`; } /* ── Migration ── */ @@ -951,14 +946,14 @@ async function startMigration() { body: JSON.stringify({ concurrency: 4 }) }); if (resp.ok) { - showMigrationMsg(t('admin.migration_started') || 'Migration started', 'success'); + showMigrationMsg(i18n.t('admin.migration_started') || 'Migration started', 'success'); loadMigrationStatus(); } else { const e = await resp.json().catch(() => ({})); showMigrationMsg(`Error: ${e.message || resp.statusText}`, 'error'); } } catch (e) { - showMigrationMsg(t('admin.error_network', { message: e.message }), 'error'); + showMigrationMsg(i18n.t('admin.error_network', { message: e.message }), 'error'); } btn.disabled = false; } @@ -971,7 +966,7 @@ async function pauseMigration() { credentials: 'same-origin' }); if (resp.ok) { - showMigrationMsg(t('admin.migration_paused_msg') || 'Migration paused', 'success'); + showMigrationMsg(i18n.t('admin.migration_paused_msg') || 'Migration paused', 'success'); loadMigrationStatus(); } } catch (_e) { @@ -987,7 +982,7 @@ async function resumeMigration() { credentials: 'same-origin' }); if (resp.ok) { - showMigrationMsg(t('admin.migration_resumed_msg') || 'Migration resumed', 'success'); + showMigrationMsg(i18n.t('admin.migration_resumed_msg') || 'Migration resumed', 'success'); loadMigrationStatus(); } } catch (_e) { @@ -998,7 +993,7 @@ async function resumeMigration() { async function verifyMigration() { const btn = document.getElementById('btn-verify-migration'); btn.disabled = true; - btn.innerHTML = ` ${escapeHtml(t('admin.migration_verifying') || 'Verifying...')}`; + btn.innerHTML = ` ${escapeHtml(i18n.t('admin.migration_verifying') || 'Verifying...')}`; const resultDiv = document.getElementById('migration-verify-result'); try { const resp = await fetch(`${API}/admin/storage/migration/verify`, { @@ -1010,19 +1005,19 @@ async function verifyMigration() { const r = await resp.json(); resultDiv.style.display = ''; if (r.passed) { - resultDiv.innerHTML = `
${escapeHtml(t('admin.migration_verify_passed') || 'Verification passed')}

${r.sample_checked} blobs checked, ${r.pg_blob_count} total in database

`; + resultDiv.innerHTML = `
${escapeHtml(i18n.t('admin.migration_verify_passed') || 'Verification passed')}

${r.sample_checked} blobs checked, ${r.pg_blob_count} total in database

`; } else { const issues = []; if (r.missing_in_target.length) issues.push(`${r.missing_in_target.length} missing`); if (r.size_mismatches.length) issues.push(`${r.size_mismatches.length} size mismatches`); - resultDiv.innerHTML = `
${escapeHtml(t('admin.migration_verify_failed') || 'Verification failed')}

${issues.join(', ')}

`; + resultDiv.innerHTML = `
${escapeHtml(i18n.t('admin.migration_verify_failed') || 'Verification failed')}

${issues.join(', ')}

`; } } catch (e) { resultDiv.style.display = ''; resultDiv.innerHTML = `
Error: ${escapeHtml(e.message)}
`; } btn.disabled = false; - btn.innerHTML = ` ${escapeHtml(t('admin.migration_verify') || 'Verify Integrity')}`; + btn.innerHTML = ` ${escapeHtml(i18n.t('admin.migration_verify') || 'Verify Integrity')}`; } async function completeMigration() { @@ -1033,14 +1028,14 @@ async function completeMigration() { credentials: 'same-origin' }); if (resp.ok) { - showMigrationMsg(t('admin.migration_completed_msg') || 'Migration finalized. Restart the server to use the new backend.', 'success'); + showMigrationMsg(i18n.t('admin.migration_completed_msg') || 'Migration finalized. Restart the server to use the new backend.', 'success'); loadMigrationStatus(); } else { const e = await resp.json().catch(() => ({})); showMigrationMsg(`Error: ${e.message || resp.statusText}`, 'error'); } } catch (e) { - showMigrationMsg(t('admin.error_network', { message: e.message }), 'error'); + showMigrationMsg(i18n.t('admin.error_network', { message: e.message }), 'error'); } } @@ -1104,7 +1099,7 @@ function showAccessDenied() { /* ── Apply i18n when translations load / change ── */ document.addEventListener('translationsLoaded', () => { i18n.translatePage(); - // Re-render dynamic content that uses t() + // Re-render dynamic content that uses i18n.t() loadDashboard(); if (activeTabName === 'users') loadUsers(); }); diff --git a/static/js/views/profile/profile.js b/static/js/views/profile/profile.js index 1743d101..a73f4ced 100644 --- a/static/js/views/profile/profile.js +++ b/static/js/views/profile/profile.js @@ -3,11 +3,6 @@ import { i18n } from '../../core/i18n.js'; const API = '/api'; -/* ── i18n helper ── */ -function t(key, params) { - return i18n.t(key, params); -} - function headers() { return { 'Content-Type': 'application/json', ...getCsrfHeaders() }; } @@ -21,14 +16,14 @@ function formatBytes(bytes) { } function timeAgo(dateStr) { - if (!dateStr) return t('profile.never'); + if (!dateStr) return i18n.t('profile.never'); const d = new Date(dateStr); const now = new Date(); const secs = Math.floor((now - d) / 1000); - if (secs < 60) return t('profile.just_now'); - if (secs < 3600) return t('profile.minutes_ago', { n: Math.floor(secs / 60) }); - if (secs < 86400) return t('profile.hours_ago', { n: Math.floor(secs / 3600) }); - if (secs < 2592000) return t('profile.days_ago', { n: Math.floor(secs / 86400) }); + if (secs < 60) return i18n.t('profile.just_now'); + if (secs < 3600) return i18n.t('profile.minutes_ago', { n: Math.floor(secs / 60) }); + if (secs < 86400) return i18n.t('profile.hours_ago', { n: Math.floor(secs / 3600) }); + if (secs < 2592000) return i18n.t('profile.days_ago', { n: Math.floor(secs / 86400) }); return d.toLocaleDateString(); } @@ -52,15 +47,15 @@ async function init() { const badge = document.getElementById('p-role-badge'); if (user.role === 'admin') { badge.className = 'role-badge role-badge-admin'; - badge.innerHTML = ` ${t('profile.role_admin')}`; + badge.innerHTML = ` ${i18n.t('profile.role_admin')}`; } else { badge.className = 'role-badge role-badge-user'; - badge.innerHTML = ` ${t('profile.role_user')}`; + badge.innerHTML = ` ${i18n.t('profile.role_user')}`; } document.getElementById('p-detail-username').textContent = user.username; document.getElementById('p-detail-email').textContent = user.email || 'β€”'; - document.getElementById('p-detail-role').textContent = user.role === 'admin' ? t('profile.role_admin') : t('profile.role_user'); + document.getElementById('p-detail-role').textContent = user.role === 'admin' ? i18n.t('profile.role_admin') : i18n.t('profile.role_user'); document.getElementById('p-detail-login').textContent = timeAgo(user.last_login_at); const used = user.storage_used_bytes || 0; @@ -74,7 +69,7 @@ async function init() { const bar = document.getElementById('p-storage-bar'); bar.style.width = `${pct}%`; bar.className = `storage-fill ${pct > 90 ? 'red' : pct > 70 ? 'orange' : 'green'}`; - document.getElementById('p-storage-text').textContent = `${formatBytes(used)} / ${quota > 0 ? formatBytes(quota) : t('profile.unlimited')}`; + document.getElementById('p-storage-text').textContent = `${formatBytes(used)} / ${quota > 0 ? formatBytes(quota) : i18n.t('profile.unlimited')}`; if (user.auth_provider && user.auth_provider !== 'local') { document.getElementById('password-section').classList.add('hidden'); @@ -115,18 +110,18 @@ async function changePassword(e) { const statusEl = document.getElementById('pw-status'); if (newPw !== confirmPw) { - statusEl.innerHTML = `
${escapeHtml(t('profile.passwords_no_match'))}
`; + statusEl.innerHTML = `
${escapeHtml(i18n.t('profile.passwords_no_match'))}
`; return false; } if (newPw.length < 8) { - statusEl.innerHTML = `
${escapeHtml(t('profile.password_too_short'))}
`; + statusEl.innerHTML = `
${escapeHtml(i18n.t('profile.password_too_short'))}
`; return false; } const btn = document.getElementById('pw-submit'); btn.disabled = true; - btn.innerHTML = ` ${escapeHtml(t('profile.updating'))}`; + btn.innerHTML = ` ${escapeHtml(i18n.t('profile.updating'))}`; try { const resp = await fetch(`${API}/auth/change-password`, { @@ -140,24 +135,24 @@ async function changePassword(e) { }); if (resp.ok) { - statusEl.innerHTML = `
${escapeHtml(t('profile.password_updated'))}
`; + statusEl.innerHTML = `
${escapeHtml(i18n.t('profile.password_updated'))}
`; document.getElementById('password-form').reset(); } else { const err = await resp.json().catch(() => ({})); statusEl.innerHTML = '
' + - escapeHtml(err.message || t('profile.password_change_failed')) + + escapeHtml(err.message || i18n.t('profile.password_change_failed')) + '
'; } } catch (err) { statusEl.innerHTML = '
' + - escapeHtml(t('profile.error_network', { message: err.message })) + + escapeHtml(i18n.t('profile.error_network', { message: err.message })) + '
'; } btn.disabled = false; - btn.innerHTML = ` ${escapeHtml(t('profile.update_password'))}`; + btn.innerHTML = ` ${escapeHtml(i18n.t('profile.update_password'))}`; return false; } @@ -176,15 +171,15 @@ function renderPwRow(pw) { const created = document.createElement('td'); created.textContent = new Date(pw.created_at).toLocaleDateString(); const lastUsed = document.createElement('td'); - lastUsed.textContent = pw.last_used_at ? timeAgo(pw.last_used_at) : t('profile.never'); + lastUsed.textContent = pw.last_used_at ? timeAgo(pw.last_used_at) : i18n.t('profile.never'); const status = document.createElement('td'); const badge = document.createElement('span'); if (pw.active !== false) { badge.className = 'badge badge-active'; - badge.textContent = t('profile.active'); + badge.textContent = i18n.t('profile.active'); } else { badge.className = 'badge badge-expired'; - badge.textContent = t('profile.revoked'); + badge.textContent = i18n.t('profile.revoked'); } status.appendChild(badge); const actions = document.createElement('td'); @@ -192,7 +187,7 @@ function renderPwRow(pw) { const btn = document.createElement('button'); btn.className = 'btn btn-danger-sm'; btn.innerHTML = ''; - btn.title = t('profile.revoke_title'); + btn.title = i18n.t('profile.revoke_title'); btn.addEventListener('click', () => { revokeAppPassword(pw.id, pw.label); }); @@ -264,12 +259,12 @@ async function createAppPassword() { const btn = document.getElementById('app-pw-generate'); if (!label) { - statusEl.innerHTML = `
${escapeHtml(t('profile.error_label_required'))}
`; + statusEl.innerHTML = `
${escapeHtml(i18n.t('profile.error_label_required'))}
`; return; } btn.disabled = true; - btn.innerHTML = ` ${escapeHtml(t('profile.generating'))}`; + btn.innerHTML = ` ${escapeHtml(i18n.t('profile.generating'))}`; statusEl.innerHTML = ''; try { @@ -283,7 +278,7 @@ async function createAppPassword() { const err = await resp.json().catch(() => ({})); statusEl.innerHTML = '
' + - escapeHtml(err.message || t('profile.error_create_pw')) + + escapeHtml(err.message || i18n.t('profile.error_create_pw')) + '
'; return; } @@ -297,7 +292,7 @@ async function createAppPassword() { statusEl.innerHTML = `
${err.message}
`; } finally { btn.disabled = false; - btn.innerHTML = ` ${escapeHtml(t('profile.generate'))}`; + btn.innerHTML = ` ${escapeHtml(i18n.t('profile.generate'))}`; } } @@ -313,7 +308,7 @@ function copyAppPassword() { } async function revokeAppPassword(id, label) { - if (!confirm(t('profile.confirm_revoke', { label: label }))) return; + if (!confirm(i18n.t('profile.confirm_revoke', { label: label }))) return; try { const resp = await fetch(`${API}/auth/app-passwords/${encodeURIComponent(id)}`, { method: 'DELETE', @@ -325,10 +320,10 @@ async function revokeAppPassword(id, label) { loadAppPasswords(); } else { const err = await resp.json().catch(() => ({})); - alert(err.message || t('profile.error_revoke')); + alert(err.message || i18n.t('profile.error_revoke')); } } catch (err) { - alert(t('profile.error_network', { message: err.message })); + alert(i18n.t('profile.error_network', { message: err.message })); } }