You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(originalImg, defaultOcrLang = 'eng', defaultTransLang = 'es') {
// Main Wrapper Container
const container = document.createElement('div');
container.style.fontFamily = 'system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif';
container.style.maxWidth = '900px';
container.style.margin = '0 auto';
container.style.padding = '20px';
container.style.boxSizing = 'border-box';
container.style.backgroundColor = '#ffffff';
container.style.borderRadius = '12px';
container.style.boxShadow = '0 4px 15px rgba(0,0,0,0.08)';
container.style.border = '1px solid #e0e0e0';
// Header Title
const title = document.createElement('h2');
title.textContent = 'Image Scanner, Identifier & Translator';
title.style.marginTop = '0';
title.style.color = '#333';
title.style.borderBottom = '2px solid #f0f0f0';
title.style.paddingBottom = '10px';
container.appendChild(title);
// Control Panel
const controls = document.createElement('div');
controls.style.display = 'flex';
controls.style.gap = '15px';
controls.style.alignItems = 'center';
controls.style.marginBottom = '20px';
controls.style.flexWrap = 'wrap';
// OCR Language Selector
const ocrSelect = document.createElement('select');
ocrSelect.style.padding = '8px';
ocrSelect.style.borderRadius = '6px';
ocrSelect.style.border = '1px solid #ccc';
const tesseractLangs = {
'eng': 'English', 'spa': 'Spanish', 'fra': 'French',
'deu': 'German', 'chi_sim': 'Chinese', 'jpn': 'Japanese', 'rus': 'Russian',
'por': 'Portuguese', 'ita': 'Italian', 'kor': 'Korean', 'hin': 'Hindi'
};
for (const [code, name] of Object.entries(tesseractLangs)) {
const option = document.createElement('option');
option.value = code;
option.textContent = name;
if (code === defaultOcrLang) option.selected = true;
ocrSelect.appendChild(option);
}
// Target Translator Language Selector
const transSelect = document.createElement('select');
transSelect.style.padding = '8px';
transSelect.style.borderRadius = '6px';
transSelect.style.border = '1px solid #ccc';
const googleLangs = {
'en': 'English', 'es': 'Spanish', 'fr': 'French',
'de': 'German', 'zh-CN': 'Chinese (Simp)', 'ja': 'Japanese', 'ru': 'Russian',
'pt': 'Portuguese', 'it': 'Italian', 'ko': 'Korean', 'hi': 'Hindi', 'ar': 'Arabic'
};
for (const [code, name] of Object.entries(googleLangs)) {
const option = document.createElement('option');
option.value = code;
option.textContent = name;
if (code === defaultTransLang) option.selected = true;
transSelect.appendChild(option);
}
// Action Button
const runBtn = document.createElement('button');
runBtn.textContent = 'Scan & Translate';
runBtn.style.padding = '8px 20px';
runBtn.style.backgroundColor = '#0070f3';
runBtn.style.color = '#fff';
runBtn.style.border = 'none';
runBtn.style.borderRadius = '6px';
runBtn.style.fontWeight = 'bold';
runBtn.style.cursor = 'pointer';
runBtn.style.boxShadow = '0 2px 4px rgba(0,112,243,0.3)';
runBtn.onmouseover = () => runBtn.style.backgroundColor = '#0051b3';
runBtn.onmouseout = () => runBtn.style.backgroundColor = '#0070f3';
const createLabel = (text) => {
const lbl = document.createElement('span');
lbl.textContent = text;
lbl.style.fontWeight = '500';
lbl.style.color = '#555';
lbl.style.fontSize = '14px';
return lbl;
};
controls.appendChild(createLabel('Image Language:'));
controls.appendChild(ocrSelect);
controls.appendChild(createLabel('Translate to:'));
controls.appendChild(transSelect);
controls.appendChild(runBtn);
container.appendChild(controls);
// Image Display Canvas
const imgWrapper = document.createElement('div');
imgWrapper.style.marginBottom = '20px';
imgWrapper.style.textAlign = 'center';
imgWrapper.style.backgroundColor = '#f8f9fa';
imgWrapper.style.padding = '10px';
imgWrapper.style.borderRadius = '8px';
imgWrapper.style.border = '1px dashed #ccc';
const canvas = document.createElement('canvas');
canvas.style.maxWidth = '100%';
canvas.style.maxHeight = '400px';
canvas.style.objectFit = 'contain';
canvas.style.borderRadius = '4px';
// Draw Original Image on Canvas
const ctx = canvas.getContext('2d');
canvas.width = originalImg.width;
canvas.height = originalImg.height;
ctx.drawImage(originalImg, 0, 0);
imgWrapper.appendChild(canvas);
container.appendChild(imgWrapper);
// Status / Progress Bar
const statusBox = document.createElement('div');
statusBox.style.padding = '12px 15px';
statusBox.style.backgroundColor = '#e0f7fa';
statusBox.style.color = '#006064';
statusBox.style.borderRadius = '6px';
statusBox.style.marginBottom = '20px';
statusBox.style.fontWeight = 'bold';
statusBox.style.fontSize = '14px';
statusBox.style.display = 'flex';
statusBox.style.alignItems = 'center';
statusBox.textContent = 'Awaiting initialization...';
container.appendChild(statusBox);
// Text Areas for OCR and Translation
const textContainer = document.createElement('div');
textContainer.style.display = 'flex';
textContainer.style.gap = '20px';
textContainer.style.flexWrap = 'wrap';
const createTextAreaBlock = (titleText, placeholder) => {
const block = document.createElement('div');
block.style.flex = '1';
block.style.minWidth = '280px';
block.style.display = 'flex';
block.style.flexDirection = 'column';
const label = document.createElement('label');
label.textContent = titleText;
label.style.fontWeight = '600';
label.style.marginBottom = '8px';
label.style.color = '#333';
label.style.fontSize = '14px';
const textarea = document.createElement('textarea');
textarea.style.height = '180px';
textarea.style.padding = '12px';
textarea.style.border = '1px solid #dcdcdc';
textarea.style.borderRadius = '6px';
textarea.style.fontFamily = 'inherit';
textarea.style.fontSize = '14px';
textarea.style.lineHeight = '1.5';
textarea.style.resize = 'vertical';
textarea.style.backgroundColor = '#fafafa';
textarea.placeholder = placeholder;
block.appendChild(label);
block.appendChild(textarea);
return { block, textarea };
};
const sourceBlock = createTextAreaBlock('Extracted Text (Editable)', 'Image text will appear here...');
const translatedBlock = createTextAreaBlock('Translated Result', 'Translation will appear here...');
translatedBlock.textarea.readOnly = true;
translatedBlock.textarea.style.backgroundColor = '#eef3fb';
textContainer.appendChild(sourceBlock.block);
textContainer.appendChild(translatedBlock.block);
container.appendChild(textContainer);
// Core Processing Functions
const translateText = async () => {
const text = sourceBlock.textarea.value.trim();
if (!text) {
translatedBlock.textarea.value = '';
statusBox.textContent = 'Ready.';
return;
}
statusBox.textContent = 'Translating...';
statusBox.style.backgroundColor = '#fff3e0';
statusBox.style.color = '#ef6c00';
try {
// Using a highly reliable cross-origin Google Translate client API endpoint
const targetLang = transSelect.value;
const url = `https://translate.googleapis.com/translate_a/single?client=gtx&sl=auto&tl=${targetLang}&dt=t&q=${encodeURIComponent(text)}`;
const response = await fetch(url);
const json = await response.json();
let translated = '';
if (json && json[0]) {
for (let i = 0; i < json[0].length; i++) {
if (json[0][i][0]) translated += json[0][i][0];
}
}
translatedBlock.textarea.value = translated;
const detectedLang = json[2] ? json[2].toUpperCase() : 'AUTO';
statusBox.style.backgroundColor = '#e8f5e9';
statusBox.style.color = '#2e7d32';
statusBox.textContent = `Completed! Detected input language: ${detectedLang}`;
} catch (err) {
console.error('Translation error:', err);
statusBox.style.backgroundColor = '#ffebee';
statusBox.style.color = '#c62828';
statusBox.textContent = `Translation Error: ${err.message}`;
}
};
// Make UI interactive: translating instantly when target language changes or when user edits OCR output manually
transSelect.addEventListener('change', translateText);
let typingTimer;
sourceBlock.textarea.addEventListener('input', () => {
clearTimeout(typingTimer);
typingTimer = setTimeout(translateText, 700); // 700ms debounce
});
const loadTesseract = () => {
return new Promise((resolve, reject) => {
if (window.Tesseract) return resolve();
let script = document.getElementById('tesseract-ocr-script');
if (script) {
// Script tag exists, wait for it to assign window.Tesseract
const interval = setInterval(() => {
if (window.Tesseract) {
clearInterval(interval);
resolve();
}
}, 100);
return;
}
script = document.createElement('script');
script.id = 'tesseract-ocr-script';
script.src = 'https://unpkg.com/tesseract.js@5.0.3/dist/tesseract.min.js';
script.onload = resolve;
script.onerror = reject;
document.head.appendChild(script);
});
};
const runFullProcess = async () => {
runBtn.disabled = true;
runBtn.style.opacity = '0.6';
runBtn.style.cursor = 'not-allowed';
const previousStatusColor = statusBox.style.color;
const previousStatusBg = statusBox.style.backgroundColor;
try {
sourceBlock.textarea.value = '';
translatedBlock.textarea.value = '';
statusBox.style.backgroundColor = '#e3f2fd';
statusBox.style.color = '#1565c0';
statusBox.textContent = 'Loading OCR Engine (Tesseract.js)...';
await loadTesseract();
statusBox.textContent = 'Engine Loaded. Initializing Scan...';
const selectedOcrLang = ocrSelect.value;
const result = await window.Tesseract.recognize(originalImg, selectedOcrLang, {
logger: m => {
if (m.status === 'recognizing text') {
const progress = Math.round(m.progress * 100);
statusBox.textContent = `Scanning Image... ${progress}%`;
} else if (m.status === 'loading language traineddata') {
statusBox.textContent = `Downloading ${selectedOcrLang} language components (${Math.round(m.progress * 100)}%)`;
} else {
statusBox.textContent = `Processing: ${m.status}`;
}
}
});
const text = result.data.text;
sourceBlock.textarea.value = text;
if (!text.trim()) {
statusBox.textContent = 'Finished. No discernible text found in the image.';
statusBox.style.backgroundColor = '#fff3e0';
statusBox.style.color = '#e65100';
return;
}
// Once text is extracted, automatically translate it
await translateText();
} catch (err) {
console.error('OCR Process failed:', err);
statusBox.style.backgroundColor = '#ffebee';
statusBox.style.color = '#c62828';
statusBox.textContent = `Scanner Error: ${err.message}`;
} finally {
runBtn.disabled = false;
runBtn.style.opacity = '1';
runBtn.style.cursor = 'pointer';
}
};
runBtn.addEventListener('click', runFullProcess);
// Auto-run the scanner as soon as the component mounts
setTimeout(runFullProcess, 100);
return container;
}
Apply Changes