Added translations and data attributes for almost all user-facing text

This commit is contained in:
Ryan
2025-04-13 15:22:52 -04:00
committed by GitHub
parent 0683b27534
commit 97559873dc
6 changed files with 62 additions and 27 deletions

View File

@@ -7,6 +7,9 @@
- main.js added Dark & Light mode i18n.js keys. - main.js added Dark & Light mode i18n.js keys.
- New Admin section Header Settings to change Header Title. - New Admin section Header Settings to change Header Title.
- Admin Panel confirm unsaved changes. - Admin Panel confirm unsaved changes.
- Added translations and data attributes for almost all user-facing text
---
## Changes 4/12/2025 ## Changes 4/12/2025

View File

@@ -1,5 +1,5 @@
import { sendRequest } from './networkUtils.js'; import { sendRequest } from './networkUtils.js';
import { t } from './i18n.js'; import { t, applyTranslations } from './i18n.js';
import { import {
toggleVisibility, toggleVisibility,
showToast as originalShowToast, showToast as originalShowToast,
@@ -164,7 +164,8 @@ function updateAuthenticatedUI(data) {
restoreBtn = document.createElement("button"); restoreBtn = document.createElement("button");
restoreBtn.id = "restoreFilesBtn"; restoreBtn.id = "restoreFilesBtn";
restoreBtn.classList.add("btn", "btn-warning"); restoreBtn.classList.add("btn", "btn-warning");
restoreBtn.innerHTML = '<i class="material-icons" title="Restore/Delete Trash">restore_from_trash</i>'; restoreBtn.setAttribute("data-i18n-title", "trash_restore_delete");
restoreBtn.innerHTML = '<i class="material-icons">restore_from_trash</i>';
if (firstButton) insertAfter(restoreBtn, firstButton); if (firstButton) insertAfter(restoreBtn, firstButton);
else headerButtons.appendChild(restoreBtn); else headerButtons.appendChild(restoreBtn);
} }
@@ -175,7 +176,8 @@ function updateAuthenticatedUI(data) {
adminPanelBtn = document.createElement("button"); adminPanelBtn = document.createElement("button");
adminPanelBtn.id = "adminPanelBtn"; adminPanelBtn.id = "adminPanelBtn";
adminPanelBtn.classList.add("btn", "btn-info"); adminPanelBtn.classList.add("btn", "btn-info");
adminPanelBtn.innerHTML = '<i class="material-icons" title="Admin Panel">admin_panel_settings</i>'; adminPanelBtn.setAttribute("data-i18n-title", "admin_panel");
adminPanelBtn.innerHTML = '<i class="material-icons">admin_panel_settings</i>';
insertAfter(adminPanelBtn, restoreBtn); insertAfter(adminPanelBtn, restoreBtn);
adminPanelBtn.addEventListener("click", openAdminPanel); adminPanelBtn.addEventListener("click", openAdminPanel);
} else { } else {
@@ -194,7 +196,9 @@ function updateAuthenticatedUI(data) {
userPanelBtn = document.createElement("button"); userPanelBtn = document.createElement("button");
userPanelBtn.id = "userPanelBtn"; userPanelBtn.id = "userPanelBtn";
userPanelBtn.classList.add("btn", "btn-user"); userPanelBtn.classList.add("btn", "btn-user");
userPanelBtn.innerHTML = '<i class="material-icons" title="User Panel">account_circle</i>'; userPanelBtn.setAttribute("data-i18n-title", "user_panel");
userPanelBtn.innerHTML = '<i class="material-icons">account_circle</i>';
const adminBtn = document.getElementById("adminPanelBtn"); const adminBtn = document.getElementById("adminPanelBtn");
if (adminBtn) insertAfter(userPanelBtn, adminBtn); if (adminBtn) insertAfter(userPanelBtn, adminBtn);
else if (firstButton) insertAfter(userPanelBtn, firstButton); else if (firstButton) insertAfter(userPanelBtn, firstButton);
@@ -204,7 +208,7 @@ function updateAuthenticatedUI(data) {
userPanelBtn.style.display = "block"; userPanelBtn.style.display = "block";
} }
} }
applyTranslations();
updateItemsPerPageSelect(); updateItemsPerPageSelect();
updateLoginOptionsUIFromStorage(); updateLoginOptionsUIFromStorage();
} }

