@voerkai18n/cli
Version:
命令行工具,用来初始化/提取/编译/自动翻译等工具链
119 lines (112 loc) • 5.26 kB
JavaScript
const md5 = require("md5");
const qs = require("qs");
const baiduTagMap = require('bcp47-language-tags/mapper/baidu').default;
const axios = require('axios');
// const ERRORS = {
// 52000: "成功",
// 52001: "请求超时 - 检查请求query是否超长,以及原文或译文参数是否在支持的语种列表里",
// 52002: "系统错误 - 请重试",
// 52003: "未授权用户 - 请检查appid是否正确或者服务是否开通",
// 54000: "必填参数为空 - 请检查是否少传参数",
// 54001: "签名错误 - 请检查您的签名生成方法",
// 54003: "访问频率受限 - 请降低您的调用频率,或在控制台进行身份认证后切换为高级版/尊享版",
// 54004: "账户余额不足 - 请前往管理控制台为账户充值",
// 54005: "长query请求频繁 - 请降低长query的发送频率,3s后再试",
// 58000: "客户端IP非法 - 检查个人资料里填写的IP地址是否正确,可前往开发者信息-基本信息修改",
// 58001: "译文语言方向不支持 - 检查译文语言是否在语言列表里",
// 58002: "服务当前已关闭 - 请前往管理控制台开启服务",
// 58003: "此IP已被封禁 - 同一IP当日使用多个APPID发送翻译请求,则该IP将被封禁当日请求权限,次日解封。请勿将APPID和密钥填写到第三方软件中。",
// 90107: "认证未通过或未生效 - 请前往我的认证查看认证进度",
// 20003: "请求内容存在安全风险 - 请检查请求内容"
// }
/**
* 文本中的插值变量不进行翻译,所以需要进行替换为特殊字符,翻译后再替换回来
*
* 如“My name is {},I am {} years old” 先替换为“My name is __$_$_$__,I am __$_$_$__ years old”
* 翻译后再替换回来
*
* @param {String | Object} messages
*/
function replaceInterpVars(messages){
const interpVars = []
const msgs = Array.isArray(messages) ? messages : Object.keys(messages)
const replacedMessages = msgs.map((message)=>{
let vars=[]
let result = message.replaceAll(/\{\s*.*?\s*\}/gm,(matched)=>{
vars.push(matched)
return `__$_$_$__`
})
interpVars.push(vars)
return result
})
return [replacedMessages,interpVars]
}
/**
* 将翻译后的内容还原为插值变量
* @param {String[]} messages 翻译后的内容
* @param {*} interpVars
*/
function restoreInterpVars(messages,interpVars){
return messages.map((message,index)=>{
let i = 0
return message.replaceAll(/__\s*\$_\s*\$_\s*\$__/gm,()=>interpVars[index][i++])
})
}
/**
* 在翻译内容中如果包含|,()等在插值变量里面需要的符号时,百度翻译会将其转成全角符号,这会导致插值变量无法识别,所以需要转回来
* @param {*} message
* @returns
*/
function fixMesssage(message){
return message.replaceAll("|", "|")
.replaceAll(",", ",")
.replaceAll("(", "(")
.replaceAll(")", ")");
}
/**
* q string 是 请求翻译query UTF-8编码
* from string 是 翻译源语言 可设置为auto
* to string 是 翻译目标语言 不可设置为auto
* appid string 是 APPID 可在管理控制台查看
* salt string 是 随机数 可为字母或数字的字符串
* sign string 是 签名 appid+q+salt+密钥的MD5值
*
* @param {} ctx {id,key,url}
*/
module.exports = function(params){
const { key:appkey,id:appid ,url:baseurl = "http://api.fanyi.baidu.com/api/trans/vip/translate" } = params
return {
translate:async (texts=[],from="zh",to="en")=>{
from = baiduTagMap[from] || from;
to = baiduTagMap[to] || to;
const [fixedTexts,interpVars] = replaceInterpVars(texts)
texts = fixedTexts.join("\n")
// saltStep1. 拼接字符串1:
// 拼接[appid=2015063000000001][q=apple][salt=1435660288][密钥=12345678]得到字符串1:“2015063000000001apple143566028812345678”
// Step2. 计算签名:(对字符串1做MD5加密)
// sign=MD5(2015063000000001apple143566028812345678),得到sign=f89f9594663708c1605f3d736d01d2d4
const salt = new Date().getTime()
const sign = md5(`${appid}${texts}${salt}${appkey}`);
let params = qs.stringify({
q:texts,
from,
to,
appid,
salt,
sign
});
try {
const response = await axios.get(`${baseurl}?${params}`);
const data = response.data;
if (data.error_code) {
throw new Error(`${data.error_msg}(code=${data.error_code})`);
} else {
const result = data.trans_result.map(item => fixMesssage(item.dst));
return restoreInterpVars(result, interpVars);
}
} catch (err) {
throw err;
}
}
}
}