翻译

1 翻译工具评测

这里抽取了主流翻译平台,百度翻译、有道翻译、谷歌翻译、必应翻译、金山翻译和沪江翻译,以下是翻译测试:

句子:
The Commit Panel is where files and changes from your working directory are staged and committed.
谷歌:在提交面板中,暂存和提交工作目录中的文件和更改。
百度、必应和沪江:提交面板是暂存和提交工作目录中的文件和更改的位置。
有道:提交面板是暂存和提交工作目录中的文件和更改的地方。
金山:提交面板是从工作目录中归档和提交文件和更改的地方。

Changes not staged for commit.
百度和沪江:未暂存进行提交的更改。
有道:更改没有暂存以提交。
谷歌:尚未进行提交的更改。
金山: 未为提交而进行的更改。
必应:未暂存用于提交的更改。

What if you undid something, only to realize that you didn't want to undo it? GitKraken also has a Redo button so you can undo your undos.
百度和沪江:如果你拆开一些东西,却发现你不想撤销它呢?GitKraken还有一个重做按钮,你可以撤销你的撤销。
有道:如果你撤销了某件事,却发现自己并不想撤销它,那该怎么办?GitKraken还有一个重做按钮,这样你就可以撤销撤销。
谷歌:如果您撤消某些操作而只是意识到自己不想撤消该怎么办? GitKraken还具有“重做”按钮,因此您可以撤消撤消。
必应:如果你解开了什么东西, 才意识到你不想撤消它呢?GitKraken 还有一个重做按钮,因此您可以撤消撤消。
金山: 如果你解开了什么东西,却意识到你不想解开它呢? Git Kraken还有一个Redo按钮,这样您就可以撤消undos了。

Behold the evolution of GitKraken! Find out what’s new, what’s fixed, or just take a trip down memory lane with a nostalgic swagger, remembering those bugs of yesterday.
百度和沪江:看看吉特克拉肯的进化吧!找出什么是新的,什么是固定的,或者只是带着怀旧的大摇大摆地沿着记忆的小路走一趟,回忆起昨天的那些虫子。
有道:看看GitKraken的演变吧!看看什么是新的,什么是固定的,或者只是带着怀旧的神气在记忆的小路上旅行,回忆那些昨天的bug。
谷歌:看看GitKraken的发展! 找出最新消息,已解决的问题,或怀旧怀旧之情,回忆过去的那些虫子。
必应:看看 GitKraken 的演变!找出什么是新的, 什么是固定的, 或只是带着怀旧的狂妄沿着记忆车道旅行, 记住昨天的那些错误。
沪江:看看 GitKraken 的演变!找出什么是新的, 什么是固定的, 或只是带着怀旧的狂妄沿着记忆车道旅行, 记住昨天的那些错误。
金山:看看吉特克拉肯的进化! 找出什么是新的,什么是固定的,或者只是带着怀旧的昂首阔步走在记忆的道路上,记住昨天的那些虫子。

Git is a fast, scalable, distributed revision control system with an unusually rich command set that provides both high-level operations and full access to internals.
百度和沪江:Git是一个快速的、可伸缩的、分布式的修订控制系统,它具有异常丰富的命令集,可以提供高级操作和对内部构件的完全访问。
有道:Git是一种快速、可伸缩的分布式修订控制系统,具有异常丰富的命令集,提供高级操作和对内部的完全访问。
谷歌:Git是一种快速,可扩展的分布式修订版本控制系统,具有异常丰富的命令集,该命令集提供高级操作和对内部组件的完全访问权限。
必应:Git 是一个快速、可扩展的分布式修订控制系统,具有异常丰富的命令集,可提供高级操作和对内部功能的完全访问。
金山: Git是一个快速、可伸缩、分布式的修订控制系统,具有非常丰富的命令集,它提供高级操作和对内部的完全访问。

GitKraken uses profiles to store your app preferences, current Tabs, and Git config information.
百度和沪江:GitKraken使用配置文件存储应用程序首选项、当前选项卡和Git配置信息。
有道:GitKraken使用配置文件来存储你的应用偏好、当前标签和Git配置信息。
谷歌:GitKraken使用配置文件存储您的应用程序首选项,当前选项卡和Git配置信息。
必应:GitKraken 使用配置文件来存储应用首选项、当前选项卡和 Git 配置信息。
金山: Git Kraken使用配置文件来存储应用程序首选项、当前Tabs和Git配置信息。

Git is a free and open source distributed version control system designed to handle everything from small to very large projects with speed and efficiency.
百度和沪江:Git是一个免费的、开源的分布式版本控制系统,旨在快速高效地处理从小型到非常大的项目。
有道:Git是一个免费的开源分布式版本控制系统,它可以快速高效地处理从小型到大型的项目。
谷歌:Git是一个免费的开源分布式版本控制系统,旨在快速高效地处理从小型到大型项目的所有内容。
必应:Git 是一个免费且开源的分布式版本控制系统,旨在快速、高效地处理从小型到非常大的项目的所有内容。
金山: Git是一个自由开放的分布式版本控制系统,旨在以速度和效率处理从小到大的项目。

