diff --git a/static/js/app/filesView.js b/static/js/app/filesView.js index 30c5175b..a9650ee4 100644 --- a/static/js/app/filesView.js +++ b/static/js/app/filesView.js @@ -134,7 +134,7 @@ async function loadFiles(options = { insertHistory: true }) { ui.showError(`
- ${i18n ? i18n.t('files.loading') : 'Loading files…'} + ${i18n.t('files.loading')}
`); }, 100); diff --git a/static/js/app/main.js b/static/js/app/main.js index 0170c140..fa07b9d8 100644 --- a/static/js/app/main.js +++ b/static/js/app/main.js @@ -166,9 +166,7 @@ function setActionsBarMode(mode, force = false) { elements.gridViewBtn = document.getElementById('grid-view-btn'); elements.listViewBtn = document.getElementById('list-view-btn'); - if (i18n?.translateElement) { - i18n.translateElement(elements.actionsBar); - } + i18n.translateElement(elements.actionsBar); if (mode === 'files') { setupUploadDropdown(); @@ -395,7 +393,7 @@ function initApp() { }); // Wait for translations to load before checking authentication - if (i18n?.isLoaded?.()) { + if (i18n.isLoaded()) { // Translations already loaded, proceed with authentication checkAuthentication(); } else { @@ -408,7 +406,7 @@ function initApp() { // Set a timeout as a fallback in case translations take too long setTimeout(() => { - if (!i18n?.isLoaded?.()) { + if (!i18n.isLoaded()) { console.warn('Translations loading timeout, proceeding with authentication anyway'); checkAuthentication(); } @@ -727,16 +725,11 @@ function updateStorageUsageDisplay(userData) { // Remove data-i18n attribute to prevent i18n from overwriting our value storageInfo.removeAttribute('data-i18n'); - // Use i18n if available - if (i18n?.t) { - storageInfo.textContent = i18n.t('storage.used', { - percentage: usagePercentage, - used: usedFormatted, - total: quotaFormatted - }); - } else { - storageInfo.textContent = `${usagePercentage}% used (${usedFormatted} / ${quotaFormatted})`; - } + storageInfo.textContent = i18n.t('storage.used', { + percentage: usagePercentage, + used: usedFormatted, + total: quotaFormatted + }); } console.log(`Updated storage display: ${usagePercentage}% (${usedFormatted} / ${quotaFormatted})`); diff --git a/static/js/app/navigation.js b/static/js/app/navigation.js index 018afd9e..bc1451d5 100644 --- a/static/js/app/navigation.js +++ b/static/js/app/navigation.js @@ -152,7 +152,7 @@ function setCurrentSection(section) { // Update page title const titleKey = `nav.${section}`; const defaultTitle = section.charAt(0).toUpperCase() + section.slice(1); - appElements.pageTitle.textContent = i18n ? i18n.t(titleKey) : defaultTitle; + appElements.pageTitle.textContent = i18n.t(titleKey); appElements.pageTitle.setAttribute('data-i18n', titleKey); // Hide sharedView when switching to any other section diff --git a/static/js/app/trashView.js b/static/js/app/trashView.js index 102d8e56..fc89defc 100644 --- a/static/js/app/trashView.js +++ b/static/js/app/trashView.js @@ -15,14 +15,13 @@ async function loadTrashItems() { try { if (multiSelect) multiSelect.clear(); ui.resetFilesList(); // ensure also list visible & error hidden - const _tt = i18n?.t ? i18n.t : (k) => k.split('.').pop(); elements.filesList.innerHTML = `
-
${_tt('files.name')}
-
${_tt('files.type')}
-
${_tt('trash.original_location')}
-
${_tt('trash.deleted_date')}
-
${_tt('trash.actions')}
+
${i18n.t('files.name')}
+
${i18n.t('files.type')}
+
${i18n.t('trash.original_location')}
+
${i18n.t('trash.deleted_date')}
+
${i18n.t('trash.actions')}
`; @@ -33,7 +32,7 @@ async function loadTrashItems() { if (trashItems.length === 0) { ui.showError(` -

${i18n ? i18n.t('trash.empty_state') : 'The trash is empty'}

+

${i18n.t('trash.empty_state')}

`); return; } @@ -58,12 +57,12 @@ function addTrashItemToView(item) { let iconSpecialClass = ''; if (!isFile) { iconClass = item.icon_class || 'fas fa-folder'; - typeLabel = i18n ? i18n.t('files.file_types.folder') : 'Folder'; + typeLabel = i18n.t('files.file_types.folder'); } else { iconClass = item.icon_class || (ui?.getIconClass ? ui.getIconClass(item.name) : 'fas fa-file'); iconSpecialClass = ui?.getIconSpecialClass ? ui.getIconSpecialClass(item.name) : ''; const cat = item.category || ''; - typeLabel = cat ? (i18n ? i18n.t(`files.file_types.${cat.toLowerCase()}`) || cat : cat) : i18n ? i18n.t('files.file_types.document') : 'Document'; + typeLabel = cat ? (i18n.t(`files.file_types.${cat.toLowerCase()}`) || cat) : i18n.t('files.file_types.document'); } const isFolder = !isFile; @@ -86,10 +85,10 @@ function addTrashItemToView(item) {
${escapeHtml(item.original_path || '--')}
${escapeHtml(formattedDate)}
- -
diff --git a/static/js/app/ui.js b/static/js/app/ui.js index 4deac6bb..4c89c5a2 100644 --- a/static/js/app/ui.js +++ b/static/js/app/ui.js @@ -542,17 +542,11 @@ const ui = { breadcrumb.innerHTML = ''; const path = app.breadcrumbPath; // [{id, name}, ...] - // Helper function to safely get translation text - const getTranslatedText = (key, defaultValue) => { - if (!i18n?.t) return defaultValue; - return i18n.t(key); - }; - // -- Home icon (always present, clickable to go to root) -- const homeIcon = document.createElement('span'); homeIcon.className = 'breadcrumb-item breadcrumb-home'; homeIcon.innerHTML = ''; - homeIcon.title = getTranslatedText('breadcrumb.home', 'Home'); + homeIcon.title = i18n.t('breadcrumb.home'); // Home is always clickable if we have a home folder if (app.userHomeFolderId) { @@ -1265,7 +1259,7 @@ const ui = {
-
${i18n ? i18n.t('files.file_types.folder') : 'Folder'}
+
${i18n.t('files.file_types.folder')}
--
${formattedDate}
@@ -1288,7 +1282,7 @@ const ui = { const iconClass = file.icon_class || this.getIconClass(file.name); const iconSpecialClass = file.icon_special_class || this.getIconSpecialClass(file.name); const cat = file.category || ''; - const typeLabel = cat ? (i18n ? i18n.t(`files.file_types.${cat.toLowerCase()}`) || cat : cat) : i18n ? i18n.t('files.file_types.document') : 'Document'; + const typeLabel = cat ? (i18n.t(`files.file_types.${cat.toLowerCase()}`) || cat) : i18n.t('files.file_types.document'); const fileSize = file.size_formatted || formatFileSize(file.size); const formattedDate = formatDateTime(file.modified_at); const isFav = favorites?.isFavorite(file.id, 'file'); @@ -1352,7 +1346,7 @@ const ui = {
`; - if (i18n?.translateElement) i18n.translateElement(filesList); + i18n.translateElement(filesList); filesList.classList.remove('hidden'); filesContainerError?.classList.add('hidden'); @@ -1376,7 +1370,7 @@ const ui = { const filesList = document.getElementById('files-list'); if (filesContainerError) filesContainerError.innerHTML = content; - if (i18n?.translateElement) i18n.translateElement(filesContainerError); + i18n.translateElement(filesContainerError); filesContainerError?.classList.remove('hidden'); filesList?.classList.add('hidden'); @@ -1642,9 +1636,9 @@ if (document.readyState === 'loading') { * @returns {Promise} true if confirmed, false if cancelled */ function showConfirmDialog({ title, message, confirmText, cancelText, danger = true } = {}) { - const ct = confirmText || (i18n ? i18n.t('actions.delete') : 'Delete'); - const cc = cancelText || (i18n ? i18n.t('actions.cancel') : 'Cancel'); - const t = title || (i18n ? i18n.t('dialogs.confirm_title') : 'Confirm action'); + const ct = confirmText || i18n.t('actions.delete'); + const cc = cancelText || i18n.t('actions.cancel'); + const t = title || i18n.t('dialogs.confirm_title'); return new Promise((resolve) => { // Remove any previous confirm dialog diff --git a/static/js/app/userMenu.js b/static/js/app/userMenu.js index 80432567..c8cf7010 100644 --- a/static/js/app/userMenu.js +++ b/static/js/app/userMenu.js @@ -209,9 +209,10 @@ function showUserProfileModal() { const usedBytes = userData.storage_used_bytes || 0; const quotaBytes = userData.storage_quota_bytes == null ? 10 * 1024 * 1024 * 1024 : userData.storage_quota_bytes; const percentage = quotaBytes > 0 ? Math.min(Math.round((usedBytes / quotaBytes) * 100), 100) : 0; + // FIXME: use classes const barColor = percentage > 90 ? '#ef4444' : percentage > 70 ? '#f59e0b' : '#22c55e'; - const t = (key, fallback) => (i18n?.t ? i18n.t(key) || fallback : fallback); + const t = (key, fallback) => i18n.t(key) || fallback; const existing = document.getElementById('profile-modal-overlay'); if (existing) existing.remove(); diff --git a/static/js/core/languageSelector.js b/static/js/core/languageSelector.js index df012709..6468f545 100644 --- a/static/js/core/languageSelector.js +++ b/static/js/core/languageSelector.js @@ -66,7 +66,7 @@ function createLanguageSelector(containerId = 'language-selector') { // Get current language const languages = getAvailableLanguages(); - const currentLocale = i18n ? i18n.getCurrentLocale() : 'en'; + const currentLocale = i18n.getCurrentLocale(); const currentLang = languages.find((l) => l.code === currentLocale) || languages[0]; // Set initial HTML attributes @@ -185,10 +185,7 @@ function closeDropdown(container) { * Select a language */ async function selectLanguage(langCode, container) { - // Update i18n if available - if (i18n) { - await i18n.setLocale(langCode); - } + await i18n.setLocale(langCode); // Update HTML lang attribute and dir for RTL languages updateHtmlAttributes(langCode); diff --git a/static/js/core/modal.js b/static/js/core/modal.js index 6735c2ae..22f28522 100644 --- a/static/js/core/modal.js +++ b/static/js/core/modal.js @@ -108,16 +108,15 @@ const Modal = { this.input.placeholder = placeholder; this.input.value = value; - // Set button text (use i18n if available) if (confirmText) { this.confirmBtn.textContent = confirmText; - } else if (i18n) { + } else { this.confirmBtn.textContent = i18n.t('actions.confirm'); } if (cancelText) { this.cancelBtn.textContent = cancelText; - } else if (i18n) { + } else { this.cancelBtn.textContent = i18n.t('actions.cancel'); } @@ -138,7 +137,7 @@ const Modal = { * @returns {Promise} */ promptNewFolder() { - const t = i18n ? i18n.t.bind(i18n) : (k) => k; + const t = i18n.t.bind(i18n); return this.prompt({ title: t('dialogs.new_folder_title') || 'New folder', @@ -156,7 +155,7 @@ const Modal = { * @returns {Promise} */ promptRename(currentName, isFolder = false) { - const t = i18n ? i18n.t.bind(i18n) : (k) => k; + const t = i18n.t.bind(i18n); // For files, we want to select only the name part (without extension) this._selectNameOnly = !isFolder; diff --git a/static/js/core/notifications.js b/static/js/core/notifications.js index 59878ee6..4986653e 100644 --- a/static/js/core/notifications.js +++ b/static/js/core/notifications.js @@ -149,7 +149,7 @@ const notifications = (() => { item.className = 'notif-item'; item.id = batchId; - const t = i18n?.t || ((k) => k); + const t = i18n.t.bind(i18n); const uploadingText = folderName ? `πŸ“ ${t('upload.uploading')} ${_esc(folderName)}…` : t('upload.uploading'); const filesLabel = t('upload.files'); @@ -254,7 +254,7 @@ const notifications = (() => { const pctEl = $(`${batchId}-pct`); const statsEl = $(`${batchId}-stats`); - const t = i18n?.t || ((k) => k); + const t = i18n.t.bind(i18n); const filesLabel = t('upload.files'); if (fillEl) fillEl.style.width = `${pctVal}%`; @@ -282,7 +282,7 @@ const notifications = (() => { const curEl = $(`${batchId}-current`); if (curEl) curEl.textContent = ''; - const t = i18n?.t || ((k) => k); + const t = i18n.t.bind(i18n); const completeText = t('upload.complete', { count: successCount, total: totalFiles diff --git a/static/js/features/auth/auth.js b/static/js/features/auth/auth.js index 30c424f8..122ace65 100644 --- a/static/js/features/auth/auth.js +++ b/static/js/features/auth/auth.js @@ -511,10 +511,7 @@ function initLanguageSelector() { localStorage.setItem(LOCALE_KEY, selectedLanguage); localStorage.setItem(FIRST_RUN_KEY, 'true'); - // Update i18n if available - if (i18n?.setLocale) { - await i18n.setLocale(selectedLanguage); - } + await i18n.setLocale(selectedLanguage); // Hide language panel hidePanel(languagePanel); @@ -635,7 +632,7 @@ async function configureOidcLoginUI() { // Update button text with provider name const btnTextEl = oidcBtn.querySelector('span'); if (btnTextEl && oidcInfo.provider_name) { - const template = i18n?.t ? i18n.t('auth.sso_login_provider') : 'Sign in with {{provider}}'; + const template = i18n.t('auth.sso_login_provider'); btnTextEl.textContent = template.replace('{{provider}}', oidcInfo.provider_name); } @@ -942,8 +939,7 @@ if (isLoginPage && registerForm) { // Validate passwords match if (password !== confirmPassword) { - const errorMsg = i18n ? i18n.t('auth.passwords_mismatch') : 'Passwords do not match'; - registerError.textContent = errorMsg; + registerError.textContent = i18n.t('auth.passwords_mismatch'); registerError.style.display = 'block'; return; } @@ -951,9 +947,7 @@ if (isLoginPage && registerForm) { try { await register(username, email, password); - // Show success message - const successMsg = i18n ? i18n.t('auth.account_success') : 'Account created successfully! You can now log in.'; - registerSuccess.textContent = successMsg; + registerSuccess.textContent = i18n.t('auth.account_success'); registerSuccess.style.display = 'block'; // Clear form @@ -965,8 +959,7 @@ if (isLoginPage && registerForm) { hidePanel(registerPanel); }, 2000); } catch (error) { - const errorMsg = i18n ? i18n.t('auth.admin_create_error') : 'Error registering account'; - registerError.textContent = error.message || errorMsg; + registerError.textContent = error.message || i18n.t('auth.admin_create_error'); registerError.style.display = 'block'; } }); @@ -988,8 +981,7 @@ if (isLoginPage && adminSetupForm) { // Validate passwords match if (password !== confirmPassword) { - const errorMsg = i18n ? i18n.t('auth.passwords_mismatch') : 'Passwords do not match'; - adminSetupError.textContent = errorMsg; + adminSetupError.textContent = i18n.t('auth.passwords_mismatch'); adminSetupError.style.display = 'block'; return; } @@ -1012,11 +1004,8 @@ if (isLoginPage && adminSetupForm) { } await response.json(); - // Show success message in the GUI instead of alert - const successMsg = i18n ? i18n.t('auth.admin_success') : 'Admin account created successfully! You can now log in.'; - if (adminSetupSuccess) { - adminSetupSuccess.textContent = successMsg; + adminSetupSuccess.textContent = i18n.t('auth.admin_success'); adminSetupSuccess.style.display = 'block'; } @@ -1027,8 +1016,7 @@ if (isLoginPage && adminSetupForm) { if (adminSetupSuccess) adminSetupSuccess.style.display = 'none'; }, 2000); } catch (error) { - const errorMsg = i18n ? i18n.t('auth.admin_create_error') : 'Error creating admin account'; - adminSetupError.textContent = error.message || errorMsg; + adminSetupError.textContent = error.message || i18n.t('auth.admin_create_error'); adminSetupError.style.display = 'block'; } }); diff --git a/static/js/features/files/contextMenus.js b/static/js/features/files/contextMenus.js index d95218b6..d0eae153 100644 --- a/static/js/features/files/contextMenus.js +++ b/static/js/features/files/contextMenus.js @@ -27,7 +27,7 @@ const contextMenus = { if (!option) return; const label = option.querySelector('span'); if (!label) return; - label.textContent = i18n ? i18n.t(isFavorite ? 'actions.unfavorite' : 'actions.favorite') : isFavorite ? 'Remove from favorites' : 'Add to favorites'; + label.textContent = i18n.t(isFavorite ? 'actions.unfavorite' : 'actions.favorite'); }, /** @@ -382,7 +382,7 @@ const contextMenus = { renameInput.value = folder.name; // Update header text const headerSpan = renameDialog.querySelector('.rename-dialog-header span'); - if (headerSpan) headerSpan.textContent = i18n ? i18n.t('dialogs.rename_folder') : 'Rename folder'; + if (headerSpan) headerSpan.textContent = i18n.t('dialogs.rename_folder'); renameDialog?.classList.remove('hidden'); renameInput.focus(); renameInput.select(); @@ -402,7 +402,7 @@ const contextMenus = { renameInput.value = file.name; // Update header text const headerSpan = renameDialog.querySelector('.rename-dialog-header span'); - if (headerSpan) headerSpan.textContent = i18n ? i18n.t('dialogs.rename_file') : 'Rename file'; + if (headerSpan) headerSpan.textContent = i18n.t('dialogs.rename_file'); renameDialog?.classList.remove('hidden'); renameInput.focus(); renameInput.select(); @@ -471,7 +471,7 @@ const contextMenus = { // Update dialog title (preserve icon) const dialogHeader = document.getElementById('move-file-dialog').querySelector('.rename-dialog-header'); - const titleText = mode === 'file' ? (i18n ? i18n.t('dialogs.move_file') : 'Move file') : i18n ? i18n.t('dialogs.move_folder') : 'Move folder'; + const titleText = mode === 'file' ? i18n.t('dialogs.move_file') : i18n.t('dialogs.move_folder'); dialogHeader.innerHTML = ` ${titleText}`; // Load folders for the starting location @@ -496,7 +496,7 @@ const contextMenus = { async renameItem() { const newName = document.getElementById('rename-input').value.trim(); if (!newName) { - alert(i18n ? i18n.t('errors.empty_name') : 'Name cannot be empty'); + alert(i18n.t('errors.empty_name')); return; } @@ -587,7 +587,7 @@ const contextMenus = { currentFolderOption.className = 'folder-select-item folder-select-current'; currentFolderOption.innerHTML = ` - ${i18n ? i18n.t('dialogs.select_this_folder') : 'Select this folder'} + ${i18n.t('dialogs.select_this_folder')} `; currentFolderOption.addEventListener('click', () => { document.querySelectorAll('.folder-select-item').forEach((item) => { @@ -606,7 +606,7 @@ const contextMenus = { parentOption.className = 'folder-select-item folder-navigate-up'; parentOption.innerHTML = ` - ${i18n ? i18n.t('dialogs.go_to_parent') : '.. (parent folder)'} + ${i18n.t('dialogs.go_to_parent')} `; parentOption.addEventListener('click', () => { // Navigate to parent folder @@ -664,7 +664,7 @@ const contextMenus = { homeOption.className = 'folder-select-item folder-select-current'; homeOption.innerHTML = ` - ${i18n ? i18n.t('dialogs.move_to_home') : 'Move to Home folder'} + ${i18n.t('dialogs.move_to_home')} `; homeOption.addEventListener('click', () => { document.querySelectorAll('.folder-select-item').forEach((item) => { @@ -678,7 +678,7 @@ const contextMenus = { // Inside a subfolder with no children - show empty message const emptyMsg = document.createElement('div'); emptyMsg.className = 'folder-select-empty'; - emptyMsg.innerHTML = ` ${i18n ? i18n.t('dialogs.no_subfolders') : 'No subfolders to navigate'}`; + emptyMsg.innerHTML = ` ${i18n.t('dialogs.no_subfolders')}`; folderSelectContainer.appendChild(emptyMsg); } @@ -686,9 +686,7 @@ const contextMenus = { app.selectedTargetFolderId = parentFolderId || ''; // Translate new elements - if (i18n?.translateElement) { - i18n.translateElement(folderSelectContainer); - } + i18n.translateElement(folderSelectContainer); } catch (error) { console.error('Error loading folders:', error); } @@ -799,7 +797,7 @@ const contextMenus = { if (dialogHeader) { const headerSpan = dialogHeader.querySelector('span'); const titleText = - itemType === 'file' ? (i18n ? i18n.t('dialogs.share_file') : 'Share file') : i18n ? i18n.t('dialogs.share_folder') : 'Share folder'; + itemType === 'file' ? i18n.t('dialogs.share_file') : i18n.t('dialogs.share_folder'); if (headerSpan) { headerSpan.textContent = titleText; } else { @@ -900,9 +898,9 @@ const contextMenus = { const shareId = btn.getAttribute('data-share-id'); showConfirmDialog({ - title: i18n ? i18n.t('dialogs.confirm_delete_share') : 'Delete link', - message: i18n ? i18n.t('dialogs.confirm_delete_share_msg') : 'Are you sure you want to delete this shared link?', - confirmText: i18n ? i18n.t('actions.delete') : 'Delete' + title: i18n.t('dialogs.confirm_delete_share'), + message: i18n.t('dialogs.confirm_delete_share_msg'), + confirmText: i18n.t('actions.delete') }).then(async (confirmed) => { if (confirmed) { await fileSharing.removeSharedLink(shareId); @@ -998,8 +996,8 @@ const contextMenus = { // Show success message ui.showNotification( - i18n ? i18n.t('notifications.link_created') : 'Link created', - i18n ? i18n.t('notifications.share_success') : 'Shared link created successfully' + i18n.t('notifications.link_created'), + i18n.t('notifications.share_success') ); } catch (error) { console.error('Error creating shared link:', error); @@ -1088,7 +1086,7 @@ const contextMenus = { // Update files info if (filesInfo) { - filesInfo.innerHTML = `${i18n ? i18n.t('music.selected_files', 'Selected:') : 'Selected:'} ${file.name}`; + filesInfo.innerHTML = `${i18n.t('music.selected_files')} ${file.name}`; } // Reset selection @@ -1112,12 +1110,12 @@ const contextMenus = { this._renderPlaylistSelect(container, playlists); } catch (err) { console.error('Error loading playlists:', err); - container.innerHTML = `
${i18n ? i18n.t('music.load_error', 'Error loading playlists') : 'Error loading playlists'}
`; + container.innerHTML = `
${i18n.t('music.load_error')}
`; } }, _renderPlaylistSelect(container, playlists) { - const t = (key, fallback) => (i18n ? i18n.t(key, fallback) : fallback); + const t = i18n.t.bind(i18n); container.innerHTML = ''; @@ -1177,8 +1175,8 @@ const contextMenus = { await resp.json(); ui.showNotification( - i18n ? i18n.t('music.added', 'Added!') : 'Added!', - `${files.length} ${files.length === 1 ? 'track' : 'tracks'} ${i18n ? i18n.t('music.added_to_playlist', 'added to playlist') : 'added to playlist'}` + i18n.t('music.added'), + `${files.length} ${files.length === 1 ? 'track' : 'tracks'} ${i18n.t('music.added_to_playlist')}` ); this.closePlaylistDialog(); @@ -1189,7 +1187,7 @@ const contextMenus = { } } catch (err) { console.error('Error adding to playlist:', err); - ui.showNotification(i18n ? i18n.t('music.error', 'Error') : 'Error', err.message || i18n.t('music.add_error', 'Could not add tracks to playlist')); + ui.showNotification(i18n.t('music.error'), err.message || i18n.t('music.add_error')); if (addBtn) addBtn.disabled = false; } }, diff --git a/static/js/features/files/fileOperations.js b/static/js/features/files/fileOperations.js index 9f85102a..9c8530d9 100644 --- a/static/js/features/files/fileOperations.js +++ b/static/js/features/files/fileOperations.js @@ -30,12 +30,12 @@ const fileOps = { /** Start a new upload batch in the notification bell */ _initUploadToast(totalFiles, folderName) { - this._currentBatchId = notifications ? notifications.addUploadBatch(totalFiles, folderName) : null; + this._currentBatchId = notifications.addUploadBatch(totalFiles, folderName); }, /** Finalise the batch in the notification bell */ _finishUploadToast(successCount, totalFiles) { - if (notifications && this._currentBatchId) { + if (this._currentBatchId) { notifications.finishBatch(this._currentBatchId, successCount, totalFiles); } }, @@ -361,7 +361,7 @@ const fileOps = { progressBar.style.width = `${(uploadedCount / totalFiles) * 100}%`; } // Notify bell of per-file completion - if (notifications && batchId) { + if (batchId) { try { notifications.fileCompleted(batchId, result.ok); } catch (e) { @@ -383,7 +383,7 @@ const fileOps = { }); } if (result.isQuotaError) { - const msg = result.errorMsg || i18n?.t('storage_quota_exceeded') || 'Storage quota exceeded'; + const msg = result.errorMsg || i18n.t('storage_quota_exceeded'); if (notifications) { notifications.addNotification({ icon: 'fa-exclamation-triangle', @@ -591,7 +591,7 @@ const fileOps = { console.warn(`[SKIP] #${idx} ${rel} β€” cannot read 0-byte file (FIFO/pipe?), skipping`); uploadedCount++; successCount++; - if (notifications && batchId) { + if (batchId) { try { notifications.fileCompleted(batchId, true); } catch (_) {} @@ -623,7 +623,7 @@ const fileOps = { uploadedCount++; - if (notifications && batchId) { + if (batchId) { try { notifications.fileCompleted(batchId, result.ok); } catch (_) {} @@ -995,8 +995,8 @@ const fileOps = { if (response.ok) { ui.showNotification( - i18n ? i18n.t('notifications.file_renamed') : 'File renamed', - i18n ? i18n.t('notifications.file_renamed_to', { name: newName }) : `File renamed to "${newName}"` + i18n.t('notifications.file_renamed'), + i18n.t('notifications.file_renamed_to', { name: newName }) ); return true; } else { @@ -1075,9 +1075,9 @@ const fileOps = { */ async deleteFile(fileId, fileName) { const confirmed = await showConfirmDialog({ - title: i18n ? i18n.t('dialogs.confirm_delete') : 'Move to trash', - message: i18n ? i18n.t('dialogs.confirm_delete_file', { name: fileName }) : `Are you sure you want to move the file "${fileName}" to trash?`, - confirmText: i18n ? i18n.t('actions.delete') : 'Delete' + title: i18n.t('dialogs.confirm_delete'), + message: i18n.t('dialogs.confirm_delete_file', { name: fileName }), + confirmText: i18n.t('actions.delete') }); if (!confirmed) return false; @@ -1123,11 +1123,9 @@ const fileOps = { */ async deleteFolder(folderId, folderName) { const confirmed = await showConfirmDialog({ - title: i18n ? i18n.t('dialogs.confirm_delete') : 'Move to trash', - message: i18n - ? i18n.t('dialogs.confirm_delete_folder', { name: folderName }) - : `Are you sure you want to move the folder "${folderName}" and all its contents to trash?`, - confirmText: i18n ? i18n.t('actions.delete') : 'Delete' + title: i18n.t('dialogs.confirm_delete'), + message: i18n.t('dialogs.confirm_delete_folder', { name: folderName }), + confirmText: i18n.t('actions.delete') }); if (!confirmed) return false; @@ -1234,11 +1232,9 @@ const fileOps = { */ async deletePermanently(trashId) { const confirmed = await showConfirmDialog({ - title: i18n ? i18n.t('dialogs.confirm_permanent_delete') : 'Delete permanently', - message: i18n - ? i18n.t('dialogs.confirm_permanent_delete_msg') - : 'Are you sure you want to permanently delete this item? This action cannot be undone.', - confirmText: i18n ? i18n.t('actions.delete_permanently') : 'Delete permanently' + title: i18n.t('dialogs.confirm_permanent_delete'), + message: i18n.t('dialogs.confirm_permanent_delete_msg'), + confirmText: i18n.t('actions.delete_permanently') }); if (!confirmed) return false; @@ -1268,9 +1264,9 @@ const fileOps = { */ async emptyTrash() { const confirmed = await showConfirmDialog({ - title: i18n ? i18n.t('dialogs.confirm_empty_trash') : 'Empty trash', - message: i18n ? i18n.t('trash.empty_confirm') : 'Are you sure you want to empty the trash? This action will permanently delete all items.', - confirmText: i18n ? i18n.t('actions.empty_trash') : 'Empty trash' + title: i18n.t('dialogs.confirm_empty_trash'), + message: i18n.t('trash.empty_confirm'), + confirmText: i18n.t('actions.empty_trash') }); if (!confirmed) return false; diff --git a/static/js/features/files/multiSelect.js b/static/js/features/files/multiSelect.js index a3a4a56a..f77194cd 100644 --- a/static/js/features/files/multiSelect.js +++ b/static/js/features/files/multiSelect.js @@ -50,12 +50,8 @@ const multiSelect = { // ── Helpers for i18n ──────────────────────────────────── _t(key, vars) { - if (i18n && typeof i18n.t === 'function') { - const val = i18n.t(key, vars); - // If i18n returned the key itself, it's missing β†’ fall back - if (val && val !== key) return val; - } - return null; + const val = i18n.t(key, vars); + return val !== key ? val : null; }, // ── Selection state management ────────────────────────── diff --git a/static/js/features/library/favorites.js b/static/js/features/library/favorites.js index 43fe53be..7ce2a490 100644 --- a/static/js/features/library/favorites.js +++ b/static/js/features/library/favorites.js @@ -111,8 +111,8 @@ const favorites = { // Notify user if (ui?.showNotification) { ui.showNotification( - i18n ? i18n.t('favorites.added_title') : 'Added to favorites', - `"${name}" ${i18n ? i18n.t('favorites.added_msg') : 'added to favorites'}` + i18n.t('favorites.added_title'), + `"${name}" ${i18n.t('favorites.added_msg')}` ); } @@ -146,8 +146,8 @@ const favorites = { if (ui?.showNotification) { ui.showNotification( - i18n ? i18n.t('favorites.removed_title') : 'Removed from favorites', - `"${itemName}" ${i18n ? i18n.t('favorites.removed_msg') : 'removed from favorites'}` + i18n.t('favorites.removed_title'), + `"${itemName}" ${i18n.t('favorites.removed_msg')}` ); } @@ -182,8 +182,8 @@ const favorites = { if (this._cache.size === 0) { ui.showError(` -

${i18n ? i18n.t('favorites.empty_state') : 'No favorite items'}

-

${i18n ? i18n.t('favorites.empty_hint') : 'To mark as favorite, right-click on any file or folder'}

+

${i18n.t('favorites.empty_state')}

+

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

`); return; } diff --git a/static/js/features/library/music.js b/static/js/features/library/music.js index 456ec605..f002efac 100644 --- a/static/js/features/library/music.js +++ b/static/js/features/library/music.js @@ -186,9 +186,7 @@ const musicView = { const listEl = document.getElementById('music-playlist-list'); if (!listEl) return; - const t = (key, fallback = '') => { - return i18n?.t ? i18n.t(key) : fallback || key; - }; + const t = (key) => i18n.t(key); if (this.playlists.length === 0) { listEl.innerHTML = ` @@ -291,9 +289,7 @@ const musicView = { if (detailEl) detailEl.classList.remove('hidden'); if (nameEl) nameEl.textContent = playlist.name; if (metaEl) { - const t = (key, fallback = '') => { - return i18n?.t ? i18n.t(key) : fallback || key; - }; + const t = (key) => i18n.t(key); metaEl.textContent = `${playlist.track_count || 0} ${t('music.tracks', 'tracks')}`; } @@ -314,7 +310,7 @@ const musicView = { } const togglePublicBtn = document.getElementById('music-toggle-public-btn'); if (togglePublicBtn) { - const t2 = (key, fallback = '') => (i18n?.t ? i18n.t(key) : fallback || key); + const t2 = (key) => i18n.t(key); togglePublicBtn.title = playlist.is_public ? t2('music.make_private', 'Make private') : t2('music.make_public', 'Make public'); togglePublicBtn.classList.toggle('active', playlist.is_public); } @@ -352,9 +348,7 @@ const musicView = { const trackListEl = document.getElementById('music-track-list'); if (!trackListEl) return; - const t = (key, fallback = '') => { - return i18n?.t ? i18n.t(key) : fallback || key; - }; + const t = (key) => i18n.t(key); if (this.currentTracks.length === 0) { trackListEl.innerHTML = ` @@ -467,9 +461,7 @@ const musicView = { _playTrack(idx) { if (!this.currentTracks[idx]) return; - const t = (key, fallback = '') => { - return i18n?.t ? i18n.t(key) : fallback || key; - }; + const t = (key) => i18n.t(key); musicPlayer.setQueue(this.currentTracks, this.currentPlaylist?.name || t('music.playlists', 'Playlist')); musicPlayer.playTrack(idx); }, @@ -487,18 +479,14 @@ const musicView = { const j = Math.floor(Math.random() * (i + 1)); [shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]]; } - const t = (key, fallback = '') => { - return i18n?.t ? i18n.t(key) : fallback || key; - }; + const t = (key) => i18n.t(key); musicPlayer.setQueue(shuffled, this.currentPlaylist?.name || t('music.shuffle', 'Shuffle')); musicPlayer.playTrack(0); } }, async _showCreatePlaylistDialog() { - const t = (key, fallback = '') => { - return i18n?.t ? i18n.t(key) : fallback || key; - }; + const t = (key) => i18n.t(key); const name = await Modal.prompt({ title: t('music.create_playlist', 'Create Playlist'), @@ -513,9 +501,7 @@ const musicView = { }, async _createPlaylist(name) { - const t = (key, fallback = '') => { - return i18n?.t ? i18n.t(key) : fallback || key; - }; + const t = (key) => i18n.t(key); const createBtn = document.getElementById('music-create-playlist-btn'); if (createBtn) createBtn.disabled = true; try { @@ -556,9 +542,7 @@ const musicView = { }, async _deletePlaylist() { - const t = (key, fallback = '') => { - return i18n?.t ? i18n.t(key) : fallback || key; - }; + const t = (key) => i18n.t(key); if (!this.currentPlaylist) return; @@ -644,9 +628,7 @@ const musicView = { async _showEditPlaylistDialog() { if (!this.currentPlaylist) return; - const t = (key, fallback = '') => { - return i18n?.t ? i18n.t(key) : fallback || key; - }; + const t = (key) => i18n.t(key); const newName = await Modal.prompt({ title: t('music.edit', 'Edit'), @@ -690,9 +672,7 @@ const musicView = { async _showSharePlaylistDialog() { if (!this.currentPlaylist) return; - const t = (key, fallback = '') => { - return i18n?.t ? i18n.t(key) : fallback || key; - }; + const t = (key) => i18n.t(key); const userId = await Modal.prompt({ title: t('music.share', 'Share'), @@ -736,9 +716,7 @@ const musicView = { async _showAddTracksDialog() { if (!this.currentPlaylist) return; - const t = (key, fallback = '') => { - return i18n?.t ? i18n.t(key) : fallback || key; - }; + const t = (key) => i18n.t(key); // ── Build modal overlay ── const overlay = document.createElement('div'); @@ -900,7 +878,7 @@ const musicView = { async _removeTrackFromPlaylist(_trackId, fileId) { if (!this.currentPlaylist) return; - const t = (key, fallback = '') => (i18n?.t ? i18n.t(key) : fallback || key); + const t = (key) => i18n.t(key); try { const resp = await fetch(`/api/playlists/${this.currentPlaylist.id}/tracks/${encodeURIComponent(fileId)}`, { @@ -942,7 +920,7 @@ const musicView = { async _reorderTrack(fromIdx, toIdx) { if (!this.currentPlaylist) return; - const t = (key, fallback = '') => (i18n?.t ? i18n.t(key) : fallback || key); + const t = (key) => i18n.t(key); const tracks = [...this.currentTracks]; const [moved] = tracks.splice(fromIdx, 1); @@ -975,7 +953,7 @@ const musicView = { async _showManageSharesDialog() { if (!this.currentPlaylist) return; - const t = (key, fallback = '') => (i18n?.t ? i18n.t(key) : fallback || key); + const t = (key) => i18n.t(key); const existing = document.getElementById('music-shares-dialog'); if (existing) existing.remove(); @@ -1052,7 +1030,7 @@ const musicView = { async _loadSharesList(dialog) { if (!this.currentPlaylist) return; - const t = (key, fallback = '') => (i18n?.t ? i18n.t(key) : fallback || key); + const t = (key) => i18n.t(key); const body = dialog.querySelector('.music-shares-body'); if (!body) return; @@ -1097,7 +1075,7 @@ const musicView = { async _removeShare(userId, dialog) { if (!this.currentPlaylist) return; - const t = (key, fallback = '') => (i18n?.t ? i18n.t(key) : fallback || key); + const t = (key) => i18n.t(key); try { const resp = await fetch(`/api/playlists/${this.currentPlaylist.id}/share/${encodeURIComponent(userId)}`, { @@ -1121,7 +1099,7 @@ const musicView = { async _togglePublic() { if (!this.currentPlaylist) return; - const t = (key, fallback = '') => (i18n?.t ? i18n.t(key) : fallback || key); + const t = (key) => i18n.t(key); const newValue = !this.currentPlaylist.is_public; try { @@ -1170,7 +1148,7 @@ const musicView = { async _showCoverPicker() { if (!this.currentPlaylist) return; - const t = (key, fallback = '') => (i18n?.t ? i18n.t(key) : fallback || key); + const t = (key) => i18n.t(key); const input = document.createElement('input'); input.type = 'file'; @@ -1289,25 +1267,25 @@ const musicPlayer = {
- ${i18n?.t('music.not_playing', 'Not playing') || 'Not playing'} + ${i18n.t('music.not_playing')}
- - - - -
@@ -1321,22 +1299,22 @@ const musicPlayer = {
- -
-