翻译
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和请求头直接复制程序里测试,哪些参数是必须的,哪些参数是不需要的,哪些是加密的参数,遇到加密的参数就要逆向源码,通过搜索或堆栈跟踪找到生成加密参数的函数!

浙公网安备 33010602011771号