离线场景下任意文档的在线预览及原样格式翻译,不依赖其他厂商接口非侵入式一行js代码实现网站的翻译及国际化,可配置使用多种翻译语言

离线场景下任意文档的在线预览及原样格式翻译,不依赖其他厂商接口非侵入式一行js代码实现网站的翻译及国际化,可配置使用多种翻译语言。

离线场景下任意文档的在线预览及原样格式翻译,不依赖其他厂商接口非侵入式一行js代码实现网站的翻译及国际化,可配置使用多种翻译语言_第1张图片

要实现翻译需要解决以下3个主要问题:
1)from:内容本身的语言类型是什么?
2)to:需要翻译为目标语言是什么?
3)text:需要翻译的文本内容是什么?

转化为:

1)首先,如何识别文档内容的语言?一篇文章中有多种语言混合的如何识别?
2)其次,用户使用的是什么语言?如何获取目标语言?
3)在文档或者网页中,所有内容都是带有格式的,如何翻译之后进行还原保证样式不丢失?

离线场景下任意文档的在线预览及原样格式翻译,不依赖其他厂商接口非侵入式一行js代码实现网站的翻译及国际化,可配置使用多种翻译语言_第2张图片


在网上查了很多资料,也下载了很多 收费 的资料,结果不尽人意。

获取用户的语言:

1)通过浏览器的默认语言判断用户使用的语言:


  <script>
    // 获取浏览器默认语言
    const getBrowserLang = function () {
      let browserLang = navigator.language ? navigator.language : navigator.browserLanguage;
      let defaultBrowserLang = "";
      if (
        browserLang.toLowerCase() === "us" ||
        browserLang.toLowerCase() === "en" ||
        browserLang.toLowerCase() === "en_us"
      ) {
        defaultBrowserLang = "en_US";
      } else {
        defaultBrowserLang = "zh_CN";
      }
      return defaultBrowserLang;
    };

    console.log(getBrowserLang());
  </script>

或者:


  <script>
    var type = navigator.appName; //BOM对象获取浏览器名称
    if (type == "Netscape") {
      var lang = navigator.language.toLowerCase(); //获取浏览器配置语言,支持非IE浏览器
    } else {
      var lang = navigator.browserLanguage.toLowerCase(); //获取浏览器配置语言,支持IE5+
    }
    console.log(lang);
    var lang = lang.substr(0, 5); //获取浏览器配置语言前4位
    console.log(lang);
  </script>

如何通过js在html中动态导入其他的js库:


  <script>
   

    var head = document.getElementsByTagName("head")[0];
    var script = document.createElement("script");
    script.type = "text/javascript";
    script.src = "http://localhost:8060/static/translate.min.js";
    script.onload = script.onreadystatechange = function () {
      translate.storage.set("to", "");
      //设置使用v2.x 版本
      translate.setUseVersion2();

      //SELECT 修改 onchange 事件
      translate.selectLanguageTag.selectOnChange = function (event) {
        //判断是否是第一次翻译,如果是,那就不用刷新页面了。 true则是需要刷新,不是第一次翻译
        var isReload = translate.to != null && translate.to.length > 0;
        if (isReload) {
          //如果要刷新页面的话,弹出友好提示
          alert(
            "您好,快速体验暂时只能切换其中一种语言进行体验,只是提供效果展示,您可参考接入文档来接入您的项目中进行完整体验及使用。",
          );
        } else {
          var language = event.target.value;
          console.log(language);
          // translate.changeLanguage(language);
        }
      };
    };
  </script>

如何获取文档或者网页的需要翻译的内容?找了一个网页翻译助手实现的js插件代码进行参考,完整代码如下:

// ==UserScript==
// @name         网页翻译助手
// @version      1.3.3
// @namespace    https://github.com/zyufstudio/TM/tree/master/webTranslate
// @description  支持划词翻译,输入文本翻译,谷歌整页翻译。可以自行选择谷歌翻译,有道字典翻译和百度翻译。
// @icon         data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAENklEQVRoQ+2ZTVITQRTH/28qyNIJFxCqnGwNJzCcwHACcelkIZxAPAG4IFkaTyCegHgC4jZjVfACzLBEUvOs7jCx57unZ6JSRZZJ9+v3e+/1++gQHviHHrj+eAT41x4s9YA99F5qK3nb+h4c7QTa6xtYmAuwNfLeg3Fc5QxmBGy1doO3O1dV9tVZmwmwdeYdg/DeTDB/vnY7B2Z7q+/KBhh6XF3UcgeDr3y3s2O6v+q+FIB9NutaRJdVBanrQ+bdYNCZ1pGhuzcNMPJ6FuNCV0DWuhA4ClzntI4M3b1rARBhRERjXSWK1oUhfy3y5loAmlBclcHAue86+1lyHwTAfXb4cD1wUmm9EgAzfhLoPLT4fGUNRpeAPgH6Bc/ARcw89Qed3eRWLQAGbhg4LrqY9sjrUYgxEZ7FDmH6GAPWUZ7JJvBh0ijXrpPStxRAKs/c00mLAiKewcyLmn0yt63Nha/yGgGEoP3Afb4KGSEYT+5eWxbZQniUJUT9INAFEeT3RXGr4wSxpj30JqoXKgMw8M13nV50oH02OyDQSUxJWX55zKB+8vu8uP1rAKr17dF8m8LFZUr5Em1Cau2ozZ09/NEn8Dum1puypq+2B1SXbQ1nY4Be61rvTxjxOPy1cRS12e2z2SURdcHITIuq/FoA4vL6rrOK56QwBn/n241eXv/fHs6mBHpRFViEHVsb+8I79QAYgT9w2pECSWFlFlxZuiqBSAz3YVcLQJxbFEJyeGEcwMJyAmO+UVPtltKSJzOZyiTuhAX+woyv/sDpNxZCMkUqKVReYl5MCXiaZdSQ+U0w6MgGLt2S59eD9tC7IKCn7s/zukkanfiusxdLo0SnaYi4gnkTXXLYiQqfLJa3re3kfaodQkkvSOuKQra5OLB4WbBCC5PgrTNZQZ7MbXqymKdqAnAjwZXsU5aRGgGQsQ7e02olpPJ3FzJNJj4ixonwSkIz71qgvpi7RYPoD5ztrLBsBCASHDIfBoPOx7yksmwl8ClLeXnHgW/ENAXxO2GUyENF42ejAEsl+Eq204DSTnOXiHqipS7LmOFtq02bi/OovykbPRsHKFNQ4/dTkXoj68ui9WtjL78Y1mzmNBQyWiKqOETPT3hWBPE/egBRwRLZjDbvJqLdyIP4LwFCwl6UdssgzAAaeNgqjKtEFxqDAGKF0whAHK72MUZBXrApOSRFxXGZnfhKfVc1B6j1uFuGrD8nGwNIL6wBIq/nyUOuBbDqbUbeaiYus230uxWil/U8X9RWZ7cSszmBVm2GVjeqq2TZuqwRtAqAeECwiD6p5/xVgGhQSTd1PAVRyd9QvK1a/r6Xio24kdzS/8jKLF30e3voBXkDUFW5WRObkLFWAOOXjASdvPzU6mY9w6wVYFmk/nSfla0O3IAxYat1mPeGtFaAqgqbrH8EMLFak3sePdCkNU1k/QadtchPhjx3/AAAAABJRU5ErkJggg==
// @author       Johnny Li
// @license      MIT
// @match        *://*/*
// @grant        GM_info
// @grant        GM_xmlhttpRequest
// @grant        GM_addStyle
// @grant        GM_registerMenuCommand
// @grant        GM_setValue
// @grant        GM_getValue
// @connect      cdn.jsdelivr.net
// @connect      cdn.bootcss.com
// @connect      translate.google.com.hk
// @connect      fanyi.youdao.com
// @connect      dict.youdao.com
// @connect      fanyi.baidu.com
// @connect      shared.ydstatic.com
// @require      https://cdn.jsdelivr.net/npm/[email protected]/dist/jquery.min.js
// @require      https://cdn.jsdelivr.net/npm/[email protected]/index.min.js
// @require      https://cdn.jsdelivr.net/gh/zyufstudio/jQuery@3a09ff54b33fc2ae489b5083174698b3fa83f4a7/jPopBox/dist/jPopBox.min.js
// ==/UserScript==


