Merge pull request #664 from Skillkiller/translation

Localize status messages and UI elements across various tools
This commit is contained in:
Alam
2026-05-08 19:44:24 +05:30
committed by GitHub
41 changed files with 1309 additions and 312 deletions
+34 -27
View File
@@ -190,8 +190,8 @@ function initializePage(): void {
const validTypes = ['image/png', 'image/jpeg', 'image/webp'];
if (!validTypes.includes(file.type)) {
showAlert(
'Invalid Image',
'Please select a PNG, JPG, or WebP image.'
t('tools:digitalSignPdf.invalidImageTitle'),
t('tools:digitalSignPdf.invalidImageMessage')
);
return;
}
@@ -249,7 +249,10 @@ async function handlePdfFile(file: File): Promise<void> {
file.type !== 'application/pdf' &&
!file.name.toLowerCase().endsWith('.pdf')
) {
showAlert('Invalid File', 'Please select a PDF file.');
showAlert(
t('tools:digitalSignPdf.invalidFileTitle'),
t('tools:digitalSignPdf.invalidFileMessage')
);
return;
}
@@ -314,7 +317,7 @@ async function updatePdfDisplay(): Promise<void> {
state.pdfFile = result.file;
state.pdfBytes = new Uint8Array(result.bytes);
nameSpan.textContent = result.file.name;
metaSpan.textContent = `${formatBytes(result.file.size)} • ${result.pdf.numPages} pages`;
metaSpan.textContent = `${formatBytes(result.file.size)} • ${t('tools:digitalSignPdf.pageCount', { count: result.pdf.numPages })}`;
result.pdf.destroy();
}
}
@@ -373,8 +376,8 @@ async function handleCertFile(file: File): Promise<void> {
if (!hasValidExtension) {
showAlert(
'Invalid Certificate',
'Please select a .pfx, .p12, or .pem certificate file.'
t('tools:digitalSignPdf.invalidCertificateTitle'),
t('tools:digitalSignPdf.invalidCertificateMessage')
);
return;
}
@@ -393,7 +396,7 @@ async function handleCertFile(file: File): Promise<void> {
if (isEncrypted) {
showPasswordSection();
updatePasswordLabel('Private Key Password');
updatePasswordLabel(t('tools:digitalSignPdf.privateKeyPassword'));
} else {
state.certData = parseCombinedPem(pemContent);
updateCertInfo();
@@ -401,8 +404,7 @@ async function handleCertFile(file: File): Promise<void> {
const certStatus = getElement<HTMLDivElement>('cert-status');
if (certStatus) {
certStatus.innerHTML =
'Certificate loaded <i data-lucide="check" class="inline w-4 h-4"></i>';
certStatus.innerHTML = `${t('tools:digitalSignPdf.certificateLoaded')} <i data-lucide="check" class="inline w-4 h-4"></i>`;
createIcons({ icons });
certStatus.className = 'text-xs text-green-400';
}
@@ -410,13 +412,13 @@ async function handleCertFile(file: File): Promise<void> {
} catch {
const certStatus = getElement<HTMLDivElement>('cert-status');
if (certStatus) {
certStatus.textContent = 'Failed to parse PEM file';
certStatus.textContent = t('tools:digitalSignPdf.failedParsePem');
certStatus.className = 'text-xs text-red-400';
}
}
} else {
showPasswordSection();
updatePasswordLabel('Certificate Password');
updatePasswordLabel(t('tools:digitalSignPdf.certPassword'));
}
hideSignatureOptions();
@@ -444,7 +446,7 @@ function updateCertDisplay(): void {
const metaSpan = document.createElement('div');
metaSpan.className = 'text-xs text-gray-400';
metaSpan.id = 'cert-status';
metaSpan.textContent = 'Enter password to unlock';
metaSpan.textContent = t('tools:digitalSignPdf.enterPasswordToUnlock');
infoContainer.append(nameSpan, metaSpan);
@@ -557,8 +559,7 @@ async function handlePasswordInput(): Promise<void> {
const certStatus = getElement<HTMLDivElement>('cert-status');
if (certStatus) {
certStatus.innerHTML =
'Certificate unlocked <i data-lucide="check-circle" class="inline w-4 h-4"></i>';
certStatus.innerHTML = `${t('tools:digitalSignPdf.certificateUnlocked')} <i data-lucide="check-circle" class="inline w-4 h-4"></i>`;
createIcons({ icons });
certStatus.className = 'text-xs text-green-400';
}
@@ -572,10 +573,10 @@ async function handlePasswordInput(): Promise<void> {
const errorMessage =
error instanceof Error
? error.message
: 'Invalid password or certificate';
: t('tools:digitalSignPdf.invalidPasswordOrCertificate');
certStatus.textContent = errorMessage.includes('password')
? 'Incorrect password'
: 'Failed to parse certificate';
? t('tools:digitalSignPdf.incorrectPassword')
: t('tools:digitalSignPdf.failedParseCertificate');
certStatus.className = 'text-xs text-red-400';
}
}
@@ -623,8 +624,8 @@ function updateProcessButton(): void {
async function processSignature(): Promise<void> {
if (!state.pdfBytes || !state.certData) {
showAlert(
'Missing Data',
'Please upload both a PDF and a valid certificate.'
t('tools:digitalSignPdf.missingDataTitle'),
t('tools:digitalSignPdf.missingDataMessage')
);
return;
}
@@ -706,7 +707,10 @@ async function processSignature(): Promise<void> {
if (!state.sigImageData && !sigText && state.certData) {
const certInfo = getCertificateInfo(state.certData.certificate);
const date = new Date().toLocaleDateString();
sigText = `Digitally signed by ${certInfo.subject}\n${date}`;
sigText = t('tools:digitalSignPdf.defaultSignatureText', {
signer: certInfo.subject,
date,
});
}
let finalHeight = sigHeight;
@@ -735,7 +739,7 @@ async function processSignature(): Promise<void> {
};
}
showLoader('Applying digital signature...');
showLoader(t('tools:digitalSignPdf.applyingSignature'));
try {
const signedPdfBytes = await signPdf(state.pdfBytes, state.certData, {
@@ -750,8 +754,8 @@ async function processSignature(): Promise<void> {
hideLoader();
showAlert(
'Success',
'PDF signed successfully! The signature can be verified in any PDF reader.',
t('common.success'),
t('tools:digitalSignPdf.successMessage'),
'success',
() => {
resetState();
@@ -761,7 +765,7 @@ async function processSignature(): Promise<void> {
hideLoader();
console.error('Signing error:', error);
const errorMessage =
error instanceof Error ? error.message : 'Unknown error occurred';
error instanceof Error ? error.message : t('common.unknownError');
// Check if this is a CORS/network error from certificate chain fetching
if (
@@ -770,11 +774,14 @@ async function processSignature(): Promise<void> {
errorMessage.includes('NetworkError')
) {
showAlert(
'Signing Failed',
'Failed to fetch certificate chain. This may be due to network issues or the certificate proxy being unavailable. Please check your internet connection and try again. If the issue persists, contact support.'
t('tools:digitalSignPdf.signingFailedTitle'),
t('tools:digitalSignPdf.chainFetchFailed')
);
} else {
showAlert('Signing Failed', `Failed to sign PDF: ${errorMessage}`);
showAlert(
t('tools:digitalSignPdf.signingFailedTitle'),
t('tools:digitalSignPdf.failedToSignPdf', { message: errorMessage })
);
}
}
}
+52 -36
View File
@@ -10,6 +10,7 @@ import {
showWasmRequiredDialog,
WasmProvider,
} from '../utils/wasm-provider.js';
import { initI18n, t } from '../i18n/i18n';
const worker = new Worker(
import.meta.env.BASE_URL + 'workers/json-to-pdf.worker.js'
@@ -17,8 +18,8 @@ const worker = new Worker(
let selectedFiles: File[] = [];
const jsonFilesInput = document.getElementById('jsonFiles') as HTMLInputElement;
const convertBtn = document.getElementById('convertBtn') as HTMLButtonElement;
let jsonFilesInput!: HTMLInputElement;
let convertBtn!: HTMLButtonElement;
const statusMessage = document.getElementById(
'status-message'
) as HTMLDivElement;
@@ -72,27 +73,9 @@ function updateFileList() {
});
}
jsonFilesInput.addEventListener('change', (e) => {
const target = e.target as HTMLInputElement;
if (target.files && target.files.length > 0) {
selectedFiles = Array.from(target.files);
convertBtn.disabled = selectedFiles.length === 0;
updateFileList();
if (selectedFiles.length === 0) {
showStatus('Please select at least 1 JSON file', 'info');
} else {
showStatus(
`${selectedFiles.length} file(s) selected. Ready to convert!`,
'info'
);
}
}
});
async function convertJSONsToPDF() {
if (selectedFiles.length === 0) {
showStatus('Please select at least 1 JSON file', 'error');
showStatus(t('tools:jsonToPdf.status.selectAtLeastOne'), 'error');
return;
}
@@ -104,13 +87,13 @@ async function convertJSONsToPDF() {
try {
convertBtn.disabled = true;
showStatus('Reading files (Main Thread)...', 'info');
showStatus(t('tools:jsonToPdf.status.readingFiles'), 'info');
const fileBuffers = await Promise.all(
selectedFiles.map((file) => readFileAsArrayBuffer(file))
);
showStatus('Converting JSONs to PDFs...', 'info');
showStatus(t('tools:jsonToPdf.status.converting'), 'info');
worker.postMessage(
{
@@ -124,7 +107,10 @@ async function convertJSONsToPDF() {
} catch (error) {
console.error('Error reading files:', error);
showStatus(
`❌ Error reading files: ${error instanceof Error ? error.message : 'Unknown error'}`,
t('tools:jsonToPdf.status.readError', {
message:
error instanceof Error ? error.message : t('common.unknownError'),
}),
'error'
);
convertBtn.disabled = false;
@@ -141,7 +127,7 @@ worker.onmessage = async (e: MessageEvent) => {
}>;
try {
showStatus('Creating ZIP file...', 'info');
showStatus(t('tools:jsonToPdf.status.creatingZip'), 'info');
const zip = new JSZip();
pdfFiles.forEach(({ name, data }) => {
@@ -157,10 +143,7 @@ worker.onmessage = async (e: MessageEvent) => {
a.download = 'jsons-to-pdf.zip';
downloadFile(zipBlob, 'jsons-to-pdf.zip');
showStatus(
'✅ JSONs converted to PDF successfully! ZIP download started.',
'success'
);
showStatus(t('tools:jsonToPdf.status.success'), 'success');
selectedFiles = [];
jsonFilesInput.value = '';
@@ -174,14 +157,20 @@ worker.onmessage = async (e: MessageEvent) => {
} catch (error) {
console.error('Error creating ZIP:', error);
showStatus(
`❌ Error creating ZIP: ${error instanceof Error ? error.message : 'Unknown error'}`,
t('tools:jsonToPdf.status.zipError', {
message:
error instanceof Error ? error.message : t('common.unknownError'),
}),
'error'
);
}
} else if (e.data.status === 'error') {
const errorMessage = e.data.message || 'Unknown error occurred in worker.';
const errorMessage = e.data.message || t('common.unknownError');
console.error('Worker Error:', errorMessage);
showStatus(`❌ Worker Error: ${errorMessage}`, 'error');
showStatus(
t('tools:jsonToPdf.status.workerError', { message: errorMessage }),
'error'
);
}
};
@@ -191,8 +180,35 @@ if (backToToolsBtn) {
});
}
convertBtn.addEventListener('click', convertJSONsToPDF);
// Initialize after i18n is ready so the default status is translated.
void (async () => {
await initI18n();
// Initialize
showStatus('Select JSON files to get started', 'info');
initializeGlobalShortcuts();
jsonFilesInput = document.getElementById('jsonFiles') as HTMLInputElement;
convertBtn = document.getElementById('convertBtn') as HTMLButtonElement;
jsonFilesInput.addEventListener('change', (e) => {
const target = e.target as HTMLInputElement;
if (target.files && target.files.length > 0) {
selectedFiles = Array.from(target.files);
convertBtn.disabled = selectedFiles.length === 0;
updateFileList();
if (selectedFiles.length === 0) {
showStatus(t('tools:jsonToPdf.status.selectAtLeastOne'), 'info');
} else {
showStatus(
t('tools:jsonToPdf.status.selectedReady', {
count: selectedFiles.length,
}),
'info'
);
}
}
});
convertBtn.addEventListener('click', convertJSONsToPDF);
showStatus(t('tools:jsonToPdf.status.getStarted'), 'info');
initializeGlobalShortcuts();
})();
+19 -15
View File
@@ -10,6 +10,7 @@ import {
resolveConfiguredTesseractAvailableLanguages,
UnsupportedOcrLanguageError,
} from '../utils/tesseract-language-availability.js';
import { initI18n, t } from '../i18n/index.js';
const pageState: OcrState = {
file: null,
@@ -37,7 +38,7 @@ function updateProgress(status: string, progress: number) {
progressStatus.textContent = status;
progressBar.style.width = `${Math.min(100, progress * 100)}%`;
const logMessage = `Status: ${status}`;
const logMessage = t('tools:ocrPdf.statusPrefix', { status });
progressLog.textContent += logMessage + '\n';
progressLog.scrollTop = progressLog.scrollHeight;
}
@@ -78,7 +79,9 @@ function resetState() {
const selectedLangsDisplay = document.getElementById(
'selected-langs-display'
);
if (selectedLangsDisplay) selectedLangsDisplay.textContent = 'None';
if (selectedLangsDisplay) {
selectedLangsDisplay.textContent = t('tools:ocrPdf.none');
}
const processBtn = document.getElementById(
'process-btn'
@@ -100,14 +103,15 @@ function updateLanguageAvailabilityNotice() {
const availableEntries = getAvailableTesseractLanguageEntries();
if (availableEntries.length === 0) {
notice.classList.remove('hidden');
notice.textContent =
'This deployment does not expose any valid OCR languages. Rebuild it with VITE_TESSERACT_AVAILABLE_LANGUAGES set to valid Tesseract codes.';
notice.textContent = t('tools:ocrPdf.availabilityInvalidConfig');
return;
}
const availableNames = availableEntries.map(([, name]) => name).join(', ');
notice.classList.remove('hidden');
notice.textContent = `This deployment bundles OCR for: ${availableNames}.`;
notice.textContent = t('tools:ocrPdf.availabilityBundles', {
languages: availableNames,
});
}
async function runOCR() {
@@ -131,14 +135,14 @@ async function runOCR() {
if (selectedLangs.length === 0) {
showAlert(
'No Languages Selected',
'Please select at least one language for OCR.'
t('tools:ocrPdf.noLanguagesTitle'),
t('tools:ocrPdf.noLanguagesMessage')
);
return;
}
if (!pageState.file) {
showAlert('No File', 'Please upload a PDF file first.');
showAlert(t('tools:ocrPdf.noFileTitle'), t('tools:ocrPdf.noFileMessage'));
return;
}
@@ -177,11 +181,11 @@ async function runOCR() {
} catch (e) {
console.error(e);
if (e instanceof UnsupportedOcrLanguageError) {
showAlert('OCR Language Not Available', e.message);
showAlert(t('tools:ocrPdf.languageUnavailableTitle'), e.message);
} else {
showAlert(
'OCR Error',
'An error occurred during the OCR process. The worker may have failed to load. Please try again.'
t('tools:ocrPdf.ocrErrorTitle'),
t('tools:ocrPdf.ocrErrorMessage')
);
}
if (toolOptions) toolOptions.classList.remove('hidden');
@@ -258,8 +262,7 @@ function populateLanguageList() {
if (availableEntries.length === 0) {
const emptyState = document.createElement('p');
emptyState.className = 'text-sm text-yellow-300 p-2';
emptyState.textContent =
'No OCR languages are available in this deployment.';
emptyState.textContent = t('tools:ocrPdf.noLanguagesAvailable');
langList.appendChild(emptyState);
return;
}
@@ -282,7 +285,7 @@ function populateLanguageList() {
});
}
document.addEventListener('DOMContentLoaded', function () {
document.addEventListener('DOMContentLoaded', async function () {
const fileInput = document.getElementById('file-input') as HTMLInputElement;
const dropZone = document.getElementById('drop-zone');
const processBtn = document.getElementById(
@@ -304,6 +307,7 @@ document.addEventListener('DOMContentLoaded', function () {
const downloadTxtBtn = document.getElementById('download-txt-btn');
const downloadPdfBtn = document.getElementById('download-searchable-pdf');
await initI18n();
populateLanguageList();
updateLanguageAvailabilityNotice();
@@ -376,7 +380,7 @@ document.addEventListener('DOMContentLoaded', function () {
if (selectedLangsDisplay) {
selectedLangsDisplay.textContent =
selected.length > 0 ? selected.join(', ') : 'None';
selected.length > 0 ? selected.join(', ') : t('tools:ocrPdf.none');
}
if (processBtn) {
+29 -17
View File
@@ -12,6 +12,7 @@ import {
WasmProvider,
} from '../utils/wasm-provider.js';
import { batchDecryptIfNeeded } from '../utils/password-prompt.js';
import { initI18n, t } from '../i18n/i18n';
const worker = new Worker(
import.meta.env.BASE_URL + 'workers/pdf-to-json.worker.js'
@@ -82,10 +83,12 @@ pdfFilesInput.addEventListener('change', (e) => {
updateFileList();
if (selectedFiles.length === 0) {
showStatus('Please select at least 1 PDF file', 'info');
showStatus(t('tools:pdfToJson.status.selectAtLeastOne'), 'info');
} else {
showStatus(
`${selectedFiles.length} file(s) selected. Ready to convert!`,
t('tools:pdfToJson.status.selectedReady', {
count: selectedFiles.length,
}),
'info'
);
}
@@ -94,7 +97,7 @@ pdfFilesInput.addEventListener('change', (e) => {
async function convertPDFsToJSON() {
if (selectedFiles.length === 0) {
showStatus('Please select at least 1 PDF file', 'error');
showStatus(t('tools:pdfToJson.status.selectAtLeastOne'), 'error');
return;
}
@@ -106,17 +109,17 @@ async function convertPDFsToJSON() {
try {
convertBtn.disabled = true;
showStatus('Checking for encrypted PDFs...', 'info');
showStatus(t('tools:pdfToJson.status.checkingEncrypted'), 'info');
selectedFiles = await batchDecryptIfNeeded(selectedFiles);
showStatus('Reading files (Main Thread)...', 'info');
showStatus(t('tools:pdfToJson.status.readingFiles'), 'info');
const fileBuffers = await Promise.all(
selectedFiles.map((file) => readFileAsArrayBuffer(file))
);
showStatus('Converting PDFs to JSON..', 'info');
showStatus(t('tools:pdfToJson.status.converting'), 'info');
worker.postMessage(
{
@@ -130,7 +133,10 @@ async function convertPDFsToJSON() {
} catch (error) {
console.error('Error reading files:', error);
showStatus(
`❌ Error reading files: ${error instanceof Error ? error.message : 'Unknown error'}`,
t('tools:pdfToJson.status.readError', {
message:
error instanceof Error ? error.message : t('common.unknownError'),
}),
'error'
);
convertBtn.disabled = false;
@@ -147,7 +153,7 @@ worker.onmessage = async (e: MessageEvent) => {
}>;
try {
showStatus('Creating ZIP file...', 'info');
showStatus(t('tools:pdfToJson.status.creatingZip'), 'info');
const zip = new JSZip();
const usedNames = new Set<string>();
@@ -161,10 +167,7 @@ worker.onmessage = async (e: MessageEvent) => {
const zipBlob = await zip.generateAsync({ type: 'blob' });
downloadFile(zipBlob, 'pdfs-to-json.zip');
showStatus(
'✅ PDFs converted to JSON successfully! ZIP download started.',
'success'
);
showStatus(t('tools:pdfToJson.status.success'), 'success');
selectedFiles = [];
pdfFilesInput.value = '';
@@ -178,14 +181,20 @@ worker.onmessage = async (e: MessageEvent) => {
} catch (error) {
console.error('Error creating ZIP:', error);
showStatus(
`❌ Error creating ZIP: ${error instanceof Error ? error.message : 'Unknown error'}`,
t('tools:pdfToJson.status.zipError', {
message:
error instanceof Error ? error.message : t('common.unknownError'),
}),
'error'
);
}
} else if (e.data.status === 'error') {
const errorMessage = e.data.message || 'Unknown error occurred in worker.';
const errorMessage = e.data.message || t('common.unknownError');
console.error('Worker Error:', errorMessage);
showStatus(`❌ Worker Error: ${errorMessage}`, 'error');
showStatus(
t('tools:pdfToJson.status.workerError', { message: errorMessage }),
'error'
);
}
};
@@ -197,5 +206,8 @@ if (backToToolsBtn) {
convertBtn.addEventListener('click', convertPDFsToJSON);
showStatus('Select PDF files to get started', 'info');
initializeGlobalShortcuts();
void (async () => {
await initI18n();
showStatus(t('tools:pdfToJson.status.getStarted'), 'info');
initializeGlobalShortcuts();
})();
+18 -9
View File
@@ -6,6 +6,7 @@ import {
WasmProvider,
} from '../utils/wasm-provider.js';
import { loadPdfWithPasswordPrompt } from '../utils/password-prompt.js';
import { t } from '../i18n/index.js';
const worker = new Worker(
import.meta.env.BASE_URL + 'workers/table-of-contents.worker.js'
@@ -128,7 +129,7 @@ fileInput.addEventListener('change', (e) => {
async function generateTableOfContents() {
if (!pdfFile) {
showStatus('Please select a PDF file first.', 'error');
showStatus(t('tools:tableOfContents.selectPdfFirst'), 'error');
return;
}
@@ -140,13 +141,13 @@ async function generateTableOfContents() {
try {
generateBtn.disabled = true;
showStatus('Reading file (Main Thread)...', 'info');
showStatus(t('tools:tableOfContents.statusReadingFileMainThread'), 'info');
const arrayBuffer = await pdfFile.arrayBuffer();
showStatus('Generating table of contents...', 'info');
showStatus(t('tools:tableOfContents.statusGenerating'), 'info');
const title = tocTitleInput.value || 'Table of Contents';
const title = tocTitleInput.value || t('tools:tableOfContents.name');
const fontSize = parseInt(fontSizeSelect.value, 10);
const fontFamily = parseInt(fontFamilySelect.value, 10);
const addBookmark = addBookmarkCheckbox.checked;
@@ -165,7 +166,10 @@ async function generateTableOfContents() {
} catch (error) {
console.error('Error reading file:', error);
showStatus(
`Error reading file: ${error instanceof Error ? error.message : 'Unknown error occurred'}`,
t('tools:tableOfContents.errorReadingFileWithMessage', {
message:
error instanceof Error ? error.message : t('common.unknownError'),
}),
'error'
);
generateBtn.disabled = false;
@@ -183,7 +187,7 @@ worker.onmessage = (e: MessageEvent<TOCWorkerResponse>) => {
downloadFile(blob, pdfFile?.name || 'document.pdf');
showStatus(
'Table of contents generated successfully! Download started.',
t('tools:tableOfContents.successGeneratedDownloadStarted'),
'success'
);
@@ -194,15 +198,20 @@ worker.onmessage = (e: MessageEvent<TOCWorkerResponse>) => {
fileDisplayArea.classList.add('hidden');
generateBtn.disabled = true;
} else if (e.data.status === 'error') {
const errorMessage = e.data.message || 'Unknown error occurred in worker.';
const errorMessage = e.data.message || t('common.unknownError');
console.error('Worker Error:', errorMessage);
showStatus(`Error: ${errorMessage}`, 'error');
showStatus(
t('tools:tableOfContents.workerErrorWithMessage', {
message: errorMessage,
}),
'error'
);
}
};
worker.onerror = (error) => {
console.error('Worker error:', error);
showStatus('Worker error occurred. Check console for details.', 'error');
showStatus(t('tools:tableOfContents.workerErrorOccurred'), 'error');
generateBtn.disabled = false;
};
+38 -18
View File
@@ -3,6 +3,7 @@ import { showAlert, showLoader, hideLoader } from '../ui.js';
import { WasmProvider, type WasmPackage } from '../utils/wasm-provider.js';
import { clearPyMuPDFCache } from '../utils/pymupdf-loader.js';
import { clearGhostscriptCache } from '../utils/ghostscript-dynamic-loader.js';
import { initI18n, t } from '../i18n/index.js';
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initializePage);
@@ -10,9 +11,15 @@ if (document.readyState === 'loading') {
initializePage();
}
function initializePage() {
async function initializePage() {
createIcons({ icons });
try {
await initI18n();
} catch (error) {
console.warn('WASM settings i18n initialization failed:', error);
}
document.querySelectorAll('.copy-btn').forEach((btn) => {
btn.addEventListener('click', async () => {
const url = btn.getAttribute('data-copy');
@@ -90,7 +97,7 @@ function initializePage() {
input.value = url;
} else {
input.value = '';
input.placeholder = `Using default: ${url}`;
input.placeholder = t('tools:wasmSettings.usingDefault', { url });
}
updateStatus(name, true);
}
@@ -111,19 +118,19 @@ function initializePage() {
if (!statusEl) return;
if (testing) {
statusEl.textContent = 'Testing...';
statusEl.textContent = t('tools:wasmSettings.status.testing');
statusEl.className =
'text-xs px-2 py-1 rounded-full bg-yellow-600/30 text-yellow-300';
} else if (configured && WasmProvider.isUserConfigured(packageName)) {
statusEl.textContent = 'Custom Override';
statusEl.textContent = t('tools:wasmSettings.status.customOverride');
statusEl.className =
'text-xs px-2 py-1 rounded-full bg-blue-600/30 text-blue-300';
} else if (configured || WasmProvider.hasEnvDefault(packageName)) {
statusEl.textContent = 'Pre-configured';
statusEl.textContent = t('tools:wasmSettings.status.preConfigured');
statusEl.className =
'text-xs px-2 py-1 rounded-full bg-green-600/30 text-green-300';
} else {
statusEl.textContent = 'Not Configured';
statusEl.textContent = t('tools:wasmSettings.status.notConfigured');
statusEl.className =
'text-xs px-2 py-1 rounded-full bg-gray-600 text-gray-300';
}
@@ -131,7 +138,10 @@ function initializePage() {
async function testConnection(packageName: WasmPackage, url: string) {
if (!url.trim()) {
showAlert('Empty URL', 'Please enter a URL to test.');
showAlert(
t('tools:wasmSettings.emptyUrlTitle'),
t('tools:wasmSettings.emptyUrlMessage')
);
return;
}
@@ -142,15 +152,17 @@ function initializePage() {
if (result.valid) {
updateStatus(packageName, true);
showAlert(
'Success',
`Connection to ${WasmProvider.getPackageDisplayName(packageName)} successful!`,
t('tools:wasmSettings.testSuccessTitle'),
t('tools:wasmSettings.connectionSuccess', {
package: WasmProvider.getPackageDisplayName(packageName),
}),
'success'
);
} else {
updateStatus(packageName, false);
showAlert(
'Connection Failed',
result.error || 'Could not connect to the URL.'
t('tools:wasmSettings.connectionFailedTitle'),
result.error || t('tools:wasmSettings.connectionFailedMessage')
);
}
}
@@ -168,7 +180,7 @@ function initializePage() {
});
saveBtn?.addEventListener('click', async () => {
showLoader('Saving configuration...');
showLoader(t('tools:wasmSettings.savingConfiguration'));
try {
if (pymupdfUrl.value.trim()) {
@@ -196,11 +208,19 @@ function initializePage() {
}
hideLoader();
showAlert('Saved', 'Configuration saved successfully!', 'success');
showAlert(
t('tools:wasmSettings.savedTitle'),
t('tools:wasmSettings.savedMessage'),
'success'
);
} catch (e: unknown) {
hideLoader();
const errorMessage = e instanceof Error ? e.message : 'Unknown error';
showAlert('Error', `Failed to save configuration: ${errorMessage}`);
const errorMessage =
e instanceof Error ? e.message : t('common.unknownError');
showAlert(
t('tools:wasmSettings.errorTitle'),
t('tools:wasmSettings.failedSave', { message: errorMessage })
);
}
});
@@ -225,10 +245,10 @@ function initializePage() {
WasmProvider.hasEnvDefault('cpdf');
showAlert(
'Reset',
t('tools:wasmSettings.resetTitle'),
hasDefaults
? 'Custom overrides cleared. Pre-configured defaults are active.'
: 'All configurations and cached modules have been cleared.',
? t('tools:wasmSettings.resetWithDefaults')
: t('tools:wasmSettings.resetNoDefaults'),
'success'
);
});