Please bookmark this page to avoid losing your image tool!

Image Translator With Upload Or Link Support

(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', sourceLang = 'eng') {
    /**
     * Dynamically loads a script and returns a promise that resolves when the script is loaded.
     * @param {string} url - The URL of the script to load.
     * @returns {Promise<void>}
     */
    const loadScript = (url) => {
        return new Promise((resolve, reject) => {
            // Check if Tesseract is already available
            if (window.Tesseract) {
                return resolve();
            }
            // Check if script tag already exists
            let script = document.querySelector(`script[src="${url}"]`);
            if (script) {
                // If it exists, add load listeners and resolve when done
                script.addEventListener('load', () => resolve(), {
                    once: true
                });
                script.addEventListener('error', () => reject(new Error(`Script load error for ${url}`)), {
                    once: true
                });
            } else {
                // Otherwise, create and append the script
                script = document.createElement('script');
                script.src = url;
                script.onload = resolve;
                script.onerror = () => reject(new Error(`Failed to load script: ${url}`));
                document.head.appendChild(script);
            }
        });
    };

    /**
     * Translates a given text using a free public API.
     * @param {string} text - The text to translate.
     * @param {string} from - The source language code (2 letters, e.g., 'en').
     * @param {string} to - The target language code (2 letters, e.g., 'es').
     * @returns {Promise<string>} - The translated text, or original text on failure.
     */
    const translateText = async (text, from, to) => {
        if (!text || text.trim() === '') {
            return '';
        }
        try {
            const apiUrl = `https://api.mymemory.translated.net/get?q=${encodeURIComponent(text)}&langpair=${from}|${to}`;
            const response = await fetch(apiUrl);
            if (!response.ok) {
                console.error('Translation API request failed:', response.statusText);
                return text; // Return original text on API-level failure
            }
            const data = await response.json();
            if (data.responseData && data.responseData.translatedText) {
                // Decode HTML entities that the API sometimes returns
                const tempDiv = document.createElement('div');
                tempDiv.innerHTML = data.responseData.translatedText;
                return tempDiv.textContent || tempDiv.innerText || "";
            } else {
                return text; // Return original text if translation not found
            }
        } catch (error) {
            console.error('Error during translation:', error);
            return text; // Return original text on network error
        }
    };

    /**
     * Calculates the largest font size that allows a single line of text to fit within given dimensions.
     * @param {CanvasRenderingContext2D} ctx - The canvas rendering context.
     * @param {string} text - The text to measure.
     * @param {number} maxWidth - The maximum width for the text.
     * @param {number} maxHeight - The maximum height for the text.
     * @returns {string} - The CSS font property string (e.g., "20px sans-serif").
     */
    const getFittingFont = (ctx, text, maxWidth, maxHeight) => {
        let fontSize = Math.floor(maxHeight);
        if (fontSize <= 0) return `1px sans-serif`;

        while (fontSize > 0) {
            ctx.font = `${fontSize}px sans-serif`;
            const metrics = ctx.measureText(text);
            if (metrics.width < maxWidth) {
                return `${fontSize}px sans-serif`;
            }
            fontSize -= 1;
        }
        return `1px sans-serif`; // Fallback for text that cannot fit
    };

    // --- Main Logic ---

    const TESSERACT_CDN = 'https://cdn.jsdelivr.net/npm/tesseract.js@5/dist/tesseract.min.js';
    const canvas = document.createElement('canvas');
    const ctx = canvas.getContext('2d');
    canvas.width = originalImg.naturalWidth;
    canvas.height = originalImg.naturalHeight;

    let worker; // Tesseract worker instance

    try {
        // Step 1: Initialize canvas and show a processing message
        ctx.drawImage(originalImg, 0, 0);
        ctx.fillStyle = 'rgba(0, 0, 0, 0.6)';
        ctx.fillRect(0, 0, canvas.width, canvas.height);
        ctx.fillStyle = 'white';
        ctx.textAlign = 'center';
        ctx.textBaseline = 'middle';
        ctx.font = `bold ${Math.min(canvas.width, canvas.height) / 15}px sans-serif`;
        ctx.fillText('Initializing...', canvas.width / 2, canvas.height / 2);

        // Step 2: Load Tesseract.js library
        await loadScript(TESSERACT_CDN);

        // Step 3: Perform OCR
        ctx.fillText('Performing OCR...', canvas.width / 2, canvas.height / 2);
        // Tesseract language codes are 3 letters (e.g., 'eng', 'rus', 'jpn')
        worker = await Tesseract.createWorker(sourceLang);
        const {
            data: {
                paragraphs
            }
        } = await worker.recognize(canvas);


        // Step 4: Translate the recognized text
        ctx.fillText('Translating text...', canvas.width / 2, canvas.height / 2);

        // Note: Translation API uses 2-letter codes. This conversion is a heuristic.
        const sourceLang2Letter = sourceLang.substring(0, 2);
        const translationPromises = paragraphs.map(p => translateText(p.text, sourceLang2Letter, targetLang));
        const translatedTexts = await Promise.all(translationPromises);

        // Step 5: Render the final image
        // Redraw the original image to clear the processing messages
        ctx.drawImage(originalImg, 0, 0);

        // Overlay the translated text
        paragraphs.forEach((p, index) => {
            const translatedText = translatedTexts[index];
            if (!translatedText || translatedText.trim() === '') return;

            const bbox = p.bbox;
            const textToDraw = translatedText.replace(/(\r\n|\n|\r)/gm, " "); // Flatten to single line

            // Cover the original text with a white background
            ctx.fillStyle = 'white';
            ctx.fillRect(bbox.x0, bbox.y0, bbox.x1 - bbox.x0, bbox.y1 - bbox.y0);

            // Draw the translated text, fitting it into the box
            ctx.fillStyle = 'black';
            ctx.textBaseline = 'top';
            ctx.textAlign = 'left';

            const font = getFittingFont(ctx, textToDraw, bbox.x1 - bbox.x0, bbox.y1 - bbox.y0);
            ctx.font = font;
            // Vertically center the text within the bounding box
            const textMetrics = ctx.measureText(textToDraw);
            const fontHeight = textMetrics.actualBoundingBoxAscent + textMetrics.actualBoundingBoxDescent;
            const yPos = bbox.y0 + ((bbox.y1 - bbox.y0) - fontHeight) / 2;

            ctx.fillText(textToDraw, bbox.x0, yPos);
        });

        return canvas;

    } catch (error) {
        console.error("An error occurred during image translation:", error);
        // In case of an error, return the original image with an error message overlay
        ctx.drawImage(originalImg, 0, 0);
        ctx.fillStyle = 'rgba(200, 0, 0, 0.8)';
        ctx.fillRect(0, 0, canvas.width, canvas.height);
        ctx.fillStyle = 'white';
        ctx.textAlign = 'center';
        ctx.textBaseline = 'middle';
        const fontSize = Math.min(canvas.width, canvas.height) / 20;
        ctx.font = `bold ${fontSize}px sans-serif`;
        ctx.fillText('An Error Occurred', canvas.width / 2, canvas.height / 2 - fontSize);
        ctx.font = `${fontSize * 0.7}px sans-serif`;
        ctx.fillText(error.message || 'Please check console for details.', canvas.width / 2, canvas.height / 2 + fontSize * 0.7);
        return canvas;
    } finally {
        // Ensure the Tesseract worker is always terminated
        if (worker) {
            await worker.terminate();
        }
    }
}

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 Translator With Upload or Link Support’ tool allows users to upload an image or provide a link to an image containing text in any language. It uses Optical Character Recognition (OCR) to extract the text from the image and then translates the text into a specified target language. This tool is useful for translating signs, documents, or any textual content in images, making it ideal for travelers, language learners, and professionals who need quick translations from visual content.

Leave a Reply

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