Git is easy to learn and has a tiny footprint with lightning fast performance. It outclasses SCM tools like Subversion, CVS, Perforce, and ClearCase with features like cheap local branching, convenient staging areas, and multiple workflows.
百度和沪江:Git易于学习,占用空间小,性能极快。它比Subversion、CVS、Perforce和ClearCase等SCM工具具有廉价的本地分支、方便的暂存区和多个工作流等特性。
有道:Git易于学习,占用空间小,性能极好。它超越了像Subversion、CVS、Perforce和ClearCase这样的配置管理工具,具有像廉价的本地分支、方便的staging区域和多个工作流这样的特性。
谷歌:Git易于学习,占地面积小,具有闪电般的快速性能。 它具有廉价的本地分支,方便的暂存区域和多个工作流等功能,其性能优于Subversion,CVS,Perforce和ClearCase等SCM工具。
必应:Git 易于学习,具有小尺寸,性能非常快。它通过廉价的本地分支、方便的暂存区域和多个工作流等功能,超越 SCM 工具(如 Subversion、CVS、Perforce 和 ClearCase)。
金山: Git很容易学习,并且有一个微小的足迹,具有闪电般的快速性能。 它超越了像Subversion、CVS、Perforce和ClearCase这样的SCM工具,具有廉价的本地分支、方便的分期区域和多个工作流等特性。

The advantages of Git compared to other source control systems.
百度和沪江:Git与其他源代码管理系统相比的优势。
有道:Git的优势相比其他源代码控制系统。
谷歌:与其他源代码控制系统相比,Git的优势。
必应:与其他源代码管理系统相比,Git 的优势。
金山: 与其他源控制系统相比,Git的优势。

Command reference pages, Pro Git book content, videos and other material.
百度和沪江:命令参考页,专业Git书籍内容,视频和其他材料。
有道:Git命令参考页面,专业书内容,视频和其他材料。
谷歌:命令参考页,Pro Git书籍内容,视频和其他材料。
必应:命令参考页、Pro Git 书籍内容、视频和其他材料。
金山: 命令参考页面,ProGit图书内容,视频等素材。

GUI clients and binary releases for all major platforms.
百度和沪江:所有主要平台的GUI客户端和二进制版本。
有道:为所有主要平台GUI客户端和二进制版本。
谷歌:适用于所有主要平台的GUI客户端和二进制发行版。
必应:所有主要平台的 GUI 客户端和二进制版本。
金山: 所有主要平台的GUI客户端和二进制版本。

Get involved! Bug reporting, mailing list, chat, development and more.
百度和沪江:参与进来!错误报告,邮件列表,聊天,开发等等。
有道:参与!Bug报告,邮件列表,聊天,开发等等。
谷歌:参与其中! 错误报告,邮件列表,聊天,开发等。
必应:参与进来!错误报告、邮件列表、聊天、开发等。
金山:快介入! bug报告,邮件列表,聊天,开发等。

评测结果:

由于沪江翻译跟百度完全一样,故排除沪江。
金山相比百度、谷歌、必应和有道差很多,句子翻译生硬而且不准,故排除金山。

2 翻译项目

2.1 项目架构:

客户端---服务器,

客户端有一个翻译页面,通过油猴脚本,监听在非翻译页面的鼠标选中文字或在翻译页面的输入框输入文本,客户端发送请求,服务器再通过请求参数去爬取翻译结果,然后响应客户端。

