You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(originalImg, defaultOcrLang = "eng", targetLang = "en") {
// Create the main container
const container = document.createElement('div');
container.style.fontFamily = "system-ui, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif";
container.style.padding = "20px";
container.style.maxWidth = "800px";
container.style.margin = "0 auto";
container.style.backgroundColor = "#fafafa";
container.style.border = "1px solid #e0e0e0";
container.style.borderRadius = "10px";
container.style.boxShadow = "0 4px 6px rgba(0,0,0,0.05)";
// Title
const title = document.createElement('h2');
title.innerText = "Image Language Identifier & Translator";
title.style.marginTop = "0";
title.style.color = "#333";
title.style.textAlign = "center";
container.appendChild(title);
// Canvas to hold the extracted image
const canvas = document.createElement('canvas');
canvas.style.maxWidth = "100%";
canvas.style.maxHeight = "400px";
canvas.style.display = "block";
canvas.style.margin = "0 auto 20px auto";
canvas.style.borderRadius = "5px";
canvas.style.border = "1px solid #ccc";
const ctx = canvas.getContext('2d');
canvas.width = originalImg.width;
canvas.height = originalImg.height;
ctx.drawImage(originalImg, 0, 0);
container.appendChild(canvas);
// Status / Loading Area
const statusArea = document.createElement('div');
statusArea.style.marginBottom = "20px";
statusArea.style.padding = "15px";
statusArea.style.fontWeight = "bold";
statusArea.style.color = "#0056b3";
statusArea.style.backgroundColor = "#e7f1ff";
statusArea.style.border = "1px solid #b6d4fe";
statusArea.style.borderRadius = "5px";
statusArea.style.textAlign = "center";
statusArea.innerText = "Initializing processing engine...";
container.appendChild(statusArea);
// Results Area (Hidden Initially)
const resultArea = document.createElement('div');
resultArea.style.display = "none";
const preStyle = "white-space: pre-wrap; word-wrap: break-word; background: #fff; padding: 12px; border: 1px solid #ddd; border-radius: 6px; max-height: 250px; overflow-y: auto; font-family: monospace; font-size: 14px; margin-top: 8px; color: #333;";
resultArea.innerHTML = `
<div style="margin-bottom: 20px;">
<strong style="color: #444;">Extracted Text:</strong>
<div id="extracted-text" style="${preStyle}"></div>
</div>
<div style="margin-bottom: 20px; background: #eefee8; padding: 15px; border: 1px solid #c3e6cb; border-radius: 6px;">
<strong style="color: #155724; font-size: 16px;">Detected Language: </strong>
<span id="detected-lang" style="font-weight: bold; color: #155724; font-size: 16px;"></span>
</div>
<div>
<strong style="color: #444;">Translated Text (to ${targetLang}):</strong>
<div id="translated-text" style="${preStyle}"></div>
</div>
`;
container.appendChild(resultArea);
const langNames = {
"af": "Afrikaans", "sq": "Albanian", "am": "Amharic", "ar": "Arabic", "hy": "Armenian", "az": "Azerbaijani",
"eu": "Basque", "be": "Belarusian", "bn": "Bengali", "bs": "Bosnian", "bg": "Bulgarian", "ca": "Catalan",
"ceb": "Cebuano", "ny": "Chichewa", "zh-CN": "Chinese (Simplified)", "zh-TW": "Chinese (Traditional)",
"co": "Corsican", "hr": "Croatian", "cs": "Czech", "da": "Danish", "nl": "Dutch", "en": "English",
"eo": "Esperanto", "et": "Estonian", "tl": "Filipino", "fi": "Finnish", "fr": "French", "fy": "Frisian",
"gl": "Galician", "ka": "Georgian", "de": "German", "el": "Greek", "gu": "Gujarati", "ht": "Haitian Creole",
"ha": "Hausa", "haw": "Hawaiian", "iw": "Hebrew", "hi": "Hindi", "hmn": "Hmong", "hu": "Hungarian",
"is": "Icelandic", "ig": "Igbo", "id": "Indonesian", "ga": "Irish", "it": "Italian", "ja": "Japanese",
"jw": "Javanese", "kn": "Kannada", "kk": "Kazakh", "km": "Khmer", "ko": "Korean", "ku": "Kurdish (Kurmanji)",
"ky": "Kyrgyz", "lo": "Lao", "la": "Latin", "lv": "Latvian", "lt": "Lithuanian", "lb": "Luxembourgish",
"mk": "Macedonian", "mg": "Malagasy", "ms": "Malay", "ml": "Malayalam", "mt": "Maltese", "mi": "Maori",
"mr": "Marathi", "mn": "Mongolian", "my": "Myanmar (Burmese)", "ne": "Nepali", "no": "Norwegian",
"ps": "Pashto", "fa": "Persian", "pl": "Polish", "pt": "Portuguese", "pa": "Punjabi", "ro": "Romanian",
"ru": "Russian", "sm": "Samoan", "gd": "Scots Gaelic", "sr": "Serbian", "st": "Sesotho", "sn": "Shona",
"sd": "Sindhi", "si": "Sinhala", "sk": "Slovak", "sl": "Slovenian", "so": "Somali", "es": "Spanish",
"su": "Sundanese", "sw": "Swahili", "sv": "Swedish", "tg": "Tajik", "ta": "Tamil", "te": "Telugu",
"th": "Thai", "tr": "Turkish", "uk": "Ukrainian", "ur": "Urdu", "uz": "Uzbek", "vi": "Vietnamese",
"cy": "Welsh", "xh": "Xhosa", "yi": "Yiddish", "yo": "Yoruba", "zu": "Zulu"
};
// Process logic asynchronously to not block UI thread
(async () => {
try {
// Dynamically load Tesseract.js if not available
if (!window.Tesseract) {
statusArea.innerText = "Loading Tesseract.js Engine...";
await new Promise((resolve, reject) => {
const script = document.createElement('script');
script.src = "https://unpkg.com/tesseract.js@4.1.4/dist/tesseract.min.js";
script.onload = resolve;
script.onerror = () => reject(new Error("Failed to load Tesseract.js"));
document.head.appendChild(script);
});
}
let recognizeLang = defaultOcrLang;
let detectWorker = null;
// Step 1: Detect Orientation and Script to select proper OCR language map automatically
try {
detectWorker = await Tesseract.createWorker({
logger: m => {
if(m.status === "loading tesseract core" || m.status.includes("osd")) {
statusArea.innerText = `Preparing Script Detection: ${m.status}...`;
}
}
});
await detectWorker.loadLanguage('osd');
await detectWorker.initialize('osd');
statusArea.innerText = "Analyzing script on the image...";
const detectResult = await detectWorker.detect(canvas);
const scriptType = detectResult?.data?.script;
// Map common OCR scripts to appropriate language bundles
const scriptMap = {
'Latin': 'eng+spa+fra+deu+ita+por', // Includes accents
'Cyrillic': 'rus+ukr+bel',
'Han': 'chi_sim+chi_tra',
'Japanese': 'jpn',
'Hangul': 'kor',
'Arabic': 'ara',
'Hebrew': 'heb',
'Thai': 'tha',
'Devanagari': 'hin'
};
if (scriptType && scriptMap[scriptType]) {
recognizeLang = scriptMap[scriptType];
}
} catch (err) {
console.warn("OSD Script detection skipped or failed. Falling back to default languages.", err);
} finally {
if (detectWorker) await detectWorker.terminate();
}
// Step 2: Optical Character Recognition (OCR) extraction
const worker = await Tesseract.createWorker({
logger: m => {
if (m.status === 'recognizing text') {
statusArea.innerText = `Extracting Text: ${Math.round(m.progress * 100)}%`;
} else {
statusArea.innerText = `Loading OCR language models (${recognizeLang})...`;
}
}
});
await worker.loadLanguage(recognizeLang);
await worker.initialize(recognizeLang);
statusArea.innerText = "Extracting text contents...";
const { data: { text } } = await worker.recognize(canvas);
await worker.terminate();
const extractedText = text.trim();
if (!extractedText) {
statusArea.innerText = "Process Complete: No text could be found on the image.";
statusArea.style.backgroundColor = "#fff3cd";
statusArea.style.color = "#856404";
statusArea.style.borderColor = "#ffeeba";
return;
}
// Step 3: Identify Language & Translate via unofficially free Google Translate API
statusArea.innerText = "Identifying language and translating...";
// Using POST to handle large bodies of extracted text securely inside API limits
const gtUrl = `https://translate.googleapis.com/translate_a/single?client=gtx&sl=auto&tl=${targetLang}&dt=t`;
const response = await fetch(gtUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: `q=${encodeURIComponent(extractedText)}`
});
if (!response.ok) throw new Error("Translation API encountered an error.");
const json = await response.json();
let translatedText = "";
if (json[0]) {
json[0].forEach(part => {
if (part[0]) translatedText += part[0];
});
}
const detectedCode = json[2] || "Unknown";
const humanReadableLang = langNames[detectedCode] ? `${langNames[detectedCode]} (${detectedCode})` : detectedCode;
// Final Update: Reveal Results
statusArea.style.display = "none";
resultArea.style.display = "block";
resultArea.querySelector('#extracted-text').innerText = extractedText;
resultArea.querySelector('#detected-lang').innerText = humanReadableLang;
resultArea.querySelector('#translated-text').innerText = translatedText;
} catch (error) {
console.error("Processing Error:", error);
statusArea.innerText = "An error occurred: " + error.message;
statusArea.style.backgroundColor = "#f8d7da";
statusArea.style.color = "#721c24";
statusArea.style.borderColor = "#f5c6cb";
}
})();
return container;
}
Apply Changes