最近在学习的小玩意,发现腾讯的文档十分坑爹,里面有很多错误的指示。
不过腾讯的机器翻译还是很牛的,我觉得翻译水准比谷歌好很多。
腾讯的机器翻译貌似在试用阶段,不收费,用QQ或微信登录即可申请使用。
首先获得SecretId和SecretKey,最好是创建一个子用户,因为这两个“秘密”都要明文写在js里。创建一个工程,这样就有了ProjectId。
准备好如下的参数:
var reqParams = [
[ "Action", "TextTranslate" ],
[ "Nonce", Nonce ], // 随机整数
[ "ProjectId", ProjectId],
["Region", "ap-beijing"],
["SecretId", SecretId],
["Source","auto"], // 自动检测要翻译的文本的语种
["SourceText", sourceText], // 要翻译的文本。生成签名时,必须使用原文本,即没有做uri编码的
["Target","zh"], // 翻译成哪个语种,这里是翻译成中文
["Timestamp",timestamp ], // 时间戳,以秒为单位,而js里用Date对象获取的时间戳是毫秒的,注意。腾讯文档说时间戳是0时区的,但其实就是东八区(北京时区)
["Version","2018-03-21"]
];
上面是一个嵌套的数组,或者说二维数组,这是为了方便排序(上面的顺序已经是排好了的)。必须先按参数的字母顺序排序后,拼成字符串,再做签名。
参数对转字符串函数:
// 参数对转成字符串
function paramsToStr(params)
{
// 按参数名的字典序排序
params.sort(function(a,b){
if(a[0] < b[0]) return -1;
else if(a[0] > b[0]) return 1;
return 0;
});
// 连成字符串
var parPairStrArr = [];
for (var i in params)
{
var par = params[i];
var parStr = par.join("=");
parPairStrArr.push(parStr);
}
var str = parPairStrArr.join("&");
return str;
}
有了参数字符串,再构造签名用的字符串,它需要包含请求方法、请求路径、请求参数。
// 签名原文字符串
var signStr = reqMethod + makeBaseUrl(host, hostPath) + "?" + paramsStr; // reqMethod 就是 GET 或 POST
// makeBaseUrl 只是简单的返回了腾讯翻译的域名(不包括https://),在这里是tmt.tencentcloudapi.com
// 签名
var hs = CryptoJS.HmacSHA1(signStr, SecretKey);
var rawSig = CryptoJS.enc.Base64.stringify(hs);
console.log("rawSig=" + rawSig);
var sig = encodeURIComponent(rawSig); // 必须使用 encodeURIComponent,对base64字符串中的特殊字符也做uri编码
return sig;
获取到 sig 后,下面要生成真正用于http请求的url,在这里使用 GET 方法,所以参数都直接附在Url中。这时,sourceText,也就是待翻译文本,需要先把空格替换成+号、在做uri编码。
var iSourceText = reqParams.findIndex((p)=>(p[0]=="SourceText"));
reqParams[iSourceText][1] = encodeURI(sourceText.replace(/\ /g,"+"));
var paramsStr = paramsToStr(reqParams);
var url = "https://" + makeBaseUrl(host, hostPath) + "?" + paramsStr + "&Signature=" + sig;
console.log("url=" + url);
接下来就是使用 XMLHttpRequest 做http请求了。但是腾讯翻译服务器那边没有允许跨域。可以装个插件解决此问题。
允许浏览器跨域请求http的插件:https://chrome.google.com/webstore/detail/allow-control-allow-origi/nlfbmbojpeacfghkpbjhddihlkkiljbi
完整代码:
1 // 参数对转成字符串
2 function paramsToStr(params)
3 {
4 // 按参数名的字典序排序
5
6 params.sort(function(a,b){
7 if(a[0] < b[0]) return -1;
8 else if(a[0] > b[0]) return 1;
9 return 0;
10 });
11
12 // 连成字符串
13
14 var parPairStrArr = [];
15 for (var i in params)
16 {
17 var par = params[i];
18 var parStr = par.join("=");
19 parPairStrArr.push(parStr);
20 }
21 var str = parPairStrArr.join("&");
22 return str;
23 }
24
25 function makeBaseUrl(host, hostPath)
26 {
27 return host + hostPath;
28 }
29
30 // 声称签名
31 function makeSignature(reqMethod, host, hostPath, reqParams)
32 {
33 var paramsStr = paramsToStr(reqParams);
34
35 // 签名原文字符串
36 var signStr = reqMethod + makeBaseUrl(host, hostPath) + "?" + paramsStr;
37
38 // 签名
39 var hs = CryptoJS.HmacSHA1(signStr, SecretKey);
40 var rawSig = CryptoJS.enc.Base64.stringify(hs);
41 console.log("rawSig=" + rawSig);
42 var sig = encodeURIComponent(rawSig);
43 return sig;
44 }
45
46 // 发送翻译请求
47 function sendTrans(sourceText, cb)
48 {
49 if(sourceText == null || sourceText == "")
50 {
51 cb("");
52 return;
53 }
54
55 var SecretId = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";
56 var SecretKey = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXX";
57
58 var reqMethod = "GET";
59 var host = "tmt.tencentcloudapi.com";
60 var hostPath = "/";
61
62 var Nonce = Math.floor(Math.random() * 1000000000);
63 var date = new Date();
64 var timestamp = Math.floor(date.getTime() / 1000);
65
66 var reqParams = [
67 [ "Action", "TextTranslate" ],
68 [ "Nonce", Nonce ],
69 [ "ProjectId", xxxxxxxx],
70 ["Region", "ap-beijing"],
71 ["SecretId", SecretId],
72 ["Source","auto"],
73 ["SourceText", sourceText], // 生成签名时,使用原文本
74 ["Target","zh"],
75 ["Timestamp",timestamp ],
76 ["Version","2018-03-21"]
77 ];
78
79 // 生成签名
80 var sig = makeSignature(reqMethod, host, hostPath, reqParams);
81
82 // 原文本中所有的空格替换成+号,然后uri编码
83 var iSourceText = reqParams.findIndex((p)=>(p[0]=="SourceText"));
84 reqParams[iSourceText][1] = encodeURI(sourceText.replace(/\ /g,"+"));
85 var paramsStr = paramsToStr(reqParams);
86
87 var url = "https://" + makeBaseUrl(host, hostPath) + "?" + paramsStr + "&Signature=" + sig;
88 console.log("url=" + url);
89
90 var xhr = new XMLHttpRequest();
91 xhr.timeout = 3000;
92 xhr.ontimeout = function()
93 {
94 cb("超时,翻译失败");
95 };
96 xhr.onerror = function()
97 {
98 cb("错误,翻译失败");
99 };
100
101 xhr.open(reqMethod, url, true);
102 xhr.responseType = "json";
103 var sendRet = xhr.send();
104 xhr.onreadystatechange = function()
105 {
106 if(xhr.readyState == 4)
107 {
108 if(xhr.status == 200 || xhr.status == 304)
109 {
110 var ret = xhr.response;
111 cb(ret.Response.TargetText);
112 }
113 else
114 {
115 cb(xhr.responseText);
116 }
117 }
118 };
119 }