以下是翻译页面 translation.html

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8" />
        <title>翻译</title>
        <style>
            .container {
                display: flex;
                flex-direction: column;
            }
            .tool_bar,
            .fl_bar,
            .tl_bar {
                display: flex;
                justify-content: space-around;
                width: 400px;
            }
            .container > div {
                margin: auto;
                width: 70%;
                display: flex;
            }
            .switch_icon {
                cursor: pointer;
            }
            textarea {
                resize: none;
                height: 120px;
                width: 600px;
                padding: 20px;
                font-family: "Roboto", arial, sans-serif;
                font-size: 18px !important;
                margin-bottom: 5px;
            }
            textarea[class='read'] {
                border: 1px solid #eee;
                border-left: 0;
                background-color: #f0f0f0;
            }
            textarea:focus {
                outline: none;
            }
            .btn {
                width: 30%;
                padding-left: 20px;
                color: #4285f4;
                border-bottom: solid 3px;
            }
        </style>
    </head>
    <body>
        <div class="container">
            <div class="tool_bar">
                <div class="fl_bar">
                    <div class="btn">英语</div>
                </div>
                <div class="switch_icon">
                    <svg
                        t="1598407478607"
                        class="icon"
                        viewBox="0 0 1024 1024"
                        version="1.1"
                        xmlns="http://www.w3.org/2000/svg"
                        p-id="1162"
                        width="30"
                        height="30"
                    >
                        <path
                            d="M830.4 457.9H193.6c-26.5 0-48-21.5-48-48s21.5-48 48-48h636.8c26.5 0 48 21.5 48 48s-21.5 48-48 48z"
                            fill="#8a8a8a"
                            p-id="1163"
                        ></path>
                        <path
                            d="M193.6 457.9c-12.3 0-24.6-4.7-33.9-14.1-18.8-18.8-18.8-49.1 0-67.9L339.6 196c18.8-18.8 49.1-18.8 67.9 0s18.8 49.1 0 67.9l-180 180c-9.3 9.4-21.6 14-33.9 14zM830.4 662.1H193.6c-26.5 0-48-21.5-48-48s21.5-48 48-48h636.8c26.5 0 48 21.5 48 48s-21.5 48-48 48z"
                            fill="#8a8a8a"
                            p-id="1164"
                        ></path>
                        <path
                            d="M650.5 841.9c-12.3 0-24.6-4.7-33.9-14.1-18.8-18.8-18.8-49.2 0-67.9L796.5 580c18.8-18.8 49.1-18.8 67.9 0s18.8 49.2 0 67.9l-180 180c-9.4 9.4-21.6 14-33.9 14z"
                            fill="#8a8a8a"
                            p-id="1165"
                        ></path>
                    </svg>
                </div>
                <div class="tl_bar">
                    <div class="btn">中文</div>
                </div>
            </div>
            <div class="text_input">
                <textarea class="input" placeholder="输入文字" autofocus></textarea>
            </div>
            <div class="google">
                <textarea
                    class="read"
                    readonly="readonly"
                    placeholder="谷歌"
                ></textarea>
            </div>
            <div class="baidu">
                <textarea
                    class="read"
                    readonly="readonly"
                    placeholder="百度"
                ></textarea>
            </div>
            <div class="bing">
                <textarea
                    class="read"
                    readonly="readonly"
                    placeholder="必应"
                ></textarea>
            </div>
            <div class="youdao">
                <textarea
                    class="read"
                    readonly="readonly"
                    placeholder="有道"
                ></textarea>
            </div>
        </div>
    </body>
    <script src="static\translation.js"></script>
</html>

以下是油猴脚本 翻译.js

/*
 * @Author: Negan
 * @Date: 2020-08-26 21:28:48
 * @Last Modified by: Negan
 * @Last Modified time: 2020-08-29 16:52:13
 */
// ==UserScript==
// @name         翻译
// @namespace    http://tampermonkey.net/
// @description  翻译
// @author       Negan
// @include      *
// @grant GM_setValue
// @grant GM_getValue
// @grant unsafeWindow
// @run-at       document-end
// ==/UserScript==

