- Press and hold keys to set a shortcut. Changes are auto-saved.
- ⚠️ Avoid common browser shortcuts (Cmd/Ctrl+W, Cmd/Ctrl+T, Cmd/Ctrl+N
+ Press and hold keys to set a shortcut. Changes are auto-saved.
+ ⚠️ Avoid common browser shortcuts (Cmd/Ctrl+W, Cmd/Ctrl+T, Cmd/Ctrl+N
etc.) as they
may not work reliably.
diff --git a/public/locales/de/common.json b/public/locales/de/common.json
index 2b50a01..cf78551 100644
--- a/public/locales/de/common.json
+++ b/public/locales/de/common.json
@@ -271,5 +271,48 @@
"licensing": {
"title": "Lizenzierung für",
"subtitle": "Wählen Sie die Lizenz, die Ihren Anforderungen entspricht."
+ },
+ "multiTool": {
+ "uploadPdfs": "PDFs hochladen",
+ "upload": "Hochladen",
+ "addBlankPage": "Leere Seite hinzufügen",
+ "edit": "Bearbeiten:",
+ "undo": "Rückgängig",
+ "redo": "Wiederholen",
+ "reset": "Zurücksetzen",
+ "selection": "Auswahl:",
+ "selectAll": "Alles auswählen",
+ "deselectAll": "Auswahl aufheben",
+ "rotate": "Drehen:",
+ "rotateLeft": "Links",
+ "rotateRight": "Rechts",
+ "transform": "Transformieren:",
+ "duplicate": "Duplizieren",
+ "split": "Teilen",
+ "clear": "Löschen:",
+ "delete": "Entfernen",
+ "download": "Download:",
+ "downloadSelected": "Auswahl herunterladen",
+ "exportPdf": "PDF exportieren",
+ "uploadPdfFiles": "PDF-Dateien hochladen",
+ "dragAndDrop": "PDF-Dateien hierhin ziehen oder klicken zum Auswählen",
+ "selectFiles": "Dateien auswählen",
+ "renderingPages": "Seiten werden gerendert...",
+ "actions": {
+ "duplicatePage": "Diese Seite duplizieren",
+ "deletePage": "Diese Seite löschen",
+ "insertPdf": "PDF nach dieser Seite einfügen",
+ "toggleSplit": "Trennung nach dieser Seite umschalten"
+ },
+ "pleaseWait": "Bitte warten",
+ "pagesRendering": "Seiten werden noch gerendert. Bitte warten...",
+ "noPagesSelected": "Keine Seiten ausgewählt",
+ "selectOnePage": "Bitte wählen Sie mindestens eine Seite zum Herunterladen aus.",
+ "noPages": "Keine Seiten",
+ "noPagesToExport": "Keine Seiten zum Exportieren vorhanden.",
+ "renderingTitle": "Seiten-Vorschau wird gerendert",
+ "errorRendering": "Fehler beim Rendern der Seitenvorschau",
+ "error": "Fehler",
+ "failedToLoad": "Laden fehlgeschlagen"
}
}
\ No newline at end of file
diff --git a/public/locales/en/common.json b/public/locales/en/common.json
index fdd3741..6ea63e2 100644
--- a/public/locales/en/common.json
+++ b/public/locales/en/common.json
@@ -271,5 +271,48 @@
"licensing": {
"title": "Licensing for",
"subtitle": "Choose the license that fits your needs."
+ },
+ "multiTool": {
+ "uploadPdfs": "Upload PDFs",
+ "upload": "Upload",
+ "addBlankPage": "Add Blank Page",
+ "edit": "Edit:",
+ "undo": "Undo",
+ "redo": "Redo",
+ "reset": "Reset",
+ "selection": "Selection:",
+ "selectAll": "Select All",
+ "deselectAll": "Deselect All",
+ "rotate": "Rotate:",
+ "rotateLeft": "Left",
+ "rotateRight": "Right",
+ "transform": "Transform:",
+ "duplicate": "Duplicate",
+ "split": "Split",
+ "clear": "Clear:",
+ "delete": "Delete",
+ "download": "Download:",
+ "downloadSelected": "Download Selected",
+ "exportPdf": "Export PDF",
+ "uploadPdfFiles": "Upload PDF Files",
+ "dragAndDrop": "Drag and drop PDF files here, or click to select",
+ "selectFiles": "Select Files",
+ "renderingPages": "Rendering pages...",
+ "actions": {
+ "duplicatePage": "Duplicate this page",
+ "deletePage": "Delete this page",
+ "insertPdf": "Insert PDF after this page",
+ "toggleSplit": "Toggle split after this page"
+ },
+ "pleaseWait": "Please Wait",
+ "pagesRendering": "Pages are still being rendered. Please wait...",
+ "noPagesSelected": "No Pages Selected",
+ "selectOnePage": "Please select at least one page to download.",
+ "noPages": "No Pages",
+ "noPagesToExport": "There are no pages to export.",
+ "renderingTitle": "Rendering page previews",
+ "errorRendering": "Failed to render page thumbnails",
+ "error": "Error",
+ "failedToLoad": "Failed to load"
}
}
\ No newline at end of file
diff --git a/public/locales/zh/common.json b/public/locales/zh/common.json
index c588ca9..8d78ebc 100644
--- a/public/locales/zh/common.json
+++ b/public/locales/zh/common.json
@@ -271,5 +271,48 @@
"licensing": {
"title": "许可适用",
"subtitle": "选择适合您需求的许可。"
+ },
+ "multiTool": {
+ "uploadPdfs": "上传 PDF",
+ "upload": "上传",
+ "addBlankPage": "添加空白页",
+ "edit": "编辑:",
+ "undo": "撤销",
+ "redo": "重做",
+ "reset": "重置",
+ "selection": "选择:",
+ "selectAll": "全选",
+ "deselectAll": "取消全选",
+ "rotate": "旋转:",
+ "rotateLeft": "向左",
+ "rotateRight": "向右",
+ "transform": "变换:",
+ "duplicate": "复制",
+ "split": "拆分",
+ "clear": "清除:",
+ "delete": "删除",
+ "download": "下载:",
+ "downloadSelected": "下载选中",
+ "exportPdf": "导出 PDF",
+ "uploadPdfFiles": "上传 PDF 文件",
+ "dragAndDrop": "将 PDF 文件拖放到此处,或点击选择",
+ "selectFiles": "选择文件",
+ "renderingPages": "正在渲染页面...",
+ "actions": {
+ "duplicatePage": "复制此页",
+ "deletePage": "删除此页",
+ "insertPdf": "在此页后插入 PDF",
+ "toggleSplit": "在此页后切换拆分"
+ },
+ "pleaseWait": "请稍候",
+ "pagesRendering": "页面正在渲染中,请稍候...",
+ "noPagesSelected": "未选择页面",
+ "selectOnePage": "请至少选择一页以进行下载。",
+ "noPages": "没有页面",
+ "noPagesToExport": "没有可导出的页面。",
+ "renderingTitle": "正在渲染页面预览",
+ "errorRendering": "渲染页面缩略图失败",
+ "error": "错误",
+ "failedToLoad": "加载失败"
}
}
\ No newline at end of file
diff --git a/src/js/logic/pdf-multi-tool.ts b/src/js/logic/pdf-multi-tool.ts
index 99d6711..92c4e7d 100644
--- a/src/js/logic/pdf-multi-tool.ts
+++ b/src/js/logic/pdf-multi-tool.ts
@@ -13,6 +13,8 @@ pdfjsLib.GlobalWorkerOptions.workerSrc = new URL(
import.meta.url
).toString();
+import { t } from '../i18n/i18n';
+
interface PageData {
id: string; // Unique ID for DOM reconciliation
pdfIndex: number;
@@ -107,7 +109,7 @@ function showLoading(current: number, total: number) {
loader.classList.remove('hidden');
const percentage = Math.round((current / total) * 100);
progress.style.width = `${percentage}%`;
- text.textContent = `Rendering pages...`;
+ text.textContent = t('multiTool.renderingPages');
}
async function withButtonLoading(buttonId: string, action: () => Promise) {
@@ -158,7 +160,7 @@ function initializeTool() {
document.getElementById('upload-pdfs-btn')?.addEventListener('click', () => {
console.log('Upload button clicked, isRendering:', isRendering);
if (isRendering) {
- showModal('Please Wait', 'Pages are still being rendered. Please wait...', 'info');
+ showModal(t('multiTool.pleaseWait'), t('multiTool.pagesRendering'), 'info');
return;
}
document.getElementById('pdf-file-input')?.click();
@@ -193,9 +195,10 @@ function initializeTool() {
bulkSplit();
});
document.getElementById('bulk-download-btn')?.addEventListener('click', () => {
+ if (isRendering) return;
if (isRendering) return;
if (selectedPages.size === 0) {
- showModal('No Pages Selected', 'Please select at least one page to download.', 'info');
+ showModal(t('multiTool.noPagesSelected'), t('multiTool.selectOnePage'), 'info');
return;
}
withButtonLoading('bulk-download-btn', async () => {
@@ -216,9 +219,10 @@ function initializeTool() {
});
document.getElementById('export-pdf-btn')?.addEventListener('click', () => {
+ if (isRendering) return;
if (isRendering) return;
if (allPages.length === 0) {
- showModal('No Pages', 'There are no pages to export.', 'info');
+ showModal(t('multiTool.noPages'), t('multiTool.noPagesToExport'), 'info');
return;
}
withButtonLoading('export-pdf-btn', async () => {
@@ -329,7 +333,7 @@ async function handlePdfUpload(e: Event) {
async function loadPdfs(files: File[]) {
if (isRendering) {
- showModal('Please Wait', 'Pages are still being rendered. Please wait...', 'info');
+ showModal(t('multiTool.pleaseWait'), t('multiTool.pagesRendering'), 'info');
return;
}
@@ -424,7 +428,7 @@ async function loadPdfs(files: File[]) {
} catch (e) {
console.error(`Failed to load PDF ${file.name}:`, e);
- showModal('Error', `Failed to load ${file.name}. The file may be corrupted.`, 'error');
+ showModal(t('multiTool.error'), `${t('multiTool.failedToLoad')} ${file.name}.`, 'error');
}
}
@@ -501,7 +505,7 @@ function createPageElement(canvas: HTMLCanvasElement | null, index: number): HTM
loading.className = 'flex flex-col items-center justify-center text-gray-400';
loading.innerHTML = `
- Loading...
+ ${t('common.loading')}
`;
preview.appendChild(loading);
preview.classList.add('bg-gray-700'); // Darker background for loading
@@ -510,7 +514,7 @@ function createPageElement(canvas: HTMLCanvasElement | null, index: number): HTM
// Page info
const info = document.createElement('div');
info.className = 'text-xs text-gray-400 text-center mb-2';
- info.textContent = `Page ${index + 1}`;
+ info.textContent = `${t('common.page')} ${index + 1}`;
// Actions toolbar
const actions = document.createElement('div');
@@ -551,7 +555,7 @@ function createPageElement(canvas: HTMLCanvasElement | null, index: number): HTM
const duplicateBtn = document.createElement('button');
duplicateBtn.className = 'p-1 rounded hover:bg-gray-700';
duplicateBtn.innerHTML = '';
- duplicateBtn.title = 'Duplicate this page';
+ duplicateBtn.title = t('multiTool.actions.duplicatePage');
duplicateBtn.onclick = (e) => {
e.stopPropagation();
snapshot();
@@ -562,7 +566,7 @@ function createPageElement(canvas: HTMLCanvasElement | null, index: number): HTM
const deleteBtn = document.createElement('button');
deleteBtn.className = 'p-1 rounded hover:bg-gray-700';
deleteBtn.innerHTML = '';
- deleteBtn.title = 'Delete this page';
+ deleteBtn.title = t('multiTool.actions.deletePage');
deleteBtn.onclick = (e) => {
e.stopPropagation();
snapshot();
@@ -573,7 +577,7 @@ function createPageElement(canvas: HTMLCanvasElement | null, index: number): HTM
const insertBtn = document.createElement('button');
insertBtn.className = 'p-1 rounded hover:bg-gray-700';
insertBtn.innerHTML = '';
- insertBtn.title = 'Insert PDF after this page';
+ insertBtn.title = t('multiTool.actions.insertPdf');
insertBtn.onclick = (e) => {
e.stopPropagation();
snapshot();
@@ -584,7 +588,7 @@ function createPageElement(canvas: HTMLCanvasElement | null, index: number): HTM
const splitBtn = document.createElement('button');
splitBtn.className = 'p-1 rounded hover:bg-gray-700';
splitBtn.innerHTML = '';
- splitBtn.title = 'Toggle split after this page';
+ splitBtn.title = t('multiTool.actions.toggleSplit');
splitBtn.onclick = (e) => {
e.stopPropagation();
snapshot();
diff --git a/src/js/ui.ts b/src/js/ui.ts
index ed5c1f1..dda62cb 100644
--- a/src/js/ui.ts
+++ b/src/js/ui.ts
@@ -6,6 +6,7 @@ import { icons, createIcons } from 'lucide';
import Sortable from 'sortablejs';
import { getRotationState, updateRotationState } from './utils/rotation-state.js';
import * as pdfjsLib from 'pdfjs-dist';
+import { t } from './i18n/i18n';
pdfjsLib.GlobalWorkerOptions.workerSrc = new URL('pdfjs-dist/build/pdf.worker.min.mjs', import.meta.url).toString();
@@ -43,7 +44,7 @@ export const dom = {
warningConfirmBtn: document.getElementById('warning-confirm-btn'),
};
-export const showLoader = (text = 'Processing...') => {
+export const showLoader = (text = t('common.loading')) => {
if (dom.loaderText) dom.loaderText.textContent = text;
if (dom.loaderModal) dom.loaderModal.classList.remove('hidden');
};
@@ -152,7 +153,7 @@ export const renderPageThumbnails = async (toolId: any, pdfDoc: any) => {
const currentRenderId = Date.now();
container.dataset.renderId = currentRenderId.toString();
- showLoader('Rendering page previews...');
+ showLoader(t('multiTool.renderingTitle'));
const pdfData = await pdfDoc.save();
const pdf = await getPDFDocument({ data: pdfData }).promise;
@@ -373,7 +374,7 @@ export const renderPageThumbnails = async (toolId: any, pdfDoc: any) => {
createIcons({ icons });
} catch (error) {
console.error('Error rendering page thumbnails:', error);
- showAlert('Error', 'Failed to render page thumbnails');
+ showAlert(t('multiTool.error'), t('multiTool.errorRendering'));
} finally {
hideLoader();
}
@@ -418,9 +419,9 @@ const createFileInputHTML = (options = {}) => {
-
Click to select a file or drag and drop
-
${multiple ? 'PDFs or Images' : 'A single PDF file'}