基于百度翻译API的node插件

您所在的位置:网站首页 anode翻译中文 基于百度翻译API的node插件

基于百度翻译API的node插件

2024-02-25 21:39| 来源: 网络整理| 查看: 265

背景

做过国际化的项目就明白要把每处的文案翻译成不同的语言版本,如果只是一点点,自己去百度上翻译成对应语言版本,copy过来就ok了,但是如果这个项目文案特别多的话,自己去翻译,可能会烦死

umijs如果构建国际化,会有一个locals的目录,里面存放前端项目中不需要存进数据库的各种语言版本文件,我就想实现只写中文的,其他版本通过nodejs+百度翻译api直接生成

一、百度API 文档地址

需要注册开发者(使用时需要开发者的appid+密钥),并开通通用翻译API,每个月有200万个免费字符翻译,所以不用太担心会出现费用

我本來想使用google翻译api的,但是貌似需要翻墙,还是做个好公民,对比了有道和百度,百度翻译api更稳定和准确些,最终决定使用百度翻译API,下面分享一些坑:

1、直接使用百度翻译的api很坑,首先传参很多,最终要的还是无法并发,如果你频繁调用的话就是返回 54003

2、sign生成,需要md5加密多个字段的拼接

然后我写了这个插件,解决下这些问题,让使用更加方面!

