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

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

在这里插入图片描述

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

转化为:

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

在这里插入图片描述


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

获取用户的语言:

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/jquery@2.2.3/dist/jquery.min.js
// @require      https://cdn.jsdelivr.net/npm/jquery.md5@1.0.2/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\s+type="text\/javascript"\s+src="([http|https]*?:\/\/shared.ydstatic.com\/fanyi\/newweb\/v[\d.]+\/scripts\/newweb\/fanyi.min.js)"><\/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}&lts={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('<div id="panelBody{0}">{1}</div>',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('<option value="{0}" {2}>{1}</option>',k,v,selectOption);
                }
            }
            var TextTransPanelHtml=StringFormat('<div style="padding-bottom: 5px;">'+
                '翻译引擎:<select>{2}</select>&nbsp;&nbsp;&nbsp;&nbsp;'+
                '翻译语言:<select>{4}</select> &#x21E8; '+
                '<select>{3}</select> '+
                '<button style="width:46px; height:26px; cursor: pointer;overflow: visible;color: inherit;margin: 0;padding: 1px 7px;background-color: #dddddd;border: 2px outset #dddddd;text-align: center;display: inline-block;font-size: 14px; font-weight: 400; ">翻译</button></div>'+
                '<div style="word-wrap:break-word">'+
                    '<div style="padding-bottom: 5px;"><textarea placeholder="请输入你要翻译的文字" style="word-wrap: break-word;word-break: keep-all;overflow-y: auto;width:450px;height:85px;padding: 3px;line-height: 18px;font-size: 14px;font-family: arial,simsun;border: 1px solid #999;border-color: #999 #d8d8d8 #d8d8d8 #999;outline: 0;resize: none;">{5}</textarea></div><hr/>'+
                    '<div style="padding-top: 5px;">{6}</div>'+
                '</div>',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('<span>');
                for (var i = 0; i < Trans.transResult.trans.length; i++) {
                    var transtxt = Trans.transResult.trans[i];
                    transHtml.push(transtxt);
                }
                transHtml.push("</span>");
                Trans.transOrigLang=origLang;
            }
            else {
                var txt="该翻译引擎不支持 "+langList[Trans.transOrigLang]+" 翻译成 "+langList[Trans.transTargetLang];
                transHtml.push(StringFormat("<span>{0}</span>",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('<option value="{0}" {2}>{1}</option>',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('<option value="{0}" {2}>{1}</option>',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('<option value="{0}" {2}>{1}</option>', k, v, selectOption);
                }
            }
            var wordTransPanelHtml = StringFormat(
                '<div>翻译引擎:<select>{2}</select>    翻译语言:<input type="text" value="{4}" readonly style="width:80px"/> &#x21E8; <select>{3}</select></div>' +
                '<div style="word-wrap:break-word">{1}</div>', 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('<div style="padding-top: 5px;"><ul style="list-style: none;margin: 0;padding: 0;">');
                for (var i = 0; i < Trans.transResult.trans.length; i++) {
                    var transtxt = Trans.transResult.trans[i];
                    transHtml.push(StringFormat('<li style="list-style: none;"><span>{0}</span></li>', transtxt));
                }
                transHtml.push("</ul></div>");
                //原文
                var origHtml = [];
                //原文内容
                origHtml.push('<div style="padding-bottom: 5px;"><ul style="list-style: none;margin: 0;padding: 0;">');
                for (var j = 0; j < Trans.transResult.orig.length; j++) {
                    var origtxt = Trans.transResult.orig[j];
                    origHtml.push(StringFormat('<li style="list-style: none;"><span>{0}</span></li>', origtxt));
                }
                origHtml.push("</ul>");
                //原文音标
                if (Trans.transResult.symbols.en!="" || Trans.transResult.symbols.am!="") {
                    origHtml.push('<div>');
                    if(Trans.transResult.symbols.en!="")
                    origHtml.push(StringFormat('<span style="padding-right: 10px;">英 [{0}]</span>',Trans.transResult.symbols.en));
                    if(Trans.transResult.symbols.am!="")
                    origHtml.push(StringFormat('<span>美 [{0}]</span>',Trans.transResult.symbols.am));
                    origHtml.push('</div>');
                }
                origHtml.push("</div>");

                transContHtml = origHtml.join("") + "<hr/>" + transHtml.join("");
                Trans.transOrigLang = origLang;
            } else {
                var txt = "该翻译引擎不支持 " + langList[Trans.transOrigLang] + " 翻译成 " + langList[Trans.transTargetLang];
                transContHtml = StringFormat("<div><span>{0}</span></div>", 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('<option value="{0}" {2}>{1}</option>', 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('<div style="padding-left: 15px;display: inline-block;">');
                /*
                settingHtml.push('<div style="padding-bottom: 30px; max-width: 600px;">');
                    settingHtml.push('<div style="font-size: 14px; padding-bottom: 3px;">默认翻译引擎:</div>');
                    settingHtml.push(StringFormat('<div style="padding-bottom: 3px; margin-left: 10px;"><label style="font-size: 14px; cursor: pointer;"><input type="radio" name="transEngine{0}" style="cursor: pointer;" value="yd">有道</label></div>',randomCode));
                    settingHtml.push(StringFormat('<div style="padding-bottom: 0px; margin-left: 10px;"><label style="font-size: 14px; cursor: pointer;"><input type="radio" name="transEngine{0}" style="cursor: pointer;" value="ge">谷歌</label></div>',randomCode));
                settingHtml.push('</div>');
                */
                for (var index = 0; index < this.config.length; index++) {
                    var configItem = this.config[index];
                    settingHtml.push('<div style="padding-bottom: 30px; max-width: 600px;">');
                    settingHtml.push(StringFormat('<div style="font-size: 14px; padding-bottom: 3px;">{0}</div>',configItem.title));
                    for (var itemIndex = 0; itemIndex < configItem.item.length; itemIndex++) {
                        var itemObj = configItem.item[itemIndex];
                        settingHtml.push(StringFormat('<div style="padding-bottom: 0px; margin-left: 10px;"><label style="font-size: 14px; cursor: pointer;"><input type="radio" name="transEngine{0}" style="cursor: pointer;" value="{1}">{2}</label></div>',randomCode,itemObj.code,itemObj.text));
                    }   
                    settingHtml.push('</div>');
                }

                settingHtml.push('<div>');
                    settingHtml.push(StringFormat('<button id="saveBtn{0}">保存</button>',randomCode));
                    settingHtml.push(StringFormat('<span id="saveStatus{0}" style="display:none;margin-left:10px;background-color: #fff1a8;padding: 3px;">设置已保存。</span>',randomCode));
                settingHtml.push('</div>');
            settingHtml.push('</div>');

            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('<div id="wordTrans{0}" class="wordTrans{0}"><div class="wordTransIcon{0}"></div></div>',randomCode,transIconBase64);
            $body.append(StringFormat('<div id="webTrans{0}">',randomCode)+wordTransIconHtml+'</div>');
        };
        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('<script>{0}</script>',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


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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/382484.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Open CASCADE学习|扫掠

目录 1、BRepPrimAPI_MakePrism Draw Test Harness&#xff1a; C&#xff1a; 2、BRepPrimAPI_MakeRevol Draw Test Harness&#xff1a; C&#xff1a; 3、BRepOffsetAPI_MakePipeShell Draw Test Harness&#xff1a; C&#xff1a; Draw Test Harness&#xff1a;…

node.js+vue企业人事自动化办公oa系统c288a

采用B/S模式架构系统&#xff0c;开发简单&#xff0c;只需要连接网络即可登录本系统&#xff0c;不需要安装任何客户端。开发工具采用VSCode&#xff0c;前端采用VueElementUI&#xff0c;后端采用Node.js&#xff0c;数据库采用MySQL。 涉及的技术栈 1&#xff09; 前台页面…

小程序-云开发 获取用户的openid等信息

说明介绍&#xff1a; 小程序云开发功能来获取用户的openid。 一般在我们需要用到用户登录的时候&#xff0c;通常是需要获取微信小程序的openid的&#xff0c;由于微信的限制&#xff0c;一般我们只能通过后台去调微信的接口&#xff0c;来授权获取&#xff0c;增加了后端开发…

OnlyOffice-8.0版本深度测评

OnlyOffice 是一套全面的开源办公协作软件&#xff0c;不断演进的 OnlyOffice 8.0 版本为用户带来了一系列引人瞩目的新特性和功能改进。OnlyOffice 8.0 版本在功能丰富性、安全性和用户友好性上都有显著提升&#xff0c;为用户提供了更为强大、便捷和安全的文档处理和协作环境…

内网安全-内网穿透

目录 内网渗透 Nc使用详解 Nc监听和探测 Nc传文件 termite内网穿透工具 ssh代理内网穿透 ssh配置socket代理 MSF多级网络穿透 内网渗透 Nc使用详解 Nc监听和探测 Nc传文件 termite内网穿透工具 1、termite 之前叫ew &#xff08;可以进行正向连接&#xff0c;可以…

【深度学习】“智能皮肤:深度学习驱动的‘智慧之眼‘应用如何革新皮肤病诊疗未来“

在一个不久的未来世界&#xff0c;医疗科技取得了惊人的突破。一款名为“智慧之眼”的神秘应用横空出世&#xff0c;它如同科幻小说中的神器&#xff0c;能够通过摄像头扫描皮肤病变&#xff0c;并借助深度学习技术迅速得出专业级别的诊断结果。这个革新性的故事始于一场科研马…

【制作100个unity游戏之23】实现类似七日杀、森林一样的生存游戏10(附项目源码)

本节最终效果演示 文章目录 本节最终效果演示系列目录前言快捷栏绘制UI代码控制快捷列表信息 源码完结 系列目录 前言 欢迎来到【制作100个Unity游戏】系列&#xff01;本系列将引导您一步步学习如何使用Unity开发各种类型的游戏。在这第23篇中&#xff0c;我们将探索如何制作…

Java异常处理 throw和throws

目录 throwthrows实例制造异常 在Java中&#xff0c;throw和throws关键字都与异常处理有关&#xff0c;但它们的使用方式和目的有所不同。 throw throw关键字&#xff1a; * throw用于在代码中显式地抛出一个异常。你可以使用它来触发一个异常&#xff0c;并指定异常的类型。…

FPGA_简单工程_VGA显示驱动器

一 理论 使用640*48060显示模式&#xff0c;将数字信号转换位模拟信号&#xff0c;经由VGA进行显示。 使用3GM723&#xff0c;3路高清视频编码芯片。 3GM7123编码芯片&#xff1a; 该芯片的主要功能是将RGB888的颜色数据转换成模拟的电压信号&#xff0c;然后进入到VGA接口的…

STM32CubeMX,定时器之定时功能,入门学习,如何设置prescaler,以及timer计算PWM输入捕获方法(重要)

频率变小&#xff0c;周期变长 1&#xff0c;参考链接&#xff08;重要&#xff09; STM32CubeMX——定时器之定时功能&#xff08;学习使用timer定时器的设置&#xff09; STM32测量PWM信息&#xff08;学习使用设置pwm输入捕获&#xff09; 通用定时器中两个重要参数的设置心…

吹响AI PC号角!微软在Windows中不断增加“Copilot含量”

2024&#xff0c;会是AI PC元年吗&#xff1f;至少微软正在往这个方向努力。 本周&#xff0c;微软开始在Windows中测试Copilot的“新体验”&#xff0c;其中包括任务栏中的Copilot图标&#xff0c;当用户复制文本或图片时&#xff0c;Copilot操作菜单就会自动出现。 有媒体在…

《CSS 简易速速上手小册》第5章:CSS 动画与过渡(2024 最新版)

文章目录 5.1 CSS 过渡基础&#xff1a;网页的微妙舞步5.1.1 基础知识5.1.2 重点案例&#xff1a;按钮悬停效果5.1.3 拓展案例 1&#xff1a;渐变显示导航菜单5.1.4 拓展案例 2&#xff1a;动态调整元素大小 5.2 关键帧动画&#xff1a;编排你的网页芭蕾5.2.1 基础知识5.2.2 重…

基于vue+node.js的校园跳蚤市场系统多商家

校园跳蚤市场系统可以在短时间内完成大量的数据处理、帮助用户快速的查找校园跳蚤市场相关信息&#xff0c;实现的效益更加直观。校园跳蚤市场系统中采用nodejs技术和mysql数据库。主要包括管理员、发布者和用户三大部分&#xff0c;主要功能是实现对个人中心、用户管理、发布者…

【MATLAB源码-第138期】基于matlab的D2D蜂窝通信仿真,对比启发式算法,最优化算法和随机算法的性能。

操作环境&#xff1a; MATLAB 2022a 1、算法描述 D2D蜂窝通信介绍 D2D蜂窝通信允许在同一蜂窝网络覆盖区域内的终端设备直接相互通信&#xff0c;而无需数据经过基站或网络核心部分转发。这种通信模式具有几个显著优点&#xff1a;首先&#xff0c;它可以显著降低通信延迟&…

大模型训练所需的硬件配置

1. 引入 训练一个大模型&#xff0c;到底需要投入多少块GPU&#xff0c;需要多少数据&#xff0c;训练多长时间能达到一个不错的效果&#xff1f; 本文引用靠谱的数据&#xff0c;来回答这些问题。 2. 全流程训练 大模型的训练&#xff0c;简单来说&#xff0c;分为Pretrain…

C#计算矩形面积:通过定义结构 vs 通过继承类

目录 一、涉及到的知识点 1、结构 2.结构和类的区别 3.继承 4.使用类继承提高程序的开发效率 二、实例&#xff1a;通过定义结构计算矩形面积 1.源码 2.生成效果 三、实例&#xff1a;通过继承类计算梯形面积 1.源码 2.生成效果 一、涉及到的知识点 1、结构 结构是…

git安装配置

1、下载安装 下载地址 2、配置git用户 git config --global user.name "yw" git config --global user.email "88888qq.com" 3、git init 初始化 4、生成ssh密钥 mkdir .ssh //创建文件夹cd .ssh //进入新建文件夹 ssh-keygen -t rsa // 输入密钥文…

推荐系统|召回05_矩阵补充、最近邻查找

文章目录 矩阵补充Matrix Completion模型结构模型训练模型存储 矩阵补充Matrix Completion 模型结构 通过用户ID和物品ID分别找到对应的向量&#xff0c;然后去做内积&#xff0c;内积的数值可以去衡量匹配的程度。 不共享参数的意思是指用户ID和物品ID使用不同的Embedding L…

LeetCode 144 二叉树的前序遍历

大家新年快乐&#xff0c;long年大吉 今天的题很简单&#xff0c;前序用栈就行。 电脑没拿&#xff0c;用我妈的pad艰难敲代码&#xff0c;敲字 知识点随便写点吧&#xff0c;这里基础点挺多&#xff0c;以后补充下 栈&#xff1a;先进后出&#xff0c;数据结构用stack&…

鸿蒙(HarmonyOS)项目方舟框架(ArkUI)之Blank组件

鸿蒙&#xff08;HarmonyOS&#xff09;项目方舟框架&#xff08;ArkUI&#xff09;之Blank组件 一、操作环境 操作系统: Windows 10 专业版、IDE:DevEco Studio 3.1、SDK:HarmonyOS 3.1 二、Blank组件 空白填充组件&#xff0c;在容器主轴方向上&#xff0c;空白填充组件具…