// 谷歌翻译加密算法
function tk(a) {
    /*
    参数a : 传入的字符串
    返回值 加密字符串
    */
    let b = "440145.3724039124";
    let d = b.split(".");
    b = Number(d[0]);
    for (var e = [], f = 0, g = 0; g < a.length; g++) {
        var k = a.charCodeAt(g);
        128 > k
            ? (e[f++] = k)
            : (2048 > k
                  ? (e[f++] = (k >> 6) | 192)
                  : (55296 == (k & 64512) &&
                    g + 1 < a.length &&
                    56320 == (a.charCodeAt(g + 1) & 64512)
                        ? ((k =
                              65536 +
                              ((k & 1023) << 10) +
                              (a.charCodeAt(++g) & 1023)),
                          (e[f++] = (k >> 18) | 240),
                          (e[f++] = ((k >> 12) & 63) | 128))
                        : (e[f++] = (k >> 12) | 224),
                    (e[f++] = ((k >> 6) & 63) | 128)),
              (e[f++] = (k & 63) | 128));
    }
    a = b;
    for (f = 0; f < e.length; f++) (a += e[f]), (a = du(a, "+-a^+6"));
    a = du(a, "+-3^+b+-f");
    a ^= Number(d[1]) || 0;
    0 > a && (a = (a & 2147483647) + 2147483648);
    a %= 1e6;
    return a.toString() + "." + (a ^ b);

    function du(a, b) {
        for (var c = 0; c < b.length - 2; c += 3) {
            var d = b.charAt(c + 2);
            d = "a" <= d ? d.charCodeAt(0) - 87 : Number(d);
            d = "+" == b.charAt(c + 1) ? a >>> d : a << d;
            a = "+" == b.charAt(c) ? (a + d) & 4294967295 : a ^ d;
        }
        return a;
    }
}
// 百度翻译加密算法
function getSign(query) {
    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) {
        let i = "320305.131321201";
        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,
            l =
                "" +
                String.fromCharCode(103) +
                String.fromCharCode(116) +
                String.fromCharCode(107);
        u = null !== i ? i : (i = "320305.131321201" || "") || "";
        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)
        );
    }
    return e(query);
}
// 这里是开发,需要改的有 translation_url 和 tranlaste_url
let translation_url = "http://192.168.0.107/translation";
// 翻译页面和非翻译页面分开处理
if (location.href != translation_url) {
    // 非翻译页面的处理
    let body = document.body;
    body.onmouseup = () => {
        let text = window.getSelection().toString();
        var re = /[A-Za-z]+/;
        let query = GM_getValue("query");
        // 不能为空,不能全是中文,不能跟储存过的一样
        if (text != "" && re.test(text) && query != text) {
            GM_setValue("query", text);
        }
    };
} else {
    // 翻译页面的处理
    function tranHandler(query) {
        input_textarea.value = query;
        let translate_url = "http://192.168.0.107/translate";
        let init = {
            method: "POST",
            headers: { "content-type": "application/json" },
        };
        async function ajaj(translate_url, init) {
            let resp = await fetch(translate_url, init);
            let json = await resp.json();
            return json;
        }
        async function google() {
            let body = {};
            let google_url = `https://translate.google.cn/translate_a/single?client=webapp&sl=en&tl=zh-CN&hl=zh-CN&dt=at&dt=bd&dt=ex&dt=ld&dt=md&dt=qca&dt=rw&dt=rm&dt=sos&dt=ss&dt=t&source=bh&ssel=0&tsel=0&xid=45662847&kc=1&tk=${tk(
                query
            )}&q=${query}`;
            body.google = [google_url, ""];
            init.body = JSON.stringify(body);
            let json = await ajaj(translate_url, init);
            let box = document.querySelector(".google>textarea");
            let text = "";
            for (let i of json.google[0]) {
                if (i[0] == null) break;
                text += i[0];
            }
            box.value = text;
        }
        google();
        async function baidu() {
            let body = {};
            let baidu_url = "https://fanyi.baidu.com/v2transapi?from=en&to=zh";
            let baidu_data = {
                from: "en",
                to: "zh",
                transtyle: "realtime",
                domain: "common",
                simple_means_flag: 3,
                token: "37037d888fa3a9beaa06a5fafe451148",
                query: query,
                sign: getSign(query),
            };
            body.baidu = [baidu_url, baidu_data];
            init.body = JSON.stringify(body);
            let json = await ajaj(translate_url, init);
            let box = document.querySelector(".baidu>textarea");
            box.value = json.baidu.trans_result.data[0].dst;
        }
        baidu();
        async function bing() {
            let body = {};
            let bing_url = "https://cn.bing.com/ttranslatev3";
            let bing_data = {
                fromLang: "en",
                to: "zh-Hans",
                text: query,
            };
            body.bing = [bing_url, bing_data];
            init.body = JSON.stringify(body);
            let json = await ajaj(translate_url, init);
            let box = document.querySelector(".bing>textarea");
            box.value = json.bing[0].translations[0].text;
        }
        bing();
        async function youdao() {
            let body = {};
            let youdao_url = "http://fanyi.youdao.com/translate";
            let youdao_data = {
                doctype: "json",
                from: "en",
                to: "zh-CHS",
                i: query,
            };
            body.youdao = [youdao_url, youdao_data];
            init.body = JSON.stringify(body);
            let json = await ajaj(translate_url, init);
            let box = document.querySelector(".youdao>textarea");
            let text = "";
            for (let i of json.youdao.translateResult[0]) {
                text += i.tgt;
            }
            box.value = text;
        }
        youdao();
    }
    // 监听鼠标选中的字符
    setInterval(() => {
        let query = GM_getValue("query");
        let input_textarea = document.querySelector(".text_input>textarea");
        if (input_textarea.value != query && query != "") {
            tranHandler(query);
        }
    }, 1);
    // 监听输入框内输入的字符
    let input_textarea = document.querySelector(".text_input>textarea");
    input_textarea.oninput = () => {
        let query = input_textarea.value;
        if (query != "") {
            tranHandler(query);
        }
        GM_setValue("query", query);
    };
}

2.2 爬取谷歌翻译、百度翻译、必应翻译和有道翻译

通常流程是打开”开发者工具“,然后切换到网络模块开始抓包,找到翻译的url,分析请求url和参数,然后将url和请求头直接复制程序里测试,哪些参数是必须的,哪些参数是不需要的,哪些是加密的参数,遇到加密的参数就要逆向源码,通过搜索或堆栈跟踪找到生成加密参数的函数!

posted @ 2021-04-06 12:55  savagefoo  阅读(126)  评论(0)    收藏  举报