//文件使用Rollup+Gulp编译而成,如需查看源码请转到GitHub项目。

(function () {
    'use strict';

    /**
     * 字符串模板格式化
     * @param {string} formatStr - 字符串模板
     * @returns {string} 格式化后的字符串
     * @example
     * StringFormat("ab{0}c{1}ed",1,"q")  output "ab1cqed"
     */
    function StringFormat(formatStr) {
        var args = arguments;
        return formatStr.replace(/\{(\d+)\}/g, function (m, i) {
            i = parseInt(i);
            return args[i + 1];
        });
    }
    /**
     * 日期格式化
     * @param {Date} date - 日期
     * @param {string} formatStr - 格式化模板
     * @returns {string} 格式化日期后的字符串
     * @example
     * DateFormat(new Date(),"yyyy-MM-dd")  output "2020-03-23"
     * @example
     * DateFormat(new Date(),"yyyy/MM/dd hh:mm:ss")  output "2020/03/23 10:30:05"
     */
    function DateFormat(date, formatStr) {
        var o = {
            "M+": date.getMonth() + 1, //月份
            "d+": date.getDate(), //日
            "h+": date.getHours(), //小时
            "m+": date.getMinutes(), //分
            "s+": date.getSeconds(), //秒
            "q+": Math.floor((date.getMonth() + 3) / 3), //季度
            "S": date.getMilliseconds() //毫秒
        };
        if (/(y+)/.test(formatStr)) {
            formatStr = formatStr.replace(RegExp.$1, (date.getFullYear() + "").substr(4 - RegExp.$1.length));
        }
        for (var k in o) {
            if (new RegExp("(" + k + ")").test(formatStr)) {
                formatStr = formatStr.replace(
                    RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length)));
            }
        }
        return formatStr;
    }
    /**
     * 生成Guid
     * @param {boolean} hasLine - guid字符串是否包含短横线
     * @returns {string} guid
     * @example 
     * Guid(false)  output "b72f78a6cb88362c0784cb82afae450b"
     * @example
     * Guid(true) output "67b25d43-4cfa-3edb-40d7-89961ce7f388"
     */
    function Guid(hasLine){
        var guid="";
        function S4() {
           return (((1+Math.random())*0x10000)|0).toString(16).substring(1);
        }
        if(hasLine){
            guid=(S4()+S4()+"-"+S4()+"-"+S4()+"-"+S4()+"-"+S4()+S4()+S4());
        }
        else {
            guid=(S4()+S4()+S4()+S4()+S4()+S4()+S4()+S4());
        }
        return guid;
    }
    /**
     * 清除dom元素默认事件
     * @param {object} e - dom元素
     */
    function ClearBubble(e) {
        if (e.stopPropagation) {
            e.stopPropagation();
        } else {
            e.cancelBubble = true;
        }
        if (e.preventDefault) {
            e.preventDefault();
        } else {
            e.returnValue = false;
        }
    }
    function ObjectToQueryString(object){
        var querystring=Object.keys(object).map(function(key) { 
            return encodeURIComponent(key) + '=' + encodeURIComponent(object[key]) 
        }).join('&');
        return querystring;
    }

    /**
     * 配置参数
     */
    var options={
        //默认翻译引擎
        defaulttransengine:"yd"
    };
    /**
     * 获取配置参数
     */
    function GetSettingOptions(){
        var optionsJson=GM_getValue("webtranslate-options")||"";
        if(optionsJson!=""){
            var optionsData=JSON.parse(optionsJson);
            for (var key in options) {
                if (options.hasOwnProperty(key) && optionsData.hasOwnProperty(key)) {
                    options[key]= optionsData[key];   
                }
            }
        }
        return options;
    }
    /**
     * 设置配置参数
     */
    function SetSettingOptions(){
        var optionsJson=JSON.stringify(options);
        GM_setValue("webtranslate-options", optionsJson);
    }

    //谷歌翻译
    var googleTrans = {
        code: "ge",
        codeText: "谷歌",
        defaultOrigLang: "auto", //默认源语言
        defaultTargetLang: "zh-CN", //默认目标语言
        langList: {
            "auto": "自动检测",
            "zh-CN": "中文简体",
            "zh-TW": "中文繁体",
            "en": "英文",
            "ja": "日文",
            "ko": "韩文",
            "fr": "法文",
            "es": "西班牙文",
            "pt": "葡萄牙文",
            "it": "意大利文",
            "ru": "俄文",
            "vi": "越南文",
            "de": "德文",
            "ar": "阿拉伯文",
            "id": "印尼文"
        },
        Execute: function (h_onloadfn) {
            GM_xmlhttpRequest({
                method: "POST",
                url: "https://translate.google.com.hk/_/TranslateWebserverUi/data/batchexecute",
                headers: {
                    "Referer": `https://translate.google.com.hk/`,
                    "Cache-Control": "max-age=0",
                    "Content-Type": "application/x-www-form-urlencoded;charset=utf-8",
                },
                data: "f.req=" + encodeURIComponent(JSON.stringify([
                    [
                        ["MkEWBc", JSON.stringify([
                            [Trans.transText, Trans.transOrigLang, Trans.transTargetLang, true],
                            [null]
                        ]), null, "generic"]
                    ]
                ])),
                onload: function (r) {
                    setTimeout(function () {
                        var resData=r.responseText;
                        var transData=JSON.parse(JSON.parse(resData.match(/\[{2}.*\]{2}/g)[0])[0][2]);
                        var transList=transData[1][0][0][5];
                        var transTexts=[];
                        for (let index = 0; index < transList.length; index++) {
                            var transItem = transList[index];
                            transTexts.push(transItem[0]);
                        }
                        Trans.transResult.trans = transTexts;
                        Trans.transResult.orig = transData[1][4][0].split("\n");
                        Trans.transResult.origLang = transData[2];
                        h_onloadfn();
                    }, 300);
                },
                onerror: function (e) {
                    console.error(e);
                }
            });
        },
    };

    //获取sign
    function getSign() {
        GM_xmlhttpRequest({
            method: "GET",
            url: "http://fanyi.youdao.com/",
            timeout: 5000,
            onload: function (ydRes) {
                var fanyijsUrlMatch = /<\/script>/g.exec(ydRes.responseText);
                if (!fanyijsUrlMatch) {
                    console.log("获取fanyi.min.js失败!!!");
                } else {
                    var fanyijsUrl = fanyijsUrlMatch[1];
                    if (typeof fanyijsUrl !== 'undefined') {
                        GM_xmlhttpRequest({
                            method: "GET",
                            url: fanyijsUrl,
                            timeout: 5000,
                            onload: function (r) {
                                var signMatch = /sign:[a-z]{1}\.md5\("fanyideskweb"\+[a-z]{1}\+[a-z]{1}\+"(.*)"\)}};/g.exec(r.responseText);
                                if (!signMatch) {
                                    console.log("获取sign失败!!!");
                                } else {
                                    var newSign = signMatch[1];
                                    if (typeof newSign !== 'undefined') {
                                        youdaoTrans.sign = newSign;
                                    }
                                }
                            },
                            onerror: function (e) {
                                console.error(e);
                            }
                        });
                    }
                }
            },
            onerror: function (e) {
                console.error(e);
            }
        });
    }

    /**
     * 获取有道翻译音标
     * @param {String} transText 
     * @param {Function} callback 
     */
    function getYDSymbol(transText, callback) {
        var url = StringFormat("http://dict.youdao.com/fsearch?client=fanyideskweb&keyfrom=fanyi.web&q={0}&doctype=xml&xmlVersion=3.2&dogVersion=1.0&appVer=3.1.17.4208", encodeURIComponent(transText));
        GM_xmlhttpRequest({
            method: "GET",
            url: url,
            timeout: 5000,
            onload: function (ydRes) {
                var xmlnode=ydRes.responseXML;
                var symbol = {
                    uk:"",
                    us: ""
                };
                var root = xmlnode.getElementsByTagName("yodaodict")[0];
                if ("" + root.getElementsByTagName("uk-phonetic-symbol")[0] != "undefined" && "" + root.getElementsByTagName("uk-phonetic-symbol")[0].childNodes[0] != "undefined") {
                    symbol.uk = root.getElementsByTagName("uk-phonetic-symbol")[0].childNodes[0].nodeValue;
                }
                if ("" + root.getElementsByTagName("us-phonetic-symbol")[0] != "undefined" && "" + root.getElementsByTagName("us-phonetic-symbol")[0].childNodes[0] != "undefined") {
                    symbol.us = root.getElementsByTagName("us-phonetic-symbol")[0].childNodes[0].nodeValue;
                }
                callback(symbol);
            },
            onerror: function (e) {
                console.error(e);
            }
        });
    }

    //有道翻译
    var youdaoTrans = {
        code: "yd",
        codeText: "有道",
        sign: "",
        defaultOrigLang: "AUTO", //默认源语言
        defaultTargetLang: "ZH-CHS", //默认目标语言
        langList: {
            "AUTO": "自动检测",
            "zh-CHS": "中文",
            "en": "英文",
            "ja": "日文",
            "ko": "韩文",
            "fr": "法文",
            "es": "西班牙文",
            "pt": "葡萄牙文",
            "it": "意大利文",
            "ru": "俄文",
            "vi": "越南文",
            "de": "德文",
            "ar": "阿拉伯文",
            "id": "印尼文"
        },
        Execute: function (h_onloadfn) {
            var h_url = "",
                h_headers = {},
                h_data = "";

            var youdaoTransApi = "http://fanyi.youdao.com/translate_o?client=fanyideskweb&keyfrom=fanyi.web&smartresult=dict&version=2.1&doctype=json";
            var userAgent=$.md5(navigator.userAgent);
            var currentTs="" + (new Date).getTime();
            var salt=currentTs + parseInt(10 * Math.random(), 10);
            
            var sign = this.sign != "" ? this.sign : "]BjuETDhU)zqSxf-=B#7m";
            var signStr = $.md5("fanyideskweb" + Trans.transText + salt + sign);
            h_url = youdaoTransApi;
            h_headers = {
                "Content-Type": "application/x-www-form-urlencoded",
                "Referer": "http://fanyi.youdao.com/"
            };
            h_data = StringFormat("from={0}&to={1}&salt={2}&sign={3}&i={4}<s={5}&bv={6}", Trans.transOrigLang, Trans.transTargetLang, salt, signStr, encodeURIComponent(Trans.transText),currentTs,userAgent);

            GM_xmlhttpRequest({
                method: "POST",
                url: h_url,
                headers: h_headers,
                data: h_data,
                onload: function (r) {
                    setTimeout(function () {
                        var data = JSON.parse(r.responseText);
                        var trans = [],
                            origs = [],
                            src = "";
                        if (data.errorCode == 0) {
                            for (var j = 0; j < data.translateResult.length; j++) {
                                var ydTransCont = data.translateResult[j];
                                var ydtgt = "";
                                var ydsrc = "";
                                for (var k = 0; k < ydTransCont.length; k++) {
                                    var ydcont = ydTransCont[k];
                                    ydtgt += ydcont.tgt;
                                    ydsrc += ydcont.src;
                                }
                                trans.push(ydtgt);
                                origs.push(ydsrc);
                            }
                            src = data.type;
                            Trans.transResult.trans = trans;
                            Trans.transResult.orig = origs;
                            Trans.transResult.origLang = src.split("2")[0];
                            
                            var smartResult = data.smartResult;
                            if (smartResult && smartResult.entries.length > 0) {
                                getYDSymbol(Trans.transText, function (symbol) {
                                    Trans.transResult.symbols.en = symbol.uk;
                                    Trans.transResult.symbols.am = symbol.us;
                                    h_onloadfn();
                                });
                            }else {
                                h_onloadfn();
                            }
                        }
                    }, 300);
                },
                onerror: function (e) {
                    console.error(e);
                }
            });
        },
        init: function () {
            getSign();
        }
    };

    function a(r) {
      if (Array.isArray(r)) {
        for (var o = 0, t = Array(r.length); o < r.length; o++)
          t[o] = r[o];
        return t
      }
      return Array.from(r)
    }

    function n(r, o) {
      for (var t = 0; t < o.length - 2; t += 3) {
        var a = o.charAt(t + 2);
        a = a >= "a" ? a.charCodeAt(0) - 87 : Number(a),
        a = "+" === o.charAt(t + 1) ? r >>> a : r << a,
        r = "+" === o.charAt(t) ? r + a & 4294967295 : r ^ a;
      }
      return r
    }

    function e(r,gtk) {
      var i = null;
      var o = r.match(/[\uD800-\uDBFF][\uDC00-\uDFFF]/g);
      if (null === o) {
        var t = r.length;
        t > 30 && (r = "" + r.substr(0, 10) + r.substr(Math.floor(t / 2) - 5, 10) + r.substr(-10, 10));
      } else {
        for (var e = r.split(/[\uD800-\uDBFF][\uDC00-\uDFFF]/), C = 0, h = e.length, f = []; h > C; C++)
          "" !== e[C] && f.push.apply(f, a(e[C].split(""))),
          C !== h - 1 && f.push(o[C]);
        var g = f.length;
        g > 30 && (r = f.slice(0, 10).join("") + f.slice(Math.floor(g / 2) - 5, Math.floor(g / 2) + 5).join("") + f.slice(-10).join(""));
      }
      var u = void 0
        ;
      u = null !== i ? i : (i = gtk || "") || "";
      for (var d = u.split("."), m = Number(d[0]) || 0, s = Number(d[1]) || 0, S = [], c = 0, v = 0; v < r.length; v++) {
        var A = r.charCodeAt(v);
        128 > A ? S[c++] = A : (2048 > A ? S[c++] = A >> 6 | 192 : (55296 === (64512 & A) && v + 1 < r.length && 56320 === (64512 & r.charCodeAt(v + 1)) ? (A = 65536 + ((1023 & A) << 10) + (1023 & r.charCodeAt(++v)),
        S[c++] = A >> 18 | 240,
        S[c++] = A >> 12 & 63 | 128) : S[c++] = A >> 12 | 224,
        S[c++] = A >> 6 & 63 | 128),
        S[c++] = 63 & A | 128);
      }
      for (var p = m, F = "" + String.fromCharCode(43) + String.fromCharCode(45) + String.fromCharCode(97) + ("" + String.fromCharCode(94) + String.fromCharCode(43) + String.fromCharCode(54)), D = "" + String.fromCharCode(43) + String.fromCharCode(45) + String.fromCharCode(51) + ("" + String.fromCharCode(94) + String.fromCharCode(43) + String.fromCharCode(98)) + ("" + String.fromCharCode(43) + String.fromCharCode(45) + String.fromCharCode(102)), b = 0; b < S.length; b++)
        p += S[b],
        p = n(p, F);
      return p = n(p, D),
      p ^= s,
      0 > p && (p = (2147483647 & p) + 2147483648),
      p %= 1e6,
      p.toString() + "." + (p ^ m)
    }

    /**
     * @param  {string} word
     * @param  {string} gtk
     * @return {string}
     */
    var calcSign =function(word,gtk){
      return e(word,gtk);
    };

    //获取gtk和token
    function GetToken(){
        GM_xmlhttpRequest({
            method: "GET",
            url: "https://fanyi.baidu.com/",
            timeout:5000,
            onload: function (r) {
                var gtkMatch = /window\.gtk = '(.*?)'/.exec(r.responseText);
                var commonTokenMatch = /token: '(.*?)',/.exec(r.responseText);
                if (!gtkMatch) {
                  console.log("获取gtk失败!!!");
                }
                if (!commonTokenMatch) {
                  console.log("获取token失败!!!");
                }
                var newGtk = gtkMatch[1];
                var newCommonToken = commonTokenMatch[1];

                if (typeof newGtk !== 'undefined') {
                    baiduTrans.gtk=newGtk;
                }
                if (typeof newCommonToken !== 'undefined') {
                    baiduTrans.token=newCommonToken;
                }
            },
            onerror: function (e) {
                console.error(e);
            }
        });
    }

    //百度翻译
    var baiduTrans = {
        code:"bd",
        codeText:"百度",
        gtk:"",
        token:"",
        defaultOrigLang:"auto",         //默认源语言
        defaultTargetLang:"zh",         //默认目标语言
        langList: {"auto": "自动检测","zh": "中文","cht": "繁体中文","en": "英语","jp": "日语","kor": "韩语","fra": "法语","spa": "西班牙语","pt": "葡萄牙语","it": "意大利语","ru": "俄语","vie": "越南语","de": "德语","ara": "阿拉伯语"},
        Execute: function (h_onloadfn) {
            if(Trans.transOrigLang=="auto")
                this.AutoTrans(h_onloadfn);
            else
                this.ExecTrans(h_onloadfn);
            
        },
        AutoTrans:function(h_onloadfn){
            var self=this;
            var datas={
                query:Trans.transText
            };
            GM_xmlhttpRequest({
                method: "POST",
                headers:{
                    "referer": 'https://fanyi.baidu.com',
                    "Content-Type": 'application/x-www-form-urlencoded; charset=UTF-8',
                },
                url: "https://fanyi.baidu.com/langdetect",
                data: ObjectToQueryString(datas),
                onload: function (r) {
                    var data = JSON.parse(r.responseText);
                    if(data.error===0){
                        Trans.transOrigLang=data.lan;
                        self.ExecTrans(h_onloadfn);
                    }
                },
                onerror: function (e) {
                    console.error(e);
                }
            });
        },
        ExecTrans:function(h_onloadfn){
            var tempSign=calcSign(Trans.transText,this.gtk);
            var datas={
                from:Trans.transOrigLang,
                to:Trans.transTargetLang,
                query:Trans.transText,
                transtype:"translang",
                simple_means_flag:3,
                sign:tempSign,
                token:this.token
            };
            GM_xmlhttpRequest({
                method: "POST",
                headers:{
                    "referer": 'https://fanyi.baidu.com',
                    "Content-Type": 'application/x-www-form-urlencoded; charset=UTF-8',
                    //"User-Agent": window.navigator.userAgent,
                },
                url: "https://fanyi.baidu.com/v2transapi",
                data: ObjectToQueryString(datas),
                onload: function (r) {
                    setTimeout(function () {
                        var result= JSON.parse(r.responseText);
                        var trans_result=result.trans_result;
                        var dict_result=result.dict_result || null;
                        var transDatas = trans_result.data;
                        
                        var trans = [],origs = [],src = "";
                        for (var i = 0; i < transDatas.length; i++) {
                            var getransCont = transDatas[i];
                            trans.push(getransCont.dst);
                            origs.push(getransCont.src);
                        }
                        src = trans_result.from;
                        Trans.transResult.trans = trans;
                        Trans.transResult.orig = origs;
                        Trans.transResult.origLang = src;
                        
                        if(dict_result){
                            var symbols=dict_result.simple_means.symbols;
                            Trans.transResult.symbols.en=symbols[0].ph_en || "";
                            Trans.transResult.symbols.am=symbols[0].ph_am || "";
                        }
                        h_onloadfn();
                    }, 300);
                },
                onerror: function (e) {
                    console.error(e);
                }
            });
        },
        init:function(){
            GetToken();
        }
    };

    var Trans={
        transEngineList:{},         //翻译引擎实例列表
        transEngine:"",             //当前翻译引擎。ge(谷歌)/yd(有道)
        transEngineObj:{},          //当前翻译引擎实例
        transTargetLang:"",         //目标语言。
        transOrigLang:"",           //源语言
        transType:"word",           //翻译类型。word(划词翻译)/text(输入文本翻译)/page(整页翻译)
        transText:"",               //被翻译内容
        transResult:{               //当前翻译内容
            //译文
            trans:[],
            //原文
            orig:[],
            //原文语言
            origLang:"",
            //音标
            symbols:{
                //英标
                en:"",
                //美标
                am:"",
            }
        },
        Execute:function(h_onloadfn){
            resetTransResult(this);
            this.transEngineObj.Execute(h_onloadfn);
        },
        GetLangList:function(){
            var langList={};
            langList=this.transEngineObj.langList;
            return langList;
        },
        Update:function(){
            resetTransResult(this);
            this.transEngineObj=this.transEngineList[this.transEngine];
            this.transTargetLang=this.transEngineObj.defaultTargetLang;
            this.transOrigLang=this.transEngineObj.defaultOrigLang;
        },
        Clear:function(){
            this.transEngine="";                //当前翻译引擎。ge(谷歌)/yd(有道)
            this.transTargetLang="";            //目标语言。
            this.transOrigLang="";             //源语言
            this.transText="";                   //被翻译内容
            this.transResult.trans=[];
            this.transResult.orig=[];
            this.transResult.origLang="";
        },
        //注册翻译引擎接口并执行翻译引擎的初始化接口
        RegisterEngine:function(){
            /**
             * 翻译引擎必须提供以下接口
                code:"",                    //代号
                codeText:"",                //代号描述
                defaultOrigLang:"",         //默认源语言
                defaultTargetLang:"",       //默认目标语言
                langList: {},               //支持翻译语言列表
                Execute: function (h_onloadfn) {},     //执行翻译
                init:function(){},          //可选,初始化接口,在脚本创建时立即执行
             */
            var transEngineListObj={};
            transEngineListObj[googleTrans.code]=googleTrans;
            transEngineListObj[youdaoTrans.code]=youdaoTrans;
            transEngineListObj[baiduTrans.code]=baiduTrans;
            this.transEngineList=transEngineListObj;
            for (var key in this.transEngineList) {
                if (this.transEngineList.hasOwnProperty(key) && this.transEngineList[key].hasOwnProperty("init")) {
                    this.transEngineList[key].init();
                }
            }
        }
    };

    function resetTransResult(that){
        that.transResult.trans=[];
        that.transResult.orig=[];
        that.transResult.origLang="";
        that.transResult.symbols.en="";
        that.transResult.symbols.am="";
    }

    //面板
    var Panel={
        popBoxEl:{},
        randomCode:"",
        Create:function(title,placement,isShowArrow,content,shownFn){
            var self=this;
            $(self.popBoxEl).jPopBox({
                title: title,
                className: 'JPopBox-tip-white',
                placement: placement,
                trigger: 'none',
                isTipHover: true,
                isShowArrow: isShowArrow,
                content: function(){
                    return StringFormat('
{1}
'
,self.randomCode,content); } }); $(self.popBoxEl).on("shown.jPopBox",function(){ var $panel=$("div.JPopBox-tip-white"); typeof shownFn === 'function' && shownFn($panel); }); $(self.popBoxEl).jPopBox('show'); }, Update:function(Fn){ var $panel=$("div.JPopBox-tip-white"); Fn($panel); }, Destroy:function(){ //$(this.popBoxEl).jPopBox("hideDelayed"); $(this.popBoxEl).jPopBox("destroy"); }, CreateStyle:function(){ var s=""; s+=StringFormat("#panelBody{0}>div input,#panelBody{0}>div select{padding: 3px; margin: 0; background: #fff; font-size: 14px; border: 1px solid #a9a9a9; color:black;width: auto;min-height: auto; }",this.randomCode); s+=StringFormat("#panelBody{0}>div:first-child{padding-bottom: 5px;height:30px}",this.randomCode); s+=StringFormat("#panelBody{0}>div:last-child hr{border: 1px inset #eeeeee;background: none;height: 0px;margin: 0px;}",this.randomCode); return s; } }; //文本翻译面板 var TextTransPanel={ Create:function(popBoxEl,randomCode){ var self=this; var html=this.GetHtml(); var transEngineOptionsHtml=""; //翻译引擎 for (var k in Trans.transEngineList) { if (Trans.transEngineList.hasOwnProperty(k)) { var v = Trans.transEngineList[k].codeText; var selectOption=""; if(Trans.transEngine==k){ selectOption='selected="selected"'; } transEngineOptionsHtml+=StringFormat('',k,v,selectOption); } } var TextTransPanelHtml=StringFormat('
'+ '翻译引擎:    '+ '翻译语言: ⇨ '+ ' '+ '
'
+ '
'+ '

'
+ '
{6}
'
+ '
'
,randomCode,"",transEngineOptionsHtml,html.targetLangListHtml,html.origLangListHtml,"",""); Panel.popBoxEl=popBoxEl; Panel.randomCode=randomCode; Panel.Create("文本翻译","auto bottom",false,TextTransPanelHtml,function($panel){ $panel.css({ position: "fixed", top:"20px" }); //翻译引擎 $panel.find(StringFormat("#panelBody{0} div:eq(0) select:eq(0)",randomCode)).change(function(e){ Trans.transEngine=$(this).find("option:selected").val(); Trans.Update(); Panel.Update(function($panel){ var html=self.GetHtml(); //翻译内容 $panel.find(StringFormat("#panelBody{0} div:eq(1) div:eq(1)",randomCode)).html(""); $panel.find(StringFormat("#panelBody{0} div:eq(0) select:eq(1)",randomCode)).html(html.origLangListHtml); $panel.find(StringFormat("#panelBody{0} div:eq(0) select:eq(2)",randomCode)).html(html.targetLangListHtml); }); }); //源语言 $panel.find(StringFormat("#panelBody{0} div:eq(0) select:eq(1)",randomCode)).change(function(e){ Trans.transOrigLang=$(this).find("option:selected").val(); Panel.Update(function($panel){ var html=self.GetHtml(); $panel.find(StringFormat("#panelBody{0} div:eq(0) select:eq(2)",randomCode)).html(html.targetLangListHtml); }); }); //目标语言 $panel.find(StringFormat("#panelBody{0} div:eq(0) select:eq(2)",randomCode)).change(function(e){ Trans.transTargetLang=$(this).find("option:selected").val(); }); //翻译 $panel.find(StringFormat("#panelBody{0} div:eq(0) button:eq(0)",randomCode)).click(function(e){ var refTransText=$.trim($panel.find(StringFormat("#panelBody{0} div:eq(1) div:eq(0) textarea:eq(0)",randomCode)).val()); if(refTransText==""){ alert("请输入翻译文字!"); return; } Trans.transText=refTransText; Trans.Execute(function(){ Panel.Update(function($panel){ var html=self.GetHtml(); //源语言 $panel.find(StringFormat("#panelBody{0} div:eq(0) select:eq(1)",randomCode)).html(html.origLangListHtml); //翻译内容 $panel.find(StringFormat("#panelBody{0} div:eq(1) div:eq(1)",randomCode)).html(html.transHtml); }); }); }); }); }, GetHtml:function(){ var origLangListHtml=[]; var targetLangListHtml=[]; var returnHtml={}; var transHtml=[]; var langList=Trans.GetLangList(); var origLang=Trans.transResult.origLang; if(Trans.transResult.trans.length>0 && Trans.transResult.orig.length>0) { transHtml.push(''); for (var i = 0; i < Trans.transResult.trans.length; i++) { var transtxt = Trans.transResult.trans[i]; transHtml.push(transtxt); } transHtml.push(""); Trans.transOrigLang=origLang; } else { var txt="该翻译引擎不支持 "+langList[Trans.transOrigLang]+" 翻译成 "+langList[Trans.transTargetLang]; transHtml.push(StringFormat("{0}",txt)); } //源语言 for (var origKey in langList) { if (langList.hasOwnProperty(origKey)) { var origVal = langList[origKey]; var origSelectOption=""; if(Trans.transOrigLang.toUpperCase()==origKey.toUpperCase()){ origSelectOption='selected="selected"'; } origLangListHtml.push(StringFormat('',origKey,origVal,origSelectOption)); } } //目标语言 for (var targetKey in langList) { if (langList.hasOwnProperty(targetKey) && targetKey!=Trans.transOrigLang && targetKey.toUpperCase()!="AUTO") { var targetVal = langList[targetKey]; var targetSelectOption=""; targetLangListHtml.push(StringFormat('',targetKey,targetVal,targetSelectOption)); } } returnHtml.origLangListHtml=origLangListHtml.join(""); returnHtml.targetLangListHtml=targetLangListHtml.join(""); returnHtml.transHtml=transHtml.join(""); return returnHtml; } }; //划词翻译面板 var WordTransPanel = { Create: function (popBoxEl, randomCode) { var self = this; var html = this.GetTransContHtml(); var transEngineOptionsHtml = ""; for (var k in Trans.transEngineList) { if (Trans.transEngineList.hasOwnProperty(k)) { var v = Trans.transEngineList[k].codeText; var selectOption = ""; if (Trans.transEngine == k) { selectOption = 'selected="selected"'; } transEngineOptionsHtml += StringFormat('', k, v, selectOption); } } var wordTransPanelHtml = StringFormat( '
翻译引擎: 翻译语言:
'
+ '
{1}
'
, randomCode, html.transHtml, transEngineOptionsHtml, html.langListHtml, html.origLangName); Panel.popBoxEl = popBoxEl; Panel.randomCode = randomCode; Panel.Create("", "auto bottom", false, wordTransPanelHtml, function ($panel) { + //目标语言 $panel.find(StringFormat("#panelBody{0} div:eq(0) select:eq(1)", randomCode)).change(function (e) { Trans.transTargetLang = $(this).find("option:selected").val(); Trans.Execute(function () { self.Update(randomCode); }); }); //翻译引擎 $panel.find(StringFormat("#panelBody{0} div:eq(0) select:eq(0)", randomCode)).change(function (e) { Trans.transEngine = $(this).find("option:selected").val(); Trans.Update(); Trans.Execute(function () { self.Update(randomCode); }); }); }); }, Update: function (randomCode) { var self = this; Panel.Update(function ($panel) { var html = self.GetTransContHtml(); $panel.find(StringFormat("#panelBody{0} div:eq(0) input:eq(0)", randomCode)).val("").val(html.origLangName); $panel.find(StringFormat("#panelBody{0} div:eq(0) select:eq(1)", randomCode)).html("").html(html.langListHtml); $panel.find(StringFormat("#panelBody{0} div:eq(1)", randomCode)).html("").html(html.transHtml); }); }, GetTransContHtml: function () { var transObj = {}; var langListHtml = []; var langList = Trans.GetLangList(); var origLang = Trans.transResult.origLang; var transContHtml = ""; if (Trans.transResult.trans.length > 0 && Trans.transResult.orig.length > 0) { //译文 var transHtml = []; transHtml.push('
    '); for (var i = 0; i < Trans.transResult.trans.length; i++) { var transtxt = Trans.transResult.trans[i]; transHtml.push(StringFormat('
  • {0}
  • '
    , transtxt)); } transHtml.push("
"
); //原文 var origHtml = []; //原文内容 origHtml.push('
    '); for (var j = 0; j < Trans.transResult.orig.length; j++) { var origtxt = Trans.transResult.orig[j]; origHtml.push(StringFormat('
  • {0}
  • '
    , origtxt)); } origHtml.push("
"); //原文音标 if (Trans.transResult.symbols.en!="" || Trans.transResult.symbols.am!="") { origHtml.push('
'); if(Trans.transResult.symbols.en!="") origHtml.push(StringFormat('英 [{0}]',Trans.transResult.symbols.en)); if(Trans.transResult.symbols.am!="") origHtml.push(StringFormat('美 [{0}]',Trans.transResult.symbols.am)); origHtml.push('
'
); } origHtml.push("
"
); transContHtml = origHtml.join("") + "
"
+ transHtml.join(""); Trans.transOrigLang = origLang; } else { var txt = "该翻译引擎不支持 " + langList[Trans.transOrigLang] + " 翻译成 " + langList[Trans.transTargetLang]; transContHtml = StringFormat("
{0}
"
, txt); } for (var k in langList) { if (langList.hasOwnProperty(k) && k != Trans.transOrigLang && k.toUpperCase() != "AUTO") { var v = langList[k]; var selectOption = ""; if (Trans.transTargetLang == k) { selectOption = 'selected="selected"'; } langListHtml.push(StringFormat('', k, v, selectOption)); } } transObj.origLangName = langList[Trans.transOrigLang]; transObj.transHtml = transContHtml; transObj.langListHtml = langListHtml.join(""); return transObj; } }; //设置面板 var SettingPanel={ config:[{title:"",item:[{code:"",text:""}]}], Create:function(popBoxEl,randomCode){ var self=this; var settingHtml=[]; this.InitConfig(); settingHtml.push('
'); /* settingHtml.push('
'); settingHtml.push('
默认翻译引擎:
'); settingHtml.push(StringFormat('
',randomCode)); settingHtml.push(StringFormat('
',randomCode)); settingHtml.push('
'); */
for (var index = 0; index < this.config.length; index++) { var configItem = this.config[index]; settingHtml.push('
'); settingHtml.push(StringFormat('
{0}
'
,configItem.title)); for (var itemIndex = 0; itemIndex < configItem.item.length; itemIndex++) { var itemObj = configItem.item[itemIndex]; settingHtml.push(StringFormat('
'
,randomCode,itemObj.code,itemObj.text)); } settingHtml.push('
'
); } settingHtml.push('
'); settingHtml.push(StringFormat('',randomCode)); settingHtml.push(StringFormat('',randomCode)); settingHtml.push('
'
); settingHtml.push('
'
); var settingHtmlStr=settingHtml.join(""); Panel.popBoxEl=popBoxEl; Panel.randomCode=randomCode; Panel.Create("网页翻译助手设置","auto bottom",false,settingHtmlStr,function($panel){ $panel.css({ position: "fixed", top:"20px" }); self.Update(randomCode); //保存设置 $panel.find(StringFormat("#panelBody{0} #saveBtn{0}",randomCode)).click(function(e){ var defaultTransEngine=$panel.find(StringFormat("#panelBody{0} input[name='transEngine{0}']:checked",randomCode)).val(); options.defaulttransengine=defaultTransEngine; SetSettingOptions(); $panel.find(StringFormat("#panelBody{0} #saveStatus{0}",randomCode)).fadeIn(function(){ setTimeout(function(){ $panel.find(StringFormat("#panelBody{0} #saveStatus{0}",randomCode)).fadeOut(); },1500); }); }); }); }, Update:function(randomCode){ GetSettingOptions(); Panel.Update(function($panel){ $panel.find(StringFormat("#panelBody{0} input[name='transEngine{0}'][value='{1}']",randomCode,options.defaulttransengine)).prop("checked",true); }); }, InitConfig:function(){ this.config=[]; var configObj={title:"",item:[{code:"",text:""}]}; configObj.title="默认翻译引擎:"; configObj.item=[]; for (var k in Trans.transEngineList) { if (Trans.transEngineList.hasOwnProperty(k)) { var v = Trans.transEngineList[k].codeText; configObj.item.push({code:k,text:v}); } } this.config.push(configObj); } }; //主程序 var WebTranslate=function(){ var transIconBase64="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAENklEQVRoQ+2ZTVITQRTH/28qyNIJFxCqnGwNJzCcwHACcelkIZxAPAG4IFkaTyCegHgC4jZjVfACzLBEUvOs7jCx57unZ6JSRZZJ9+v3e+/1++gQHviHHrj+eAT41x4s9YA99F5qK3nb+h4c7QTa6xtYmAuwNfLeg3Fc5QxmBGy1doO3O1dV9tVZmwmwdeYdg/DeTDB/vnY7B2Z7q+/KBhh6XF3UcgeDr3y3s2O6v+q+FIB9NutaRJdVBanrQ+bdYNCZ1pGhuzcNMPJ6FuNCV0DWuhA4ClzntI4M3b1rARBhRERjXSWK1oUhfy3y5loAmlBclcHAue86+1lyHwTAfXb4cD1wUmm9EgAzfhLoPLT4fGUNRpeAPgH6Bc/ARcw89Qed3eRWLQAGbhg4LrqY9sjrUYgxEZ7FDmH6GAPWUZ7JJvBh0ijXrpPStxRAKs/c00mLAiKewcyLmn0yt63Nha/yGgGEoP3Afb4KGSEYT+5eWxbZQniUJUT9INAFEeT3RXGr4wSxpj30JqoXKgMw8M13nV50oH02OyDQSUxJWX55zKB+8vu8uP1rAKr17dF8m8LFZUr5Em1Cau2ozZ09/NEn8Dum1puypq+2B1SXbQ1nY4Be61rvTxjxOPy1cRS12e2z2SURdcHITIuq/FoA4vL6rrOK56QwBn/n241eXv/fHs6mBHpRFViEHVsb+8I79QAYgT9w2pECSWFlFlxZuiqBSAz3YVcLQJxbFEJyeGEcwMJyAmO+UVPtltKSJzOZyiTuhAX+woyv/sDpNxZCMkUqKVReYl5MCXiaZdSQ+U0w6MgGLt2S59eD9tC7IKCn7s/zukkanfiusxdLo0SnaYi4gnkTXXLYiQqfLJa3re3kfaodQkkvSOuKQra5OLB4WbBCC5PgrTNZQZ7MbXqymKdqAnAjwZXsU5aRGgGQsQ7e02olpPJ3FzJNJj4ixonwSkIz71qgvpi7RYPoD5ztrLBsBCASHDIfBoPOx7yksmwl8ClLeXnHgW/ENAXxO2GUyENF42ejAEsl+Eq204DSTnOXiHqipS7LmOFtq02bi/OovykbPRsHKFNQ4/dTkXoj68ui9WtjL78Y1mzmNBQyWiKqOETPT3hWBPE/egBRwRLZjDbvJqLdyIP4LwFCwl6UdssgzAAaeNgqjKtEFxqDAGKF0whAHK72MUZBXrApOSRFxXGZnfhKfVc1B6j1uFuGrD8nGwNIL6wBIq/nyUOuBbDqbUbeaiYus230uxWil/U8X9RWZ7cSszmBVm2GVjeqq2TZuqwRtAqAeECwiD6p5/xVgGhQSTd1PAVRyd9QvK1a/r6Xio24kdzS/8jKLF30e3voBXkDUFW5WRObkLFWAOOXjASdvPzU6mY9w6wVYFmk/nSfla0O3IAxYat1mPeGtFaAqgqbrH8EMLFak3sePdCkNU1k/QadtchPhjx3/AAAAABJRU5ErkJggg=="; var $doc=$(document); var $body=$("html body"); var $head=$("html head"); var randomCode="yyMM000000"; //属性随机码,年月加六位随机码。用于元素属性后缀,以防止属性名称重复。 var createHtml=function(){ var wordTransIconHtml=StringFormat('
'
,randomCode,transIconBase64); $body.append(StringFormat('
',randomCode)+wordTransIconHtml+'
'
); }; var createStyle=function(){ //尽可能避开csp认证 GM_xmlhttpRequest({ method:"get", url:"https://cdn.jsdelivr.net/gh/zyufstudio/jQuery@master/jPopBox/dist/jPopBox.min.css", onload:function(r){ GM_addStyle(r.responseText+".JPopBox-tip-white{width: 482px;max-width: 550px;min-width: 450px;}"); } }); var s=""; s+=StringFormat(".wordTrans{0}{background-color: rgb(245, 245, 245);box-sizing: content-box;cursor: pointer;z-index: 2147483647;border-width: 1px;border-style: solid;border-color: rgb(220, 220, 220);border-image: initial;border-radius: 5px;padding: 0.5px;position: absolute;display: none}",randomCode); s+=StringFormat(".wordTransIcon{0}{background-image: url({1});background-size: 25px;height: 25px;width: 25px;}",randomCode,transIconBase64); s+=Panel.CreateStyle(); GM_addStyle(s); }; var ShowWordTransIcon=function(){ var $wordTransIcon=$("div#wordTrans"+randomCode); var isSelect=false; var isPanel=false; var isWordTransIcon=false; $doc.on({ "selectionchange":function(e){ isSelect=true; }, "mousedown":function(e){ var $targetEl=$(e.target); isPanel=$targetEl.parents().is("div.JPopBox-tip-white"); isWordTransIcon=$targetEl.parents().is(StringFormat("div#wordTrans{0}",randomCode)); //点击翻译图标外域和翻译面板外域时,隐藏图标和翻译面板 if(!isWordTransIcon && !isPanel){ $wordTransIcon.hide(); Trans.Clear(); Panel.Destroy(); } else { //点击翻译图标,取消鼠标默认事件,防止选中的文本消失 if(isWordTransIcon){ ClearBubble(e); } } }, "mouseup":function(e){ var selectText = window.getSelection().toString().trim(); if(!isPanel&&isSelect&&selectText){ $wordTransIcon.show().css({ left: e.pageX + 'px', top : e.pageY + 12 + 'px' }); isSelect=false; } } }); $wordTransIcon.click(function(e){ Trans.Clear(); Panel.Destroy(); var selecter=window.getSelection(); var selectText = selecter.toString().trim(); GetSettingOptions(); Trans.transText=selectText; Trans.transType="word"; Trans.transEngine=options.defaulttransengine;//defaultTransEngine; Trans.Update(); Trans.Execute(function(){ WordTransPanel.Create($wordTransIcon,randomCode); $wordTransIcon.hide(); }); }); }; var guid=""; var RegMenu=function(){ GM_registerMenuCommand("文本翻译",function(){ var $body=$("html body"); $("div#wordTrans"+randomCode).hide(); Trans.Clear(); Panel.Destroy(); GetSettingOptions(); Trans.transEngine=options.defaulttransengine;//defaultTransEngine; Trans.Update(); TextTransPanel.Create($body,randomCode); }); GM_registerMenuCommand("Google整页翻译",function(){ if(guid=="") guid=Guid(); var cbscript=StringFormat('!function(){!function(){function e(){window.setTimeout(function(){window[t].showBanner(!0)},10)}function n(){return new google.translate.TranslateElement({autoDisplay:!1,floatPosition:0,multilanguagePage:!0,includedLanguages:"zh-CN,zh-TW,en",pageLanguage:"auto"})}var t=(document.documentElement.lang,"TE_{0}"),o="TECB_{0}";if(window[t])e();else if(!window.google||!google.translate||!google.translate.TranslateElement){window[o]||(window[o]=function(){window[t]=n(),e()});var a=document.createElement("script");a.src="https://translate.google.com.hk/translate_a/element.js?cb="+encodeURIComponent(o)+"&client=tee",document.getElementsByTagName("head")[0].appendChild(a)}}()}();',guid); $head.append(StringFormat('',cbscript)); }); GM_registerMenuCommand("设置",function(){ $("div#wordTrans"+randomCode).hide(); Trans.Clear(); Panel.Destroy(); SettingPanel.Create($body,randomCode); }); }; this.init=function(){ randomCode=DateFormat(new Date(),"yyMM").toString()+(Math.floor(Math.random() * (999999 - 100000 + 1) ) + 100000).toString(); Trans.RegisterEngine(); createStyle(); createHtml(); ShowWordTransIcon(); RegMenu(); }; }; var webTrans=new WebTranslate(); webTrans.init(); })();

如何判断文本内容的语言语种?

遍历所有字符,统计频率最高的语种为文档或者网页的语种,核心代码如下:



function isChinese(temp) 
{ 
	var re = /[^\u4e00-\u9fa5]/; 
	if(re.test(temp)) return false; 
	return true; 
}

 
function isJapanese(temp) 
{ 
	var re = /[^\u0800-\u4e00]/; 
	if(re.test(temp)) return false; 
	return true; 
}
 
function isKoera(chr) {
	
	if(((chr > 0x3130 && chr < 0x318F) || 
	    (chr >= 0xAC00 && chr <= 0xD7A3))) 
	{
		return true;
	}
	return false;
}

 
function isContainKoera(temp)
{
	var cnt = 0;
	for(var i=0;i < temp.length ; i++)
	{
		if(isKoera(temp.charAt(i)))
			cnt++;
	}
	if (cnt > 0) return true;
	return false;
}

 
function isContainChinese(temp)
{
	var cnt = 0;
	for(var i=0;i < temp.length ; i++)
	{
		if(isChinese(temp.charAt(i)))
			cnt++;
	}
	if (cnt > 5) return true;
	return false;
}

 
function isContainChinese2(temp)
{
	var cnt = 0;
	for(var i=0;i < temp.length ; i++)
	{
		if(isChinese(temp.charAt(i)))
			cnt++;
	}
	if (cnt > 0 && temp.length<=3) return true;
	return false;
}

 
function isContainJapanese(temp)
{
	var cnt = 0;
	for(var i=0;i < temp.length ; i++)
	{
		if(isJapanese(temp.charAt(i)))
			cnt++;
	}
	if (cnt > 2) return true;
	return false;
}


当然也可以 截取 一段文本内容发送给 语言 模型 进行识别,但是对于混合文档还是不准确,还不如放在前端做节省计算资源。

看下谷歌翻译之前的实现方式:

<script type="text/javascript" src="http://www.google.com/jsapi"></script >
    <script type="text/javascript">
      google.load("language", "1");
      function initialize()
      {
        var text = document.getElementById("text").innerHTML;
        google.language.detect(text, 
                               function(result)
                               {
          if(!result.error && result.language)
          {
            google.language.translate(text, result.language, "en", 
                                      function(result)
                                      {
              var translated = document.getElementById("translation");
              if(result.translation)
              {
                translated.innerHTML = result.translation;
              }
            });
          }
        });
      }
      google.setOnLoadCallback(initialize);
    </script> 

或者:

<script src="https://translate.google.cn/translate_a/element.js?cb=googleTranslateElementInit"></script>

还有网友改进后的一种方式:

<script src="./el_main.js"></script>
<script src="./el_main.css"></script>
<script>
function googleTranslateElementInit() {
 
	new google.translate.TranslateElement(
		{
                //这个参数不起作用,看文章底部更新,翻译面板的语言
                //pageLanguage: 'zh-CN',
            //这个是你需要翻译的语言,比如你只需要翻译成越南和英语,这里就只写en,vi
			includedLanguages: 'en,zh-CN,hr,cs,da,nl,fr,de,el,iw,hu,ga,it,ja,ko,pt,ro,ru,sr,es,th,vi',
            //选择语言的样式,这个是面板,还有下拉框的样式,具体的记不到了,找不到api~~
			layout: google.translate.TranslateElement.InlineLayout.SIMPLE,
            //自动显示翻译横幅,就是翻译后顶部出现的那个,有点丑,这个属性没有用的话,请看文章底部的其他方法
			autoDisplay: true, 
			//还有些其他参数,由于原插件不再维护,找不到详细api了,将就了,实在不行直接上dom操作
		}, 
		'google_translate_element'//触发按钮的id
	);
 
}
</script> 

还用另一个 js 库 去实现的:

<script src="./franc.js">
    // import {franc, francAll} from './franc.js'
    var ll=franc('Alle menneske er fødde til fridom') //=> 'nno'
    console.log(ll);


  </script>

依赖的 js 库太大了,不贴出来了,有需要可以留言。

查资料看到还有这种用法的:

<script>
javascript: void((function () {
        var script = document.createElement('script');
        script.src = '//translate.google.cn/translate_a/element.js?cb=googleTranslateElementInit';
        document.getElementsByTagName('head')[0].appendChild(script);

        var google_translate_element = document.createElement('div');
        google_translate_element.id = 'google_translate_element';
        google_translate_element.style = 'position:fixed; bottom:10px; right:10px; cursor:pointer;';
        document.documentElement.appendChild(google_translate_element);

        script = document.createElement('script');
        script.innerHTML = "function googleTranslateElementInit() {" +
            "new google.translate.TranslateElement({" +
            "layout: google.translate.TranslateElement.InlineLayout.SIMPLE," +
            "multilanguagePage: true," +
            "pageLanguage: 'auto'," +
            "includedLanguages: 'zh-CN,zh-TW,en'" +
            "}, 'google_translate_element');}";
        document.getElementsByTagName('head')[0].appendChild(script);
})());


</script>

获取网页内容的所有标签的文本内容,过滤非文本标签,实现如下:

<script>

function listen(callback) {

// 获取 HTML 文档中的所有元素,但不包括 下列 选择器的元素

var exclude = ['head', 'pre', 'script', 'textarea']//排除名单

var selectors = []

exclude.forEach((item, index) => {

    selectors.push(item)//排除该元素

    selectors.push(item + ' *')//排除该元素后代

})

get(document.querySelectorAll('*:not(' + selectors.join(',') + ')'))//*:not(pre,pre *)

// 创建 MutationObserver 对象

let observer = new MutationObserver(function (mutations) {

    mutations.forEach(function (mutation) {

        // 遍历新添加的节点

        for (let i = 0; i < mutation.addedNodes.length; i++) {

            let node = mutation.addedNodes[i];

            // 如果节点是元素节点,就调用 get 函数

            if (node.nodeType === 1) {

                callMyFunction(node)

                function callMyFunction(param1) {

                    setTimeout(function () {

                        get([...param1.querySelectorAll('*'), param1])

                    }, 300);

                }

            }

        }

    });

});

// 设置 MutationObserver 的参数,表示监听所有元素的变化

let config = {

    childList: true,

    subtree: true

};



// 启动 MutationObserver

observer.observe(document, config);



function get(elements) {

    // 遍历所有元素

    for (let i = 0; i < elements.length; i++) {

        let element = elements[i];

        // 遍历元素的 childNodes

        for (let j = 0; j < element.childNodes.length; j++) {

            let node = element.childNodes[j];

            // 如果当前节点是一个文本节点(nodeType 为 3)且不包含子节点(nodeName 为 '#text'),就将文本添加到数组中

            if (node.nodeType === 3 && node.nodeName.toLowerCase() === '#text') {

                // 过滤掉文本中的换行符



                let text = node.nodeValue

                var v = { a: false, b: false }

                text.slice(0, 1) == " " ? v.a = true : v.a = false

                text.slice(-1) == " " ? v.b = true : v.b = false

                text = text.replace(/[\n\t\r]/g, '').trim();

                // 如果文本不仅包含空白字符,就将它添加到数组中

                if (/\S/.test(text)) {

                    //不处理只有数字和符号的文本

                    if (!/^[0-9\+\-\*\/\=><&\!@#\$%\^\*\\(\)\[\]\{\}_,.;',。、;’、]{1,}$/.test(text)) {

                        //---------------处理

                        //翻译text

                        //text = "$" + text

                        //---------------处理结束--显示

                        v.a == true ? text = " " + text : text

                        v.b == true ? text = text + " " : text

                        if (!element.matches('script,textarea')) {//单元素阻断,白名单

                            node.nodeValue = text

                            callback.call({ text: text, node: node, element: element })

                        } else {

                            //console.log("位于排除标签列表", element);

                        }

                    } else {

                        //console.log("只有数字和符号的文本", text);

                    }

                }

            }

        }

    }

}

}

let time = null;

var data = []

listen(

function () {

    if (time !== null) {

        clearTimeout(time);

    }

    time = setTimeout(async () => {

        console.log(data);//抖动结束,开始翻译

        var sl = []

        data.forEach((item, index) => {//取text

            sl.push(item['text'])

        });

        // var tl = await translation_arr(sl) //返回一个数组[[翻译结果,源语言类型],...*]//使用的谷歌批量翻译API,这里就不提供了

        var tl = []

        sl.forEach((item, index) => {

            tl.push('[ 编辑:' + item + ',' + index + '] ')

        });



        tl.forEach((item, index) => {

            data[index]['node'].origText = data[index]['node'].nodeValue

            data[index]['node'].nodeValue = item//更改文本

        });

        //这里的this指向的是input

    }, 500)

    data.push(this)

}

)

/* 监听文本节点被点击

document.onselectstart = function (e) {

console.log(e.target,e.target.origText);

}*/



</script> 

在网页中自动插入一个 下拉框 用于展示支持的翻译语种,并根据网页内容识别的语种自动选中语种类型:


<script>

    // 获取浏览器默认语言
 const getBrowserLang = function() {
  let browserLang = navigator.language
    ? navigator.language
    : navigator.browserLanguage;
  let defaultBrowserLang = "";
  if (
    browserLang.toLowerCase() === "us" ||
    browserLang.toLowerCase() === "en" ||
    browserLang.toLowerCase() === "en_us"
  ) {
    defaultBrowserLang = "en_US";
  } else {
    defaultBrowserLang = "zh_CN";
  }
  return defaultBrowserLang;
};

console.log(getBrowserLang());
</script>


<script>
    var type = navigator.appName; //BOM对象获取浏览器名称
    if (type == "Netscape") {
        var lang = navigator.language.toLowerCase(); //获取浏览器配置语言,支持非IE浏览器
    } else {
        var lang = navigator.browserLanguage.toLowerCase(); //获取浏览器配置语言,支持IE5+
    };
    console.log(lang);
    var lang = lang.substr(0, 5); //获取浏览器配置语言前4位
   console.log(lang);


   /*
	极速测试体验,用于审查元素时直接执行的
	1. 随便打开一个网页
	2. 右键-审查元素
	3. 粘贴入一下代码:
		var head= document.getElementsByTagName('head')[0];  var script= document.createElement('script');  script.type= 'text/javascript';  script.src= 'http://localhost:8060/static/inspector.js';  head.appendChild(script); 
	4. Enter 回车键 , 执行
	5. 在当前网页的左上角,就出现了一个大大的切换语言了	
	
	使用的是 v2.x 版本进行的翻译
 */


var head= document.getElementsByTagName('head')[0]; 
var script= document.createElement('script'); 
script.type= 'text/javascript'; 
script.src= 'http://localhost:8060/static/translate.js'; 
script.onload = script.onreadystatechange = function() {
	translate.storage.set('to','');
	//设置使用v2.x 版本
	translate.setUseVersion2(); 

	//SELECT 修改 onchange 事件
	translate.selectLanguageTag.selectOnChange = function(event){
		//判断是否是第一次翻译,如果是,那就不用刷新页面了。 true则是需要刷新,不是第一次翻译
		var isReload = translate.to != null && translate.to.length > 0;
		if(isReload){
			//如果要刷新页面的话,弹出友好提示
			alert('您好,快速体验暂时只能切换其中一种语言进行体验,只是提供效果展示,您可参考接入文档来接入您的项目中进行完整体验及使用。');
		}else{
			var language = event.target.value;
			translate.changeLanguage(language);
            console.log("ttttt");
            console.log(language);
		}			
	}
	
	translate.listener.start();	//开启html页面变化的监控,对变化部分会进行自动翻译。注意,这里变化区域,是指使用 translate.setDocuments(...) 设置的区域。如果未设置,那么为监控整个网页的变化
	translate.execute();
	document.getElementById('translate').style.position = 'fixed';
	document.getElementById('translate').style.color = 'red';
	document.getElementById('translate').style.left = '10px';
	document.getElementById('translate').style.top = '10px';
	document.getElementById('translate').style.zIndex = '9999999999999';

	setInterval(function() {
		try{
			if(document.getElementById('translateSelectLanguage') == null){
				return;
			}
			document.getElementById('translateSelectLanguage').style.fontSize = '2rem';
			document.getElementById('translateSelectLanguage').style.borderWidth = '0.5rem';
			document.getElementById('translateSelectLanguage').style.borderColor = 'red';
		}catch(e){
			//select数据是通过接口返回的
		}
	},1000);

}
head.appendChild(script); 



   
</script>

不同页面或者不同 js 之间传递数据,粗暴简单一点可以通过如下方式:

window.localStorage.setItem('local_language',translate.language.local);



通过对前端js和后端翻译服务进行了长时间的研究和测试,最终实现的效果可以达到:

1)如果是自己的网站
在网页最末尾, 之前,加入以下代码,一般在页面的最底部就出现了选择语言的 select 切换标签。 其实就这么简单:

<script src="http://localhost:8060/static/translate.min.js"></script>
<script>
    // translate.language.setLocal('chinese_simplified');
    translate.request.api.host='http://localhost:8060/';
    translate.execute();
</script>

或者:

<script src="http://localhost:8060/static/i18n.js"></script>

2)如果是第三方的网站
随便打开一个网页
右键 - 检查(审查元素)〉控制台:
粘贴入以下代码:

var head= document.getElementsByTagName('head')[0]; var script= document.createElement('script'); script.type= 'text/javascript'; script.src= 'http://localhost:8060/static/inspector.js'; head.appendChild(script);

或者:

var head= document.getElementsByTagName('head')[0]; var script= document.createElement('script'); script.type= 'text/javascript'; script.src= 'http://localhost:8060/static/inspector.js'; head.appendChild(script);

Enter 回车键 , 执行
在当前网页的左上角,就出现了一个大大的切换语言,切换试试看。


效果参考:

点击查看

https://blog.csdn.net/u014374009/article/details/135401003

源码及后端翻译服务支持docker一键部署:

点击查看

https://blog.csdn.net/u014374009/article/details/135370222


有任何定制化的需求可以联系作者完成开发。

你可能感兴趣的:(前后端,javascript,前端,html,js,翻译,在线预览,国际化)