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.
- New Admin section Header Settings to change Header Title.
- Admin Panel confirm unsaved changes.
- Added translations and data attributes for almost all user-facing text
---
## Changes 4/12/2025

View File

@@ -1,5 +1,5 @@
import { sendRequest } from './networkUtils.js';
import { t } from './i18n.js';
import { t, applyTranslations } from './i18n.js';
import {
toggleVisibility,
showToast as originalShowToast,
@@ -164,7 +164,8 @@ function updateAuthenticatedUI(data) {
restoreBtn = document.createElement("button");
restoreBtn.id = "restoreFilesBtn";
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);
else headerButtons.appendChild(restoreBtn);
}
@@ -175,7 +176,8 @@ function updateAuthenticatedUI(data) {
adminPanelBtn = document.createElement("button");
adminPanelBtn.id = "adminPanelBtn";
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);
adminPanelBtn.addEventListener("click", openAdminPanel);
} else {
@@ -194,17 +196,19 @@ function updateAuthenticatedUI(data) {
userPanelBtn = document.createElement("button");
userPanelBtn.id = "userPanelBtn";
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");
if (adminBtn) insertAfter(userPanelBtn, adminBtn);
else if (firstButton) insertAfter(userPanelBtn, firstButton);
else headerButtons.appendChild(userPanelBtn);
else headerButtons.appendChild(userPanelBtn);
userPanelBtn.addEventListener("click", openUserPanel);
} else {
userPanelBtn.style.display = "block";
}
}
applyTranslations();
updateItemsPerPageSelect();
updateLoginOptionsUIFromStorage();
}

View File

@@ -811,7 +811,7 @@ export function openAdminPanel() {
export async function closeAdminPanel() {
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) {
return;
}

View File

@@ -117,9 +117,9 @@ export function buildSearchAndPaginationControls({ currentPage, totalPages, sear
</div>
<div class="col-12 col-md-4 text-left">
<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>
<span class="page-indicator">Page ${currentPage} 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 === 1 ? "disabled" : ""} onclick="changePage(${currentPage - 1})">${t("prev")}</button>
<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})">${t("next")}</button>
</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="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>Actions</th>
<th>${t("actions")}</th>
</tr>
</thead>
`;
@@ -181,36 +181,38 @@ export function buildFileTableRow(file, folderPath) {
<div class="button-wrap" style="display: flex; justify-content: left; gap: 5px;">
<button type="button" class="btn btn-sm btn-success download-btn"
onclick="openDownloadModal('${file.name}', '${file.folder || 'root'}')"
title="Download">
<i class="material-icons">file_download</i>
title="${t('download')}">
<i class="material-icons">file_download</i>
</button>
${file.editable ? `
<button class="btn btn-sm edit-btn"
onclick='editFile(${JSON.stringify(file.name)}, ${JSON.stringify(file.folder || "root")})'
title="Edit">
title="${t('edit')}">
<i class="material-icons">edit</i>
</button>
` : ""}
${previewButton}
<button class="btn btn-sm btn-warning rename-btn"
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>
</button>
</div>
</td>
</tr>
`;
`;
}
export function buildBottomControls(itemsPerPageSetting) {
return `
<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)">
${[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>
<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>
`;
}

View File

@@ -73,7 +73,7 @@ function buildFolderSummary(filteredFiles) {
return sum + parseSizeToBytes(file.size);
}, 0);
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) => {
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>
</button>$1`);
rowsHTML += rowHTML;
@@ -425,18 +425,18 @@ export function renderGalleryView(folder, container) {
<div class="button-wrap" style="display: flex; justify-content: center; gap: 5px;">
<button type="button" class="btn btn-sm btn-success download-btn"
onclick="openDownloadModal('${file.name}', '${file.folder || 'root'}')"
title="Download">
title="${t('download')}">
<i class="material-icons">file_download</i>
</button>
${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>
</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>
</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>
</button>
</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.",
// File List keys:
"actions": "Actions",
"file_list_title": "Files in (Root)",
"files_in": "Files in",
"delete_files": "Delete Files",
@@ -101,6 +102,13 @@ const translations = {
"download_zip_title": "Download Selected Files as Zip",
"download_zip_prompt": "Enter a name for the zip file:",
"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": "Login",
@@ -142,6 +150,7 @@ const translations = {
// Custom Confirm Modal keys:
"yes": "Yes",
"no": "No",
"unsaved_changes_confirm": "You have unsaved changes. Are you sure you want to close without saving?",
"delete": "Delete",
"download": "Download",
"upload": "Upload",
@@ -164,6 +173,7 @@ const translations = {
// NEW KEYS ADDED FOR ADMIN, USER PANELS, AND TOTP MODALS:
"admin_panel": "Admin Panel",
"user_panel": "User Panel",
"trash_restore_delete": "Trash Restore/Delete",
"totp_settings": "TOTP Settings",
"enable_totp": "Enable TOTP",
"language": "Language",
@@ -217,7 +227,9 @@ const translations = {
"save_permissions": "Save Permissions",
"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.",
"ok": "OK"
"ok": "OK",
"show": "Show",
"items_per_page": "items per page"
},
es: {
"please_log_in_to_continue": "Por favor, inicie sesión para continuar.",
@@ -361,6 +373,7 @@ const translations = {
// Custom Confirm Modal keys:
"yes": "Sí",
"no": "No",
"unsaved_changes_confirm": "Tiene cambios sin guardar. ¿Está seguro de que desea cerrar sin guardar?",
"delete": "Eliminar",
"download": "Descargar",
"upload": "Cargar",
@@ -580,6 +593,7 @@ const translations = {
// Custom Confirm Modal keys:
"yes": "Oui",
"no": "Non",
"unsaved_changes_confirm": "Vous avez des modifications non enregistrées. Êtes-vous sûr de vouloir fermer sans enregistrer ?",
"delete": "Supprimer",
"download": "Télécharger",
"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.",
// File List keys:
"actions": "Aktionen",
"file_list_title": "Dateien in (Root)",
"files_in": "Dateien in",
"delete_files": "Dateien löschen",
@@ -758,6 +773,13 @@ const translations = {
"download_zip_title": "Ausgewählte Dateien als Zip herunterladen",
"download_zip_prompt": "Geben Sie einen Namen für die Zip-Datei ein:",
"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": "Anmelden",
@@ -799,6 +821,7 @@ const translations = {
// Custom Confirm Modal keys:
"yes": "Ja",
"no": "Nein",
"unsaved_changes_confirm": "Sie haben ungespeicherte Änderungen. Sind Sie sicher, dass Sie schließen möchten, ohne zu speichern?",
"delete": "Löschen",
"download": "Herunterladen",
"upload": "Hochladen",
@@ -821,6 +844,7 @@ const translations = {
// NEW KEYS ADDED FOR ADMIN, USER PANELS, AND TOTP MODALS:
"admin_panel": "Administrationsbereich",
"user_panel": "Benutzerbereich",
"trash_restore_delete": "Papierkorb wiederherstellen/löschen",
"totp_settings": "TOTP-Einstellungen",
"enable_totp": "TOTP aktivieren",
"language": "Sprache",
@@ -874,7 +898,9 @@ const translations = {
"save_permissions": "Berechtigungen speichern",
"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.",
"ok": "OK"
"ok": "OK",
"show": "Zeige",
"items_per_page": "elemente pro seite"
}
};