Translate Discord Messages

Translate Discord messages and display them under the original text

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         Translate Discord Messages
// @namespace    http://tampermonkey.net/
// @version      0.3.0
// @description  Translate Discord messages and display them under the original text
// @author       CrankyBookworm
// @match        https://*.discord.com/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=discord.com
// @grant        GM.xmlHttpRequest
// @grant        GM_xmlhttpRequest
// @connect      translate.googleapis.com
// @esversion    11
// @run-at       document-end
// @license      MIT
// ==/UserScript==

/* jshint esversion: 11 */

const translateTo = "en";

let httpRequest;
if (typeof GM < "u" && GM.xmlHttpRequest) {
    httpRequest = GM.xmlHttpRequest;
}
else if (typeof GM < "u" && GM_xmlhttpRequest) {
    httpRequest = GM_xmlhttpRequest;
}
else if (typeof GM_xmlhttpRequest < "u") {
    httpRequest = GM_xmlhttpRequest;
}
else if (typeof GM < "u" && GM.xmlHttpRequest) {
    httpRequest = GM.xmlHttpRequest;
}
else {
    httpRequest = fetch;
    console.error("No HttpRequest Permission");
}

class TranslationResult {
    constructor() {
        /**
         * Translated Text.
         * @var {string?}
         */
        this.resultText = "";
        /**
         * Alternative translation results.
         * @var {string}
         */
        this.candidateText = "";
        /**
         * Translated from.
         * @var {string}
         */
        this.sourceLanguage = "";
        /**
         * Translation accuracy.
         * @var {float}
         */
        this.percentage = 0;
        /**
         * Is Error.
         * @var {bool}
         */
        this.isError = !1;
        /**
         * Error Message.
         * @var {string}
         */
        this.errorMessage = "";
    }
}

(function () {
    'use strict';

    const horizontalLine = document.createElement("hr");
    horizontalLine.style = "border: 0; padding-top: 1px; background: linear-gradient(to right, transparent, #d0d0d5, transparent);";

    const translateMsgElementTemplate = document.createElement("span");
    translateMsgElementTemplate.className = "myTranslateMsg";
    translateMsgElementTemplate.style = "color:#a4a9ab;";
    translateMsgElementTemplate.lang = translateTo;
    translateMsgElementTemplate.innerText = "Testing";


    /**
     * Translates a message
     * @param {string} textToTranslate Text that needs to be translated
     * @param {string?} fromLang Language to translate from
     * @param {string?} toLang Language to translate to
     * @returns {TranslationResult} Translation Result
     */
    async function translateText(textToTranslate, fromLang, toLang) {
        fromLang = fromLang ?? "auto";
        toLang = toLang ?? translateTo;
        const url = `https://translate.googleapis.com/translate_a/single?client=gtx&sl=${fromLang}&tl=${toLang}&dt=t&dt=bd&dj=1&q=${encodeURIComponent(textToTranslate)}`;

        var request = {
            "method": "GET",
            "timeout" : 6e4,
            "url": url,
            "synchronous" : !1,
        };

        /** @type {Response} */
        const response = await httpRequest(request).catch((e => e.response));
        const json_data = JSON.parse(await response.responseText);
        var result = new TranslationResult();

        // console.log(`response: Type: ${typeof response}. Value: ${response}`);
        if (response && 200 === (null == response ? void 0 : response.status)) {
            console.log(`json_data: Type: ${typeof json_data}. Value: ${json_data}`);
            result.sourceLanguage = json_data.src;
            result.percentage = json_data.ld_result.srclangs_confidences[0];
            result.resultText = json_data.sentences.map((e => e.trans)).join("");
            if(json_data.dict){
                result.candidateText = json_data.dict.map(
                    (e => `${e.pos}${"" != e.pos ? ": " : ""}${e.terms.join(", ")}\n`).join("")
                );
            }
        }
        else {
            result.isError = !0;
            if (response && 0 !== response.status) {
                if (429 === response.status || 503 === response.status) {
                    result.errorMessage = "Translation Service unavailable";
                }
                else {
                    result.errorMessage = `UnknownError occurred: [${null == response ? void 0 : response.status} ${null == response ? void 0 : response.statusText}]`;
                }
            }
            console.error("sendRequest()", result, response);
        }
        return result;
    }

    var observer = new MutationObserver(function (mutations) {
        /** @type {Array<Element>} */
        var messageElms = [];
        mutations.forEach(mutation => {
            mutation.addedNodes.forEach((node) => {
                if (node instanceof HTMLElement) {
                    messageElms = messageElms.concat(
                        Array.from(node.querySelectorAll('[aria-roledescription="Message"]'))
                    );
                }
            });
        });

        // console.log(`node: Type: ${typeof node}. Value: ${node}`);
        messageElms.forEach(async (messageElm) => {
            const msgContentElm = messageElm.querySelector('[class^="contents_"] [id^="message-content-"]');
            if (!msgContentElm.querySelector(".myTranslateMsg")) {
                const translation = await translateText(msgContentElm.innerText);
                if(translation.sourceLanguage === translateTo ||
                   translation.resultText === msgContentElm.innerText){
                    return;
                }

                // Create translation element template
                var msgTranslationText = document.createTextNode(translation.isError ? translation.errorMessage : translation.resultText);
                const msgTranslationElm = translateMsgElementTemplate.cloneNode();
                msgTranslationElm.appendChild(msgTranslationText);

                // Add Translation to end of message
                msgContentElm.appendChild(horizontalLine.cloneNode());
                msgContentElm.appendChild(msgTranslationElm);
            }
        });
    });

    // Start observing the target node for configured mutations
    observer.observe(document, { attributes: true, childList: true, subtree: true });
})();