Please bookmark this page to avoid losing your image tool!

Image Translation Creator

(Free & Supports Bulk Upload)

Drag & drop your images here or

The result will appear here...
You can edit the below JavaScript code to customize the image tool.
async function processImage(originalImg, targetLang = 'en') {

    /**
     * Dynamically loads a script if it's not already present in the document.
     * @param {string} url The URL of the script to load.
     * @returns {Promise<void>} A promise that resolves when the script is loaded.
     */
    const loadScript = (url) => {
        return new Promise((resolve, reject) => {
            // Check if Tesseract is already available on the window object
            if (window.Tesseract) {
                return resolve();
            }
            // Check if the script tag already exists
            if (document.querySelector(`script[src="${url}"]`)) {
                // If it exists but Tesseract is not ready, wait for it
                const interval = setInterval(() => {
                    if (window.Tesseract) {
                        clearInterval(interval);
                        resolve();
                    }
                }, 100);
                return;
            }
            const script = document.createElement('script');
            script.src = url;
            script.onload = () => resolve();
            script.onerror = (err) => reject(new Error(`Script load error for ${url}: ${err}`));
            document.head.appendChild(script);
        });
    };

    /**
     * Maps 3-letter Tesseract language codes to 2-letter ISO 639-1 codes for the translation API.
     * @param {string} tessLang The 3-letter language code from Tesseract (e.g., 'eng').
     * @returns {string|null} The 2-letter code (e.g., 'en') or null if not found.
     */
    const mapLangCode = (tessLang) => {
        const map = {
            'eng': 'en', 'rus': 'ru', 'deu': 'de', 'fra': 'fr', 'spa': 'es', 'ita': 'it', 
            'por': 'pt', 'nld': 'nl', 'jpn': 'ja', 'chi_sim': 'zh-CN', 'chi_tra': 'zh-TW', 'kor': 'ko'
        };
        return map[tessLang] || null;
    };
    
    /**
     * Gets an average color from the border of a bounding box for inpainting.
     * @param {CanvasRenderingContext2D} ctx The canvas context.
     * @param {object} bbox The bounding box object from Tesseract.
     * @param {number} margin The distance from the box to sample pixels.
     * @returns {string} An CSS rgb color string.
     */
    const getAverageBorderColor = (ctx, bbox, margin = 3) => {
        const { x0, y0, x1, y1 } = bbox;
        const { width, height } = ctx.canvas;
        const pointsData = [];

        const samplePoints = [
            {x: x0 + bbox.width / 2, y: y0 - margin}, {x: x0 + bbox.width / 2, y: y1 + margin},
            {x: x0 - margin, y: y0 + bbox.height / 2}, {x: x1 + margin, y: y0 + bbox.height / 2},
            {x: x0 - margin, y: y0 - margin}, {x: x1 + margin, y: y0 - margin},
            {x: x0 - margin, y: y1 + margin}, {x: x1 + margin, y: y1 + margin}
        ];
        
        for (const p of samplePoints) {
            if (p.x > 0 && p.x < width && p.y > 0 && p.y < height) {
                pointsData.push(ctx.getImageData(p.x, p.y, 1, 1).data);
            }
        }

        if (pointsData.length === 0) return '#FFFFFF'; // Fallback

        let r = 0, g = 0, b = 0;
        pointsData.forEach(p => { r += p[0]; g += p[1]; b += p[2]; });
        
        r = Math.floor(r / pointsData.length);
        g = Math.floor(g / pointsData.length);
        b = Math.floor(b / pointsData.length);

        return `rgb(${r}, ${g}, ${b})`;
    };

    /**
     * Draws wrapped text, attempting to fit it within a given bounding box.
     * @param {CanvasRenderingContext2D} ctx The canvas context.
     * @param {string} text The text to draw.
     * @param {object} bbox The bounding box to draw inside.
     * @param {string} bgColor The background color, used to determine text color.
     */
    const drawTextInBox = (ctx, text, bbox, bgColor) => {
        const colorMatch = bgColor.match(/rgb\((\d+), (\d+), (\d+)\)/);
        let r = 0, g = 0, b = 0;
        if (colorMatch) { [_, r, g, b] = colorMatch.map(Number); }
        const luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255;
        ctx.fillStyle = luminance > 0.5 ? '#111111' : '#FFFFFF';
        ctx.textAlign = 'center';
        ctx.textBaseline = 'middle';
        const fontFamily = 'Arial, "Helvetica Neue", Helvetica, sans-serif';

        let fontSize = bbox.height;
        let lines = [];
        
        // Iteratively find a font size that allows the text to fit
        while (fontSize > 6) {
            ctx.font = `bold ${fontSize}px ${fontFamily}`;
            const words = text.split(' ');
            lines = [];
            if (words.length > 0) {
              let currentLine = words[0] || '';
              for (let i = 1; i < words.length; i++) {
                  const word = words[i];
                  const testLine = currentLine + ' ' + word;
                  if (ctx.measureText(testLine).width > bbox.width && i > 0) {
                      lines.push(currentLine);
                      currentLine = word;
                  } else {
                      currentLine = testLine;
                  }
              }
              lines.push(currentLine);
            }

            if (lines.length * fontSize < bbox.height) break;
            fontSize -= 1;
        }

        const lineHeight = fontSize * 1.1;
        const totalTextHeight = lines.length * lineHeight;
        const startY = bbox.y0 + (bbox.height - totalTextHeight) / 2 + lineHeight / 2 - (lineHeight * 0.1);

        for (let i = 0; i < lines.length; i++) {
            ctx.fillText(lines[i], bbox.x0 + bbox.width / 2, startY + (i * lineHeight));
        }
    };


    // --- Main Function Logic ---
    
    // 1. Load Tesseract.js library
    try {
        await loadScript('https://unpkg.com/tesseract.js@5/dist/tesseract.min.js');
    } catch (error) {
        console.error(error);
        const errorCanvas = document.createElement('canvas');
        errorCanvas.width = originalImg.naturalWidth || 500;
        errorCanvas.height = originalImg.naturalHeight || 300;
        const errorCtx = errorCanvas.getContext('2d');
        errorCtx.drawImage(originalImg, 0, 0);
        errorCtx.fillStyle = 'rgba(255, 0, 0, 0.7)';
        errorCtx.fillRect(0, 0, errorCanvas.width, errorCanvas.height);
        errorCtx.fillStyle = 'white';
        errorCtx.textAlign = 'center';
        errorCtx.font = '24px Arial';
        errorCtx.fillText('Error: Could not load OCR library.', errorCanvas.width/2, errorCanvas.height/2);
        return errorCanvas;
    }

    // 2. Setup canvas and OCR worker
    const canvas = document.createElement('canvas');
    const ctx = canvas.getContext('2d', { willReadFrequently: true });
    canvas.width = originalImg.naturalWidth;
    canvas.height = originalImg.naturalHeight;
    ctx.drawImage(originalImg, 0, 0);

    const worker = await Tesseract.createWorker('eng+rus+deu+fra+spa', 1, {
        logger: m => console.log(m) // Logs progress to the console
    });
    
    // 3. Perform OCR
    const { data: { lines } } = await worker.recognize(canvas);

    // 4. Process each detected line of text
    for (const line of lines) {
        if (line.confidence < 50 || line.text.trim().length <= 1) continue;

        const textToTranslate = line.text;
        const sourceLangIso2 = mapLangCode(line.language);

        if (!sourceLangIso2 || sourceLangIso2 === targetLang) continue;

        let translatedText = textToTranslate;
        try {
            const apiUrl = `https://api.mymemory.translated.net/get?q=${encodeURIComponent(textToTranslate)}&langpair=${sourceLangIso2}|${targetLang}`;
            const response = await fetch(apiUrl);
            if (!response.ok) throw new Error(`API request failed: ${response.statusText}`);
            const translationData = await response.json();
            
            if (translationData.responseStatus === 200) {
                translatedText = translationData.responseData.translatedText;
            } else {
                 console.warn(`Translation service issue for: "${textToTranslate}"`, translationData.responseDetails);
            }
        } catch (error) {
            console.error('Translation API error:', error);
            continue; // Skip this line on error
        }
        
        if (translatedText.trim().toLowerCase() === textToTranslate.trim().toLowerCase()) continue;

        // 5. Inpaint (erase original text)
        const bbox = line.bbox;
        const avgColor = getAverageBorderColor(ctx, bbox);
        ctx.fillStyle = avgColor;
        ctx.fillRect(bbox.x0 - 2, bbox.y0 - 2, bbox.width + 4, bbox.height + 4);

        // 6. Draw translated text
        drawTextInBox(ctx, translatedText, bbox, avgColor);
    }

    // 7. Cleanup
    await worker.terminate();

    // 8. Return result canvas
    return canvas;
}

Free Image Tool Creator

Can't find the image tool you're looking for?
Create one based on your own needs now!

Description

The Image Translation Creator is a tool designed to extract text from images and translate it into a specified language. It utilizes Optical Character Recognition (OCR) technology to identify text in various languages and seamlessly replaces the original text with its translation in the image. This tool is useful for users needing to translate foreign language signs, posters, documents, or any visual content directly from images. Applications include enhancing travel experiences, assisting in language learning, or enabling accessibility for multilingual communications.

Leave a Reply

Your email address will not be published. Required fields are marked *