Drawaria AutoTranslate Chat

Automatically translates all chat messages and player names on Drawaria.online to English. Always active. Now shows detected language in tooltip.

// ==UserScript==
// @name         Drawaria AutoTranslate Chat
// @version      2.0.1
// @description  Automatically translates all chat messages and player names on Drawaria.online to English. Always active. Now shows detected language in tooltip.
// @namespace    drawaria.auto.translate.all.names
// @author       YouTubeDrawaria
// @match        https://drawaria.online/*
// @license      MIT
// @icon         https://www.google.com/s2/favicons?sz=64&domain=drawaria.online
// @grant        none
// @run-at       document-end

// ==/UserScript==

(function() {
    'use strict';

    // Helper to create DOM elements (simplified from original script)
    const domMake = {
        Tree: function(type, attrs, childrenArrayOrVarArgs) {
            const el = document.createElement(type);
            let children;
            if (Array.isArray(childrenArrayOrVarArgs)) {
                children = childrenArrayOrVarArgs;
            } else {
                children = [];
                for (let i = 2; i < arguments.length; i++) {
                    children.push(arguments[i]);
                }
            }

            for (let i = 0; i < children.length; i++) {
                const child = children[i];
                if (typeof child === "string") {
                    el.appendChild(document.createTextNode(child));
                } else {
                    if (child) {
                        el.appendChild(child);
                    }
                }
            }
            for (const attr in attrs) {
                if (attr === "className") {
                    el[attr] = attrs[attr];
                } else {
                    el.setAttribute(attr, attrs[attr]);
                }
            }
            return el;
        }
    };

    // Helper to observe DOM changes
    const observeDOM = (function() {
        const MutationObserver = window.MutationObserver || window.WebKitMutationObserver;
        return function(nodeToObserve, callback) {
            if (!nodeToObserve || nodeToObserve.nodeType !== 1) return;

            if (MutationObserver) {
                var mutationObserver = new MutationObserver(callback);
                mutationObserver.observe(nodeToObserve, { childList: true, subtree: false });
                return mutationObserver;
            } else if (window.addEventListener) {
                nodeToObserve.addEventListener("DOMNodeInserted", callback, false);
                nodeToObserve.addEventListener("DOMNodeRemoved", callback, false);
            }
        };
    })();

    // Helper for making XHR GET requests and parsing JSON
    function xhrGetJson(url, callback) {
        const req = new XMLHttpRequest();
        req.onload = (e) => {
            const response = req.response;
            if (!callback) return;
            try {
                callback(JSON.parse(response));
            } catch (error) {
                console.error("AutoTranslate: Error parsing JSON response", error);
            }
        };
        req.onerror = (e) => {
            console.error("AutoTranslate: XHR error", e);
        };
        req.open("GET", url);
        req.send();
    }

    class AutoTranslate {
        constructor() {
            this.init();
        }

        init() {
            // Log to console
            console.log("AutoTranslate (All Messages & Names): enabled");

            // Add a system message to the chatbox
            this.addSystemMessage("AutoTranslate (All Messages & Names): enabled (with language info)");

            const chatboxMessages = document.querySelector("#chatbox_messages");

            if (chatboxMessages) {
                observeDOM(chatboxMessages, (mutations) => {
                    mutations.forEach((record) => {
                        record.addedNodes.forEach((node) => {
                            // Process all chat messages and names, but exclude system messages
                            if (node.nodeType === 1 && !node.classList.contains("systemchatmessage5")) {

                                // --- Translate Message Text ---
                                const messageElement = node.querySelector(".playerchatmessage-text");
                                if (messageElement) {
                                    const textToTranslate = messageElement.textContent;
                                    // Use 'auto' for source language to let Google Translate detect it
                                    this.translate(textToTranslate, "auto", "en", (translation, detectedLang) => {
                                        this.applyTranslationAsTooltip(translation, detectedLang, messageElement);
                                    });
                                }

                                // --- Translate Player Name ---
                                const nameElement = node.querySelector(".playerchatmessage-name");
                                if (nameElement) {
                                    const nameToTranslate = nameElement.textContent;
                                    // Use 'auto' for source language to let Google Translate detect it
                                    this.translate(nameToTranslate, "auto", "en", (translation, detectedLang) => {
                                        this.applyTranslationAsTooltip(translation, detectedLang, nameElement);
                                    });
                                }

                                // --- Translate Player selfName ---
                                const selfnameElement = node.querySelector(".playerchatmessage-selfname");
                                if (selfnameElement) {
                                    const nameToTranslate = selfnameElement.textContent;
                                    // Use 'auto' for source language to let Google Translate detect it
                                    this.translate(nameToTranslate, "auto", "en", (translation, detectedLang) => {
                                        this.applyTranslationAsTooltip(translation, detectedLang, selfnameElement);
                                    });
                                }
                            }
                        });
                    });
                });
            } else {
                console.warn("AutoTranslate (All Messages & Names): Chatbox messages container (#chatbox_messages) not found. Auto-translation may not work.");
            }
        }

        translate(textToTranslate, fromLang = "auto", toLang = "en", callback) {
            const url =
                "https://translate.googleapis.com/translate_a/single?client=gtx&sl=" +
                fromLang +
                "&tl=" +
                toLang +
                "&dt=t&q=" +
                encodeURI(textToTranslate);

            xhrGetJson(url, (data) => {
                if (data && data[0] && data[0][0] && data[0][0][0]) {
                    const translatedText = data[0][0][0];
                    const detectedSourceLanguage = data[2] || 'unknown'; // data[2] contains the detected source language
                    callback(translatedText, detectedSourceLanguage);
                } else {
                    console.warn("AutoTranslate: Could not get translation for:", textToTranslate, data);
                    // Call callback with original text and unknown language if translation fails
                    callback(textToTranslate, 'unknown');
                }
            });
        }

        // Modified to include detected language in the tooltip
        applyTranslationAsTooltip(translatedText, detectedLangCode, targetNode) {
            // Get the full language name from the code, if possible
            const langName = new Intl.DisplayNames(['en'], { type: 'language' }).of(detectedLangCode);
            let tooltipText = translatedText;

            // Only append language info if it's not 'en' (since we're translating to English)
            // and if a meaningful language name was detected.
            if (detectedLangCode !== 'en' && detectedLangCode !== 'auto' && langName && langName !== detectedLangCode) {
                tooltipText += ` (${langName})`;
            }

            targetNode.title = tooltipText;
        }

        addSystemMessage(message) {
            const loggingContainer = document.getElementById("chatbox_messages");
            if (loggingContainer) {
                const chatmessage = domMake.Tree(
                    "div",
                    { class: `chatmessage systemchatmessage5`, "data-ts": Date.now(), style: `color: #17a2b8;` }, // Info color
                    [message]
                );
                loggingContainer.appendChild(chatmessage);
                loggingContainer.scrollTop = loggingContainer.scrollHeight; // Scroll to bottom
            }
        }
    }

    // Initialize the auto-translator
    // Small delay to ensure DOM is ready, though document-end should handle most cases.
    setTimeout(() => {
        new AutoTranslate();
    }, 500);

})();