翻译

try to translate the world!

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

You will need to install an extension such as Tampermonkey to install this script.

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         翻译
// @namespace    http://tampermonkey.net/
// @version      0.5
// @description  try to translate the world!
// @author       su
// @match        http://*/*
// @include      https://*/*
// @grant        GM_xmlhttpRequest
// @connect      fy.iciba.com
// @connect      api.ai.qq.com
// @connect      translate.google.cn
// @require      http://cdn.bootcss.com/blueimp-md5/1.1.0/js/md5.min.js
// ==/UserScript==
(function() {
    //金山词霸翻译接口
    const cibaUrl = "http://fy.iciba.com/ajax.php?a=fy";
    //css样式
    const style = {
            'ballStyle': 'position: absolute;z-index: 102;top: 2px;right: 2px;width: 44px;height: 22px;border-radius: 11px;display: flex;align-items: center;border: solid 1px rgba(255, 255, 255, 0.5);background-color: darkgray;cursor: pointer;',
            'traStyle': 'position: absolute;z-index: 101;top: 0;right: 0;width: 100%;border-radius: 6px;font: 13px/normal Arial, Helvetica;box-shadow: 0 0 0 1px rgba(0, 0, 0, .05), 0 2px 3px 0 rgba(0, 0, 0, .1);visibility: hidden;',
            'selectStyle': 'width: 60%;margin-top: 3px;border: 1px solid #ececec;height: 28px;border-radius: 2px;color: #666;',
            'textareaStyle': 'height: 96px;top: 40px;width: 100%;font-weight: normal;font-family: "Roboto", Sans-Serif;outline: none;-webkit-appearance: none;white-space: pre-wrap;word-wrap: break-word;z-index: 2;background: transparent;border-width: 0;',
            'dTextStyle': 'height: auto;min-height: 120px;padding: 30px 20px 12px 20px;overflow: hidden;'
        }
        //下拉框
    const select = '<option value="en" selected>英语</option><option value="zh-CN">简体中文</option><option value="ar">阿拉伯语</option><option value="et">爱沙尼亚语</option><option value="bg">保加利亚语</option><option value="is">冰岛语</option><option value="pl">波兰语</option><option value="bs-Latn">波斯尼亚语(拉丁语)</option><option value="fa">波斯语</option><option value="da">丹麦语</option><option value="de">德语</option><option value="ru">俄语</option><option value="fr">法语</option><option value="zh-TW">繁体中文</option><option value="fil">菲律宾语</option><option value="fj">斐济语</option><option value="fi">芬兰语</option><option value="ht">海地克里奥尔语</option><option value="ko">韩语</option><option value="nl">荷兰语</option><option value="ca">加泰罗尼亚语</option><option value="cs">捷克语</option><option value="otq">克雷塔罗奥托米语</option><option value="tlh">克林贡语</option><option value="hr">克罗地亚语</option><option value="lv">拉脱维亚语</option><option value="lt">立陶宛语</option><option value="ro">罗马尼亚语</option><option value="mg">马达加斯加语</option><option value="mt">马耳他语</option><option value="ms">马来语(拉丁语)</option><option value="bn-BD">孟加拉语</option><option value="mww">苗语</option><option value="af">南非荷兰语</option><option value="pt">葡萄牙语</option><option value="ja">日语</option><option value="sv">瑞典语</option><option value="sm">萨摩亚语</option><option value="sr-Latn">塞尔维亚语(拉丁语)</option><option value="sr-Cyrl">塞尔维亚语(西里尔文)</option><option value="no">书面挪威语</option><option value="sk">斯洛伐克语</option><option value="sl">斯洛文尼亚语</option><option value="sw">斯瓦希里语</option><option value="ty">塔希提语</option><option value="te">泰卢固语</option><option value="ta">泰米尔语</option><option value="th">泰语</option><option value="tr">土耳其语</option><option value="cy">威尔士语</option><option value="ur">乌尔都语</option><option value="uk">乌克兰语</option><option value="es">西班牙语</option><option value="he">希伯来语</option><option value="el">希腊语</option><option value="hu">匈牙利语</option><option value="it">意大利语</option><option value="hi">印地语</option><option value="id">印度尼西亚语</option><option value="yua">尤卡坦玛雅语</option><option value="vi">越南语</option>';

    function Main_Su() {
        this.main_s = null;
        this.ball = null;
        this.tra = null;
        this.ori_select = null;
        this.tra_select = null;
        this.ori_text = null;
        this.tra_text = null;
        //控制翻译框显示
        this.hide = true;
        //控制拖放
        this.mouseBeginX;
        this.mouseBeginY;
        this.diffX;
        this.diffY;
        this.isMouseDown = false;
    }
    Main_Su.prototype.mouseDown = function(e) {
        this.isMouseDown = true;
        this.mouseBeginX = e.clientX;
        this.mouseBeginY = e.clientY;
        var elmX = this.main_s.offsetLeft;
        var elmY = this.main_s.offsetTop;
        var elmWidth = this.main_s.offsetWidth;
        var elmHeight = this.main_s.offsetHeight;
        this.diffX = this.mouseBeginX - elmX;
        this.diffY = this.mouseBeginY - elmY;
    }
    Main_Su.prototype.mouseMove = function(e) {
        if (!this.isMouseDown) {
            return;
        }
        var newMouseX = e.clientX;
        var newMouseY = e.clientY;
        var newElmLeft = newMouseX - this.diffX;
        var newElmTop = newMouseY - this.diffY;
        this.moveElm(this.main_s, newElmTop, newElmLeft)
    }
    Main_Su.prototype.moveElm = function(elm, top, left) {
        elm.style.top = top + 'px';
        elm.style.left = left + 'px';
    }
    Main_Su.prototype.mouseUp = function(e) {
            console.log('mouseUp');
            this.isMouseDown = false;
        }
        /**
         * 设置下拉框选中
         */
    Main_Su.prototype.setSelectChecked = function(select, checkValue) {
        for (var i = 0; i < select.options.length; i++) {
            if (select.options[i].value == checkValue) {
                select.options[i].selected = true;
                break;
            }
        }
    };
    /**
     * 创建小球
     */
    Main_Su.prototype.createBall = function() {
            this.ball = document.createElement('div');
            this.ball.setAttribute('id', 'ball');
            this.ball.setAttribute('style', style.ballStyle);
            var cir = document.createElement('div');
            cir.setAttribute('id', 'cir');
            cir.setAttribute('style', 'margin-left: 2px;width: 18px;height: 18px;border-radius: 50%;background-color: #fff;transition: transform .2s linear');
            this.ball.appendChild(cir);
            return this.ball;
        }
        /**
         * 翻译结果的语言选择下拉框
         */
    Main_Su.prototype.createOriSelect = function() {
            this.ori_select = document.createElement('select');
            this.ori_select.setAttribute('id', "ori_select");
            this.ori_select.setAttribute('style', style.selectStyle);
            this.ori_select.innerHTML = select;
            //设置默认选择
            this.setSelectChecked(this.ori_select, 'zh-CN');
            return this.ori_select;
        }
        /**
         * 需要翻译文本的语言选择下拉框
         */
    Main_Su.prototype.createTraSelect = function() {
            this.tra_select = document.createElement('select');
            this.tra_select.setAttribute('id', "tra_select");
            this.tra_select.setAttribute('style', style.selectStyle);
            this.tra_select.innerHTML = select;
            //设置默认选择
            this.setSelectChecked(this.tra_select, 'en');
            return this.tra_select;
        }
        /**
         * 需要翻译文本的语言输入框
         */
    Main_Su.prototype.createOriText = function() {
            this.ori_text = document.createElement('textarea');
            this.ori_text.setAttribute('id', 'ori_text');
            this.ori_text.setAttribute('style', style.textareaStyle);
            this.ori_text.setAttribute('placeholder', '输入文本');
            //阻止事件冒泡
            this.ori_text.onmousedown = function(e) {
                e.stopPropagation();
            };
            return this.ori_text;
        }
        /**
         * 翻译结果的语言输入框
         */
    Main_Su.prototype.createTraText = function() {
            this.tra_text = document.createElement('textarea');
            this.tra_text.setAttribute('id', 'tra_text');
            this.tra_text.setAttribute('style', style.textareaStyle);
            this.tra_text.setAttribute('placeholder', '输入文本');
            //阻止事件冒泡
            this.tra_text.onmousedown = function(e) {
                e.stopPropagation();
            };
            return this.tra_text;
        }
        /**
         * 控制翻译面板的隐藏
         */
    Main_Su.prototype.hideTra = function() {
            tra.style.height = 0;
            tra.style.transform = 'translate(100%,0)';
            ball.style.visibility = 'visible'
        }
        /**
         * 创建列
         */
    Main_Su.prototype.createOriTd = function() {
            var oriTd = document.createElement('td');
            oriTd.setAttribute('id', 'oriTd');
            oriTd.setAttribute('style', 'border-right: 1px solid #ececec;background-color: #fff;width: 50%;');
            var div = document.createElement('div');
            var div_s = document.createElement('div');
            var div_t = document.createElement('div');
            div_s.setAttribute('style', 'margin: 10px 8px 0 16px;height: 40px;');
            div_t.setAttribute('style', style.dTextStyle);
            //创建添加下拉框跟输入框
            this.createOriSelect();
            div_s.appendChild(this.ori_select);
            this.createOriText();
            div_t.appendChild(this.ori_text);
            div.appendChild(div_s);
            div.appendChild(div_t);
            oriTd.appendChild(div);
            return oriTd;
        }
        /**
         * 创建列
         */
    Main_Su.prototype.createTraTd = function() {
            var traTd = document.createElement('td');
            traTd.setAttribute('id', 'traTd');
            traTd.setAttribute('style', 'width: 50%;background: #f9f9f9;');
            var div = document.createElement('div');
            var div_s = document.createElement('div');
            var div_t = document.createElement('div');
            div_s.setAttribute('style', 'margin: 10px 8px 0 16px;height: 40px;');
            div_t.setAttribute('style', style.dTextStyle);
            this.createTraSelect();
            div_s.appendChild(this.tra_select);
            this.createTraText();
            div_t.appendChild(this.tra_text);
            div.appendChild(div_s);
            div.appendChild(div_t);
            traTd.appendChild(div);
            return traTd;
        }
        /**
         * 创建翻译框
         */
    Main_Su.prototype.createTra = function() {
            this.tra = document.createElement('div');
            this.tra.setAttribute('id', 'tra');
            this.tra.setAttribute('style', style.traStyle)
            var table = document.createElement('table');
            table.setAttribute('style', "width: 100%");
            var tr = document.createElement('tr');
            tr.appendChild(this.createOriTd());
            tr.appendChild(this.createTraTd());
            table.appendChild(tr);
            this.tra.appendChild(table);
            return this.tra;
        }
        /**
         * 创建翻译主面板
         */
    Main_Su.prototype.createMain = function() {
            this.main_s = document.createElement('div');
            this.createBall();
            this.createTra();

            this.main_s.setAttribute('id', 'main_s');
            this.main_s.setAttribute('style', 'position: fixed;top: 100px;right: 0;width: 30%;');
            this.main_s.appendChild(this.ball);
            this.main_s.appendChild(this.tra);
            return this.main_s;
        }
        /**
         * ajax 跨域访问公共方法
         * @param {*} url
         * @param {*} method
         * @param {*} data
         */
    Main_Su.prototype.ajaxFun = function(url, header, method, data) {
            if (!!!method)
                method = 'GET';
            GM_xmlhttpRequest({
                method: method,
                url: url,
                data: data,
                header: header,
                onload: function(res) {
                    if (JSON.parse(res.response)[0][0][0]) {
                        tra_text.value = JSON.parse(res.response)[0][0][0];
                        tra_text.style.color = 'black'
                    }
                },
                onloadstart: function(res) {
                    tra_text.value = "正在翻译..."
                    tra_text.style.color = 'grey'
                },
                onerror: function(res) {
                    console.log("连接失败:" + res);
                }
            });
        }
        /**
         * 判断语种:只能判断中英韩日语
         * @param {} text 
         */
    Main_Su.prototype.judgeLanguage = function(text) {
            if (new RegExp('[\\u4e00-\\u9fa5]').test(text)) {
                return 'zh-CN'
            } else if (new RegExp('[\\x3130-\\x318F]').test(text)) {
                return 'ko'
            } else if (new RegExp('[\\u0800-\\u4e00]').test(text)) {
                return 'ja'
            } else if (new RegExp('[A-Za-z]').test(text)) {
                return 'en'
            } else {
                return 'default'
            }
        }
        /**
         * 翻译
         *  @param {是否关闭自动检测语种} isAuto 
         */
    Main_Su.prototype.translate = function(closeAuto) {
        var from = this.ori_select.value;
        //判断翻译文本的语种
        if (!closeAuto) {
            from = this.judgeLanguage(this.ori_text.value) == 'default' ? from : this.judgeLanguage(this.ori_text.value);
            this.setSelectChecked(this.ori_select, from);
        }
        var to = this.tra_select.value;
        this.ajaxFun(Translations.googleFun(from, to, this.ori_text.value), {
            "User-Agent": "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36",
            "Content-Type": "application/x-www-form-urlencoded;charset=UTF-8",
            "Host": "translate.google.cn"
        });
    }

    /** 
     * 添加绑定事件
     */
    Main_Su.prototype.rigisterEvent = function() {
        this.ball.addEventListener('click', function(e) {
            if (!(e.clientX - this.mouseBeginX == 0) || !(e.clientY - this.mouseBeginY == 0)) return;
            this.hide = !this.hide;
            if (this.hide) {
                this.ball.style.backgroundColor = "darkgray";
                document.getElementById("cir").style.transform = "translate(0,0)";
                this.tra.style.visibility = "hidden"
            } else {
                this.ball.style.backgroundColor = "dodgerblue";
                document.getElementById("cir").style.transform = "translate(22px,0)";
                this.tra.style.visibility = "visible"
            }
        }.bind(this))
        this.ori_text.addEventListener('input', function() {
            this.translate(false);
        }.bind(this));
        this.ori_select.addEventListener('change',
            function() {
                this.translate(true);
            }.bind(this));
        this.tra_select.addEventListener('change', this.translate.bind(this));
        this.ball.addEventListener('mousedown', this.mouseDown.bind(this));
        this.tra.addEventListener('mousedown', this.mouseDown.bind(this));
        document.addEventListener('mousemove', this.mouseMove.bind(this));
        document.addEventListener('mouseup', this.mouseUp.bind(this));
    }
    const Translations = {
            /**
             * 金山词霸翻译
             * @param {需要翻译文本的语言} f
             * @param {翻译结果的语言} t
             * @param {翻译原文} w
             */
            cibaFun: function(f, t, w) {
                var url = cibaUrl + "&f=" + f + "&t=" + t + "&w=" + w;
                return url;
            },
            /**
             * 
             * @param {需要翻译文本的语言} sl 
             * @param {翻译结果的语言} tl 
             * @param {翻译原文} q 
             */
            googleFun: function(sl, tl, q) {
                function sq(a) {
                    return function() {
                        return a
                    }
                }

                function tq(q, b) {
                    for (var c = 0; c < b.length - 2; c += 3) {
                        var d = b.charAt(c + 2);
                        d = "a" <= d ? d.charCodeAt(0) - 87 : Number(d);
                        d = "+" == b.charAt(c + 1) ? q >>> d : q << d;
                        q = "+" == b.charAt(c) ? q + d & 4294967295 : q ^ d
                    }
                    return q
                }
                /**
                 * 计算tk值
                 * @param {你要翻译的内容} q 
                 * @param {tkk的值} uq 
                 */
                function vq(q, uq = '422388.3876711001') {
                    if (null !== uq)
                        var b = uq;
                    else {
                        b = sq('T');
                        var c = sq('K');
                        b = [b(), c()];
                        b = (uq = window[b.join(c())] || "") || ""
                    }
                    var d = sq('t');
                    c = sq('k');
                    d = [d(), c()];
                    c = "&" + d.join("") + "=";
                    d = b.split(".");
                    b = Number(d[0]) || 0;
                    for (var e = [], f = 0, g = 0; g < q.length; g++) {
                        var l = q.charCodeAt(g);
                        128 > l ? e[f++] = l : (2048 > l ? e[f++] = l >> 6 | 192 : (55296 == (l & 64512) && g + 1 < q.length && 56320 == (q.charCodeAt(g + 1) & 64512) ? (l = 65536 + ((l & 1023) << 10) + (q.charCodeAt(++g) & 1023),
                                    e[f++] = l >> 18 | 240,
                                    e[f++] = l >> 12 & 63 | 128) : e[f++] = l >> 12 | 224,
                                e[f++] = l >> 6 & 63 | 128),
                            e[f++] = l & 63 | 128)
                    }
                    q = b;
                    for (f = 0; f < e.length; f++) {
                        q += e[f],
                            q = tq(q, "+-a^+6");
                    }
                    q = tq(q, "+-3^+b+-f");
                    q ^= Number(d[1]) || 0;
                    0 > q && (q = (q & 2147483647) + 2147483648);
                    q %= 1000000;
                    return c + (q.toString() + "." + (q ^ b))
                }
                window.TTK = '422388.3876711001';
                var tk = vq(q);
                var url = "https://translate.google.cn/translate_a/single?client=webapp&sl=" + sl + "&tl=" + tl + "&hl=" + sl + "&dt=at&dt=bd&dt=ex&dt=ld&dt=md&dt=qca&dt=rw&dt=rm&dt=ss&dt=t&dt=gt&otf=1&ssel=0&tsel=0&kc=3&tk=" + tk + "&q=" + encodeURIComponent(q);
                return url;
            }
        }
        //判断当前窗口是否为最顶层窗口,是则创建
    if (window.top == window.self) {
        var main = new Main_Su();
        main.createMain();
        main.rigisterEvent();
        document.body.appendChild(main.main_s);
    }
})()