二、该插件优点 1、默认中转英 translate('密钥').then((res) => { console.log('res', res); // res secret key }); 2、能直接传入复杂对象值来进行翻译,例如:{ key: value },只翻译value,不翻译key translate({ name: 'faker' }, { from: "en", to: 'zh' }).then((res) => { console.log('res', res); // res { name: '冒牌货' } }); 3、支持嵌套对象 translate({ name: "小明", info: { father: "小明爸爸", mather: "小明妈妈" }, }).then((res) => { console.log("res", res); /* res { name: 'Xiao Ming', info: { father: "Xiao Ming's father", mather: "Xiao Ming's mother" } } */ }); // 各种对象嵌套都行,数组嵌套对象,对象嵌套数组 translate([ { name: "小明", info: { father: "小明爸爸", mather: "小明妈妈" }, }, { name: "小红", info: { father: "小红爸爸", mather: "小红妈妈" }, }, ]).then((res) => { console.log("res", res); /* res [ { name: 'Xiao Ming', info: { father: "Xiao Ming's father", mather: "Xiao Ming's mother" } }, { name: 'Xiaohong', info: { father: 'Little red Dad', mather: 'Little red mother' } } ] */ }); 4、解决了百度api的并发问题 translate('密钥', { from: "zh", to: 'en' }).then((res) => { console.log('res', res); }); translate({ name: 'faker' }, { from: "en", to: 'zh' }).then((res) => { console.log('res', res); });

直接用百度api,同时翻译,那么就会导致前面的请求失败,当然我不是vip,看说明文档上可能是没买vip,就不支持并发,但是我就是不想花钱,毕竟穷!

实现解决并发原理:其实就是上次请求没有完成,我就将这次请求排入队列

if (this.isRequesting) { return new Promise((resolve) => { setTimeout(() => { this.requestApi(value, parames).then((res) => { resolve(res); }); }, 1000); }); } 5、对于复杂对象直接传入翻译的优化

不用担心直接传入一个对象,有很多数据,因为我将对象的数据合并之后,只发送了一个请求到百度翻译,然后我再对应解析出来数据,其实传入对象,数组对翻译性能更高

translate([ { name: "小明", info: { father: "小明爸爸", mather: "小明妈妈" }, }, { name: "小红", info: { father: "小红爸爸", mather: "小红妈妈" }, }, ]).then((res) => { console.log("res", res); /* res [ { name: 'Xiao Ming', info: { father: "Xiao Ming's father", mather: "Xiao Ming's mother" } }, { name: 'Xiaohong', info: { father: 'Little red Dad', mather: 'Little red mother' } } ] */ });

这个翻译流程我把图放出来,明显值请求了4次api,每个对象如果当前层级都是string,自然会被我合并,就能一次翻译完成:

三、插件源码 const md5 = require("md5-node"); const axios = require("axios"); function MysKeyTranslate(config) { this.requestNumber = 0; this.config = { showProgress: true, requestNumber: 1, agreement: 'http', ...config, }; this.baiduApi = `${this.config.agreement}://api.fanyi.baidu.com/api/trans/vip/translate` this.createUrl = (domain, form) => { let result = domain + "?"; for (let key in form) { result += `${key}=${form[key]}&`; } return result.slice(0, result.length - 1); }; this.requestApi = (value, parames) => { if (this.requestNumber >= this.config.requestNumber) { return new Promise((resolve) => { setTimeout(() => { this.requestApi(value, parames).then((res) => { resolve(res); }); }, 1000); }); } this.requestNumber++; const { appid, secret } = this.config; const q = value; const salt = Math.random(); const sign = md5(`${appid}${q}${salt}${secret}`); const fromData = { ...parames, q: encodeURIComponent(q), sign, appid, salt, }; const fanyiApi = this.createUrl(this.baiduApi, fromData); // console.log("fanyiApi", fanyiApi); return new Promise((resolve) => { axios .get(fanyiApi) .then(({ data: res }) => { if (this.config.showProgress) console.log("翻译结果:", res); if (!res.error_code) { const resList = res.trans_result; resolve(resList); } }) .finally(() => { setTimeout(() => { this.requestNumber--; }, 1000); }); }); }; this.translate = async (value, parames = { from: "zh", to: "en" }) => { let result = ""; if (typeof value === "string") { const res = await this.requestApi(value, parames); result = res[0]["dst"]; } if ( Array.isArray(value) || Object.prototype.toString.call(value) === "[object Object]" ) { result = await this._createObjValue(value, parames); } return result; }; this._createObjValue = async (value, parames) => { let index = 0; const obj = Array.isArray(value) ? [] : {}; const strDatas = Array.isArray(value) ? value : Object.values(value); const reqData = strDatas .filter((item) => typeof item === "string") .join("\n"); const res = reqData ? await this.requestApi(reqData, parames) : []; for (let key in value) { if (typeof value[key] === "string") { obj[key] = res[index]["dst"]; index++; } if ( Array.isArray(value[key]) || Object.prototype.toString.call(value[key]) === "[object Object]" ) { obj[key] = await this.translate(value[key], parames); } } return obj; }; return this.translate; } export default MysKeyTranslate; 四、使用方法 1、安装 md5-node ,使用百度翻译api需要md5 npm i --save md5-node 2、保存上面的源码到一个文件 3、使用 const MysKeyTranslate = require("./MysKeyTranslate.js"); // 引入刚才保存的文件 const translate = new MysKeyTranslate({ appid: "", // 你的appid 去百度开发者平台查看 http://api.fanyi.baidu.com/doc/21 secret: "", // 你的密钥 }); // 下面就可以直接使用了 translate('密钥', { from: "zh", to: 'en' }).then((res) => { console.log('res', res); }); 4、如果想读取中文文件,然后生成其他版本内容,可以参考下面 const MysKeyTranslate = require("./MysKeyTranslate.js"); // 引入刚才保存的文件 const data = require("./ZH_CN.ts"); // 引入中文文件 const translate = new MysKeyTranslate({ appid: "", // 你的appid 去百度开发者平台查看 http://api.fanyi.baidu.com/doc/21 secret: "", // 你的密钥 }); ["en", "jp"].forEach((item) => { translate(data, { from: "zh", to: item }).then((res) => { createFile(item, res); }); }); function createFile(fileName, fileContent) { fs.writeFileSync( `ZH_${fileName.toUpperCase()}.ts`, `export default ${JSON.stringify(fileContent)}` ); }

ZH_CN.ts文件内容如下:

module.exports = [ { 'home.title': '首页', 'home.name': '首页名称', 'book': { 'name': '时间简史', 'date': '公元前203年' } } ] 五、语言对应表

常用语言:

中文 zh 英语 en 韩语 kor 日语 jp 法语 fra 俄语 ru

完整版请参考百度翻译文档: api.fanyi.baidu.com/doc/21

六、后续

后面有时间的话我可以让这个不止在代码层面,还是上传到npm,完善一些功能,增加能命令直接生成其他语言版本的文件...



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3