View File

@@ -811,7 +811,7 @@ export function openAdminPanel() {
export async function closeAdminPanel() { export async function closeAdminPanel() {
if (hasUnsavedChanges()) { if (hasUnsavedChanges()) {
const userConfirmed = await showCustomConfirmModal("You have unsaved changes. Are you sure you want to close without saving?"); const userConfirmed = await showCustomConfirmModal(t("unsaved_changes_confirm"));
if (!userConfirmed) { if (!userConfirmed) {
return; return;
} }

View File

@@ -117,9 +117,9 @@ export function buildSearchAndPaginationControls({ currentPage, totalPages, sear
</div> </div>
<div class="col-12 col-md-4 text-left"> <div class="col-12 col-md-4 text-left">
<div class="d-flex justify-content-center justify-content-md-start align-items-center"> <div class="d-flex justify-content-center justify-content-md-start align-items-center">
<button class="custom-prev-next-btn" ${currentPage === 1 ? "disabled" : ""} onclick="changePage(${currentPage - 1})">Prev</button> <button class="custom-prev-next-btn" ${currentPage === 1 ? "disabled" : ""} onclick="changePage(${currentPage - 1})">${t("prev")}</button>
<span class="page-indicator">Page ${currentPage} of ${totalPages || 1}</span> <span class="page-indicator">${t("page")} ${currentPage} ${t("of")} ${totalPages || 1}</span>
<button class="custom-prev-next-btn" ${currentPage === totalPages ? "disabled" : ""} onclick="changePage(${currentPage + 1})">Next</button> <button class="custom-prev-next-btn" ${currentPage === totalPages ? "disabled" : ""} onclick="changePage(${currentPage + 1})">${t("next")}</button>
</div> </div>
</div> </div>
</div> </div>
@@ -137,7 +137,7 @@ export function buildFileTableHeader(sortOrder) {
<th data-column="uploaded" class="hide-small hide-medium sortable-col">${t("upload_date")} ${sortOrder.column === "uploaded" ? (sortOrder.ascending ? "▲" : "▼") : ""}</th> <th data-column="uploaded" class="hide-small hide-medium sortable-col">${t("upload_date")} ${sortOrder.column === "uploaded" ? (sortOrder.ascending ? "▲" : "▼") : ""}</th>
<th data-column="size" class="hide-small sortable-col">${t("file_size")} ${sortOrder.column === "size" ? (sortOrder.ascending ? "▲" : "▼") : ""}</th> <th data-column="size" class="hide-small sortable-col">${t("file_size")} ${sortOrder.column === "size" ? (sortOrder.ascending ? "▲" : "▼") : ""}</th>
<th data-column="uploader" class="hide-small hide-medium sortable-col">${t("uploader")} ${sortOrder.column === "uploader" ? (sortOrder.ascending ? "▲" : "▼") : ""}</th> <th data-column="uploader" class="hide-small hide-medium sortable-col">${t("uploader")} ${sortOrder.column === "uploader" ? (sortOrder.ascending ? "▲" : "▼") : ""}</th>
<th>Actions</th> <th>${t("actions")}</th>
</tr> </tr>
</thead> </thead>
`; `;
@@ -181,36 +181,38 @@ export function buildFileTableRow(file, folderPath) {
<div class="button-wrap" style="display: flex; justify-content: left; gap: 5px;"> <div class="button-wrap" style="display: flex; justify-content: left; gap: 5px;">
<button type="button" class="btn btn-sm btn-success download-btn" <button type="button" class="btn btn-sm btn-success download-btn"
onclick="openDownloadModal('${file.name}', '${file.folder || 'root'}')" onclick="openDownloadModal('${file.name}', '${file.folder || 'root'}')"
title="Download"> title="${t('download')}">
<i class="material-icons">file_download</i> <i class="material-icons">file_download</i>
</button> </button>
${file.editable ? ` ${file.editable ? `
<button class="btn btn-sm edit-btn" <button class="btn btn-sm edit-btn"
onclick='editFile(${JSON.stringify(file.name)}, ${JSON.stringify(file.folder || "root")})' onclick='editFile(${JSON.stringify(file.name)}, ${JSON.stringify(file.folder || "root")})'
title="Edit"> title="${t('edit')}">
<i class="material-icons">edit</i> <i class="material-icons">edit</i>
</button> </button>
` : ""} ` : ""}
${previewButton} ${previewButton}
<button class="btn btn-sm btn-warning rename-btn" <button class="btn btn-sm btn-warning rename-btn"
onclick='renameFile(${JSON.stringify(file.name)}, ${JSON.stringify(file.folder || "root")})' onclick='renameFile(${JSON.stringify(file.name)}, ${JSON.stringify(file.folder || "root")})'
title="Rename"> title="${t('rename')}">
<i class="material-icons">drive_file_rename_outline</i> <i class="material-icons">drive_file_rename_outline</i>
</button> </button>
</div> </div>
</td> </td>
</tr> </tr>
`; `;
} }
export function buildBottomControls(itemsPerPageSetting) { export function buildBottomControls(itemsPerPageSetting) {
return ` return `
<div class="d-flex align-items-center mt-3 bottom-controls"> <div class="d-flex align-items-center mt-3 bottom-controls">
<label class="label-inline mr-2 mb-0">Show</label> <label class="label-inline mr-2 mb-0">${t("show")}</label>
<select class="form-control bottom-select" onchange="changeItemsPerPage(this.value)"> <select class="form-control bottom-select" onchange="changeItemsPerPage(this.value)">
${[10, 20, 50, 100].map(num => `<option value="${num}" ${num === itemsPerPageSetting ? "selected" : ""}>${num}</option>`).join("")} ${[10, 20, 50, 100]
.map(num => `<option value="${num}" ${num === itemsPerPageSetting ? "selected" : ""}>${num}</option>`)
.join("")}
</select> </select>
<span class="items-per-page-text ml-2 mb-0">items per page</span> <span class="items-per-page-text ml-2 mb-0">${t("items_per_page")}</span>
</div> </div>
`; `;
} }

View File

@@ -73,7 +73,7 @@ function buildFolderSummary(filteredFiles) {
return sum + parseSizeToBytes(file.size); return sum + parseSizeToBytes(file.size);
}, 0); }, 0);
const sizeStr = formatSize(totalBytes); const sizeStr = formatSize(totalBytes);
return `<strong>Total Files:</strong> ${totalFiles} &nbsp;|&nbsp; <strong>Total Size:</strong> ${sizeStr}`; return `<strong>${t('total_files')}:</strong> ${totalFiles} &nbsp;|&nbsp; <strong>${t('total_size')}:</strong> ${sizeStr}`;
} }
/** /**
@@ -316,7 +316,7 @@ export function renderFileTable(folder, container) {
rowHTML = rowHTML.replace(/(<td class="file-name-cell">)(.*?)(<\/td>)/, (match, p1, p2, p3) => { rowHTML = rowHTML.replace(/(<td class="file-name-cell">)(.*?)(<\/td>)/, (match, p1, p2, p3) => {
return p1 + p2 + tagBadgesHTML + p3; return p1 + p2 + tagBadgesHTML + p3;
}); });
rowHTML = rowHTML.replace(/(<\/div>\s*<\/td>\s*<\/tr>)/, `<button class="share-btn btn btn-sm btn-secondary" data-file="${escapeHTML(file.name)}" title="Share"> rowHTML = rowHTML.replace(/(<\/div>\s*<\/td>\s*<\/tr>)/, `<button class="share-btn btn btn-sm btn-secondary" data-file="${escapeHTML(file.name)}" title="${t('share')}">
<i class="material-icons">share</i> <i class="material-icons">share</i>
</button>$1`); </button>$1`);
rowsHTML += rowHTML; rowsHTML += rowHTML;
@@ -425,18 +425,18 @@ export function renderGalleryView(folder, container) {
<div class="button-wrap" style="display: flex; justify-content: center; gap: 5px;"> <div class="button-wrap" style="display: flex; justify-content: center; gap: 5px;">
<button type="button" class="btn btn-sm btn-success download-btn" <button type="button" class="btn btn-sm btn-success download-btn"
onclick="openDownloadModal('${file.name}', '${file.folder || 'root'}')" onclick="openDownloadModal('${file.name}', '${file.folder || 'root'}')"
title="Download"> title="${t('download')}">
<i class="material-icons">file_download</i> <i class="material-icons">file_download</i>
</button> </button>
${file.editable ? ` ${file.editable ? `
<button class="btn btn-sm edit-btn" onclick='editFile(${JSON.stringify(file.name)}, ${JSON.stringify(file.folder || "root")})' title="Edit"> <button class="btn btn-sm edit-btn" onclick='editFile(${JSON.stringify(file.name)}, ${JSON.stringify(file.folder || "root")})' title="${t('Edit')}">
<i class="material-icons">edit</i> <i class="material-icons">edit</i>
</button> </button>
` : ""} ` : ""}
<button class="btn btn-sm btn-warning rename-btn" onclick='renameFile(${JSON.stringify(file.name)}, ${JSON.stringify(file.folder || "root")})' title="Rename"> <button class="btn btn-sm btn-warning rename-btn" onclick='renameFile(${JSON.stringify(file.name)}, ${JSON.stringify(file.folder || "root")})' title="${t('rename')}">
<i class="material-icons">drive_file_rename_outline</i> <i class="material-icons">drive_file_rename_outline</i>
</button> </button>
<button class="btn btn-sm btn-secondary share-btn" data-file="${escapeHTML(file.name)}" title="Share"> <button class="btn btn-sm btn-secondary share-btn" data-file="${escapeHTML(file.name)}" title="${t('share')}">
<i class="material-icons">share</i> <i class="material-icons">share</i>
</button> </button>
</div> </div>

View File

@@ -85,6 +85,7 @@ const translations = {
"folder_help_item_4": "To rename or delete a folder, select it and then click the appropriate button.", "folder_help_item_4": "To rename or delete a folder, select it and then click the appropriate button.",
// File List keys: // File List keys:
"actions": "Actions",
"file_list_title": "Files in (Root)", "file_list_title": "Files in (Root)",
"files_in": "Files in", "files_in": "Files in",
"delete_files": "Delete Files", "delete_files": "Delete Files",
@@ -101,6 +102,13 @@ const translations = {
"download_zip_title": "Download Selected Files as Zip", "download_zip_title": "Download Selected Files as Zip",
"download_zip_prompt": "Enter a name for the zip file:", "download_zip_prompt": "Enter a name for the zip file:",
"zip_placeholder": "files.zip", "zip_placeholder": "files.zip",
"share": "Share",
"total_files": "Total Files",
"total_size": "Total Size",
"prev": "Prev",
"next": "Next",
"page": "Page",
"of": "of",
// Login Form keys: // Login Form keys:
"login": "Login", "login": "Login",
@@ -142,6 +150,7 @@ const translations = {
// Custom Confirm Modal keys: // Custom Confirm Modal keys:
"yes": "Yes", "yes": "Yes",
"no": "No", "no": "No",
"unsaved_changes_confirm": "You have unsaved changes. Are you sure you want to close without saving?",
"delete": "Delete", "delete": "Delete",
"download": "Download", "download": "Download",
"upload": "Upload", "upload": "Upload",
@@ -164,6 +173,7 @@ const translations = {
// NEW KEYS ADDED FOR ADMIN, USER PANELS, AND TOTP MODALS: // NEW KEYS ADDED FOR ADMIN, USER PANELS, AND TOTP MODALS:
"admin_panel": "Admin Panel", "admin_panel": "Admin Panel",
"user_panel": "User Panel", "user_panel": "User Panel",
"trash_restore_delete": "Trash Restore/Delete",
"totp_settings": "TOTP Settings", "totp_settings": "TOTP Settings",
"enable_totp": "Enable TOTP", "enable_totp": "Enable TOTP",
"language": "Language", "language": "Language",
@@ -217,7 +227,9 @@ const translations = {
"save_permissions": "Save Permissions", "save_permissions": "Save Permissions",
"your_recovery_code": "Your Recovery Code", "your_recovery_code": "Your Recovery Code",
"please_save_recovery_code": "Please save this code securely. It will not be shown again and can only be used once.", "please_save_recovery_code": "Please save this code securely. It will not be shown again and can only be used once.",
"ok": "OK" "ok": "OK",
"show": "Show",
"items_per_page": "items per page"
}, },
es: { es: {
"please_log_in_to_continue": "Por favor, inicie sesión para continuar.", "please_log_in_to_continue": "Por favor, inicie sesión para continuar.",
@@ -361,6 +373,7 @@ const translations = {
// Custom Confirm Modal keys: // Custom Confirm Modal keys:
"yes": "Sí", "yes": "Sí",
"no": "No", "no": "No",
"unsaved_changes_confirm": "Tiene cambios sin guardar. ¿Está seguro de que desea cerrar sin guardar?",
"delete": "Eliminar", "delete": "Eliminar",
"download": "Descargar", "download": "Descargar",
"upload": "Cargar", "upload": "Cargar",
@@ -580,6 +593,7 @@ const translations = {
// Custom Confirm Modal keys: // Custom Confirm Modal keys:
"yes": "Oui", "yes": "Oui",
"no": "Non", "no": "Non",
"unsaved_changes_confirm": "Vous avez des modifications non enregistrées. Êtes-vous sûr de vouloir fermer sans enregistrer ?",
"delete": "Supprimer", "delete": "Supprimer",
"download": "Télécharger", "download": "Télécharger",
"upload": "Téléverser", "upload": "Téléverser",
@@ -742,6 +756,7 @@ const translations = {
"folder_help_item_4": "Um einen Ordner umzubenennen oder zu löschen, wählen Sie ihn aus und klicken Sie auf den entsprechenden Button.", "folder_help_item_4": "Um einen Ordner umzubenennen oder zu löschen, wählen Sie ihn aus und klicken Sie auf den entsprechenden Button.",
// File List keys: // File List keys:
"actions": "Aktionen",
"file_list_title": "Dateien in (Root)", "file_list_title": "Dateien in (Root)",
"files_in": "Dateien in", "files_in": "Dateien in",
"delete_files": "Dateien löschen", "delete_files": "Dateien löschen",
@@ -758,6 +773,13 @@ const translations = {
"download_zip_title": "Ausgewählte Dateien als Zip herunterladen", "download_zip_title": "Ausgewählte Dateien als Zip herunterladen",
"download_zip_prompt": "Geben Sie einen Namen für die Zip-Datei ein:", "download_zip_prompt": "Geben Sie einen Namen für die Zip-Datei ein:",
"zip_placeholder": "files.zip", "zip_placeholder": "files.zip",
"share": "Teilen",
"total_files": "Gesamtanzahl",
"total_size": "Gesamtgröße",
"prev": "Zurück",
"next": "Weiter",
"page": "Seite",
"of": "von",
// Login Form keys: // Login Form keys:
"login": "Anmelden", "login": "Anmelden",
@@ -799,6 +821,7 @@ const translations = {
// Custom Confirm Modal keys: // Custom Confirm Modal keys:
"yes": "Ja", "yes": "Ja",
"no": "Nein", "no": "Nein",
"unsaved_changes_confirm": "Sie haben ungespeicherte Änderungen. Sind Sie sicher, dass Sie schließen möchten, ohne zu speichern?",
"delete": "Löschen", "delete": "Löschen",
"download": "Herunterladen", "download": "Herunterladen",
"upload": "Hochladen", "upload": "Hochladen",
@@ -821,6 +844,7 @@ const translations = {
// NEW KEYS ADDED FOR ADMIN, USER PANELS, AND TOTP MODALS: // NEW KEYS ADDED FOR ADMIN, USER PANELS, AND TOTP MODALS:
"admin_panel": "Administrationsbereich", "admin_panel": "Administrationsbereich",
"user_panel": "Benutzerbereich", "user_panel": "Benutzerbereich",
"trash_restore_delete": "Papierkorb wiederherstellen/löschen",
"totp_settings": "TOTP-Einstellungen", "totp_settings": "TOTP-Einstellungen",
"enable_totp": "TOTP aktivieren", "enable_totp": "TOTP aktivieren",
"language": "Sprache", "language": "Sprache",
@@ -874,7 +898,9 @@ const translations = {
"save_permissions": "Berechtigungen speichern", "save_permissions": "Berechtigungen speichern",
"your_recovery_code": "Ihr Wiederherstellungscode", "your_recovery_code": "Ihr Wiederherstellungscode",
"please_save_recovery_code": "Bitte speichern Sie diesen Code sicher. Er wird nicht erneut angezeigt und kann nur einmal verwendet werden.", "please_save_recovery_code": "Bitte speichern Sie diesen Code sicher. Er wird nicht erneut angezeigt und kann nur einmal verwendet werden.",
"ok": "OK" "ok": "OK",
"show": "Zeige",
"items_per_page": "elemente pro seite"
} }
}; };