translation.js
Advanced tools
Comparing version 0.4.0 to 0.4.1
/*! | ||
* translation.js v0.4.0 | ||
* translation.js v0.4.1 | ||
* https://github.com/Selection-Translator/translation.js#readme | ||
@@ -70,2 +70,4 @@ * Copyright 2015 Milk Lee <milk.lee@qq.com> | ||
var UNKNOWN_ERROR = 2 | ||
function Translation () { | ||
@@ -135,23 +137,18 @@ this.APIs = {} | ||
p.call = function (method, queryObj) { | ||
var that = this | ||
return new Promise(function (resolve, reject) { | ||
var instances = that.APIs[queryObj.api] | ||
if (!instances) { | ||
return reject('没有注册 ' + queryObj.api + ' API。') | ||
} | ||
var instances = this.APIs[queryObj.api] | ||
if (!instances) return Promise.reject('没有注册 ' + queryObj.api + ' API。') | ||
var a = instances.shift() | ||
instances.push(a) | ||
a[method](queryObj).then(function (resultObj) { | ||
if (method === 'translate') { | ||
resultObj.api = a | ||
} | ||
resolve(resultObj) | ||
var a = instances[0] | ||
if (!a[method]) return Promise.reject(a.name + '不支持' + method + '方法。') | ||
instances.push(instances.shift()) | ||
return a[method](queryObj) | ||
.then(function (resultObj) { | ||
if (method === 'translate') resultObj.api = a | ||
return resultObj | ||
}, function (superAgentError) { | ||
if (superAgentError == null) { | ||
return reject() | ||
} | ||
reject(analyzeErrorType(superAgentError)) | ||
if (superAgentError == null) return Promise.reject(UNKNOWN_ERROR) | ||
return Promise.reject(analyzeErrorType(superAgentError)) | ||
}) | ||
}) | ||
} | ||
@@ -163,4 +160,5 @@ | ||
tjs.SERVER_ERROR = SERVER_ERROR | ||
tjs.UNKNOWN_ERROR = UNKNOWN_ERROR | ||
// 绑定内置构造函数 | ||
// 绑定内置的翻译接口 | ||
tjs.BaiDu = __webpack_require__(1) | ||
@@ -170,3 +168,3 @@ tjs.YouDao = __webpack_require__(4) | ||
tjs.Google = __webpack_require__(6) | ||
tjs.GoogleCN = __webpack_require__(7) | ||
tjs.GoogleCN = __webpack_require__(9) | ||
@@ -226,3 +224,3 @@ module.exports = tjs | ||
this.type = 'BaiDu' | ||
this.link = 'http://fanyi.baidu.com/' | ||
this.link = 'http://fanyi.baidu.com' | ||
} | ||
@@ -249,20 +247,14 @@ | ||
var that = this | ||
return new Promise(function (resolve, reject) { | ||
superagent | ||
.get(that.link + '/v2transapi') | ||
.query({ | ||
from: standard2custom[queryObj.from] || 'auto', | ||
to: standard2custom[queryObj.to] || 'zh', // 非标准接口一定要提供目标语种 | ||
query: queryObj.text, | ||
transtype: 'hash', | ||
simple_means_flag: 3 | ||
}) | ||
.end(function (err, res) { | ||
if (err) { | ||
reject(err) | ||
} else { | ||
resolve(that.transform(res.body, queryObj)) | ||
} | ||
}) | ||
}) | ||
return superagent | ||
.get(this.link + '/v2transapi') | ||
.query({ | ||
from: standard2custom[queryObj.from] || 'auto', | ||
to: standard2custom[queryObj.to] || 'zh', // 非标准接口一定要提供目标语种 | ||
query: queryObj.text, | ||
transtype: 'hash', | ||
simple_means_flag: 3 | ||
}) | ||
.then(function (res) { | ||
return that.transform(res.body, queryObj) | ||
}) | ||
} | ||
@@ -301,7 +293,5 @@ | ||
try { | ||
var detailed = [] | ||
rawRes.dict_result.simple_means.symbols[0].parts.forEach(function (v) { | ||
detailed.push(v.part + ' ' + v.means.join(',')) | ||
obj.detailed = rawRes.dict_result.simple_means.symbols[0].parts.map(function (v) { | ||
return v.part + ' ' + v.means.join(',') | ||
}) | ||
obj.detailed = detailed | ||
} catch (e) {} | ||
@@ -324,3 +314,3 @@ | ||
/** | ||
* 检测语种的方法, 返回的语种为百度自己格式的语种。 | ||
* 检测语种的方法 | ||
* @param {Query} queryObj | ||
@@ -330,30 +320,13 @@ * @returns {Promise} | ||
p.detect = function (queryObj) { | ||
var that = this | ||
return new Promise(function (resolve, reject) { | ||
var from = queryObj.from | ||
if (from) { | ||
if (langResolve(from)) { | ||
resolve(from) | ||
} else { | ||
reject(null) | ||
return superagent | ||
.post(this.link + '/langdetect') | ||
.send('query=' + queryObj.text.slice(0, 73)) | ||
.then(function (res) { | ||
var body = res.body | ||
if (body.error === 0) { | ||
var lang = langResolve(body.lan, true) | ||
if (lang) return lang | ||
} | ||
return | ||
} | ||
superagent | ||
.post(that.link + '/langdetect') | ||
.send('query=' + queryObj.text.slice(0, 73)) | ||
.end(function (err, res) { | ||
if (err) return reject(err) | ||
var body = res.body | ||
if (body.error === 0) { | ||
var lang = langResolve(body.lan, true) | ||
if (lang) return resolve(lang) | ||
} | ||
reject(null) | ||
}) | ||
}) | ||
return null | ||
}) | ||
} | ||
@@ -367,5 +340,16 @@ | ||
p.audio = function (queryObj) { | ||
return this | ||
.detect(queryObj) | ||
.then(function (lang) { return 'http://fanyi.baidu.com/gettts?lan=' + langResolve(lang) + '&text=' + queryObj.text + '&spd=2&source=web' }) | ||
var langPromise | ||
if (queryObj.from) { | ||
langPromise = Promise.resolve(langResolve(queryObj.from)) | ||
} else { | ||
langPromise = this.detect(queryObj) | ||
} | ||
return langPromise | ||
.then(function (lang) { | ||
if (!lang) return null | ||
var l = langResolve(lang) | ||
return l | ||
? this.link + '/gettts?lan=' + l + '&text=' + queryObj.text + '&spd=2&source=web' | ||
: null | ||
}) | ||
} | ||
@@ -432,4 +416,14 @@ | ||
YouDao.resolve = langTransform | ||
var TRANSLATE_PATH = '/openapi.do' | ||
var ERROR = { | ||
20: '有道翻译服务一次性只能翻译200个字符', | ||
30: '有道翻译暂时无法翻译这段文本', | ||
40: '有道翻译不支持这种语言', | ||
50: 'api key被封禁', | ||
60: '无词典结果' | ||
} | ||
YouDao.ERROR = ERROR | ||
YouDao.TRANSLATE_PATH = TRANSLATE_PATH | ||
/** | ||
@@ -448,13 +442,7 @@ * 有道翻译构造函数 | ||
this.keyFrom = config.keyFrom | ||
this.apiRoot = 'https://fanyi.youdao.com' | ||
this.name = '有道翻译' | ||
this.link = 'http://fanyi.youdao.com/' | ||
this.link = 'http://fanyi.youdao.com' | ||
this.type = 'YouDao' | ||
this.errMsg = { | ||
20: '有道翻译服务一次性只能翻译200个字符', | ||
30: '有道翻译暂时无法翻译这段文本', | ||
40: '有道翻译不支持这种语言', | ||
50: 'api key被封禁', | ||
60: '无词典结果' | ||
} | ||
} | ||
@@ -471,21 +459,15 @@ | ||
var that = this | ||
return new Promise(function (resolve, reject) { | ||
request | ||
.get('https://fanyi.youdao.com/openapi.do') | ||
.query({ | ||
key: that.apiKey, | ||
keyfrom: that.keyFrom, | ||
type: 'data', | ||
doctype: 'json', | ||
version: '1.1', | ||
q: queryObj.text | ||
}) | ||
.end(function (err, res) { | ||
if (err) { | ||
reject(err) | ||
} else { | ||
resolve(that.transform(res.body, queryObj)) | ||
} | ||
}) | ||
}) | ||
return request | ||
.get(this.apiRoot + TRANSLATE_PATH) | ||
.query({ | ||
key: that.apiKey, | ||
keyfrom: that.keyFrom, | ||
type: 'data', | ||
doctype: 'json', | ||
version: '1.1', | ||
q: queryObj.text | ||
}) | ||
.then(function (res) { | ||
return that.transform(res.body, queryObj) | ||
}) | ||
} | ||
@@ -503,3 +485,3 @@ | ||
response: rawRes, | ||
linkToResult: 'http://fanyi.youdao.com/translate?i=' + queryObj.text | ||
linkToResult: this.link + '/translate?i=' + queryObj.text | ||
} | ||
@@ -511,3 +493,3 @@ | ||
if (rawRes.errorCode !== 0) { | ||
obj.error = this.errMsg[rawRes.errorCode] | ||
obj.error = ERROR[rawRes.errorCode] | ||
} else { | ||
@@ -536,19 +518,2 @@ // 详细释义 | ||
/** | ||
* 检测语种的方法,有道没有,所以若没有提供源语种就总是返回 null | ||
* @param {Query} queryObj | ||
* @returns {Promise} | ||
*/ | ||
p.detect = function (queryObj) { | ||
return new Promise(function (resolve, reject) { | ||
var from = queryObj.from | ||
if (langTransform(from)) { | ||
resolve(from) | ||
} else { | ||
reject(null) | ||
} | ||
}) | ||
} | ||
/** | ||
* 返回语音播放的 url | ||
@@ -559,8 +524,5 @@ * @param queryObj | ||
p.audio = function (queryObj) { | ||
return this | ||
.detect(queryObj) | ||
.then(function (lang) { | ||
var l = langTransform(lang) | ||
return 'http://tts.youdao.com/fanyivoice?keyfrom=fanyi%2Eweb%2Eindex&le=' + l + '&word=' + queryObj.text | ||
}) | ||
var lang = langTransform(queryObj.from) | ||
if (!lang) return Promise.resolve(null) | ||
return Promise.resolve('http://tts.youdao.com/fanyivoice?keyfrom=fanyi%2Eweb%2Eindex&le=' + lang + '&word=' + queryObj.text) | ||
} | ||
@@ -620,22 +582,15 @@ | ||
var that = this | ||
return new Promise(function (resolve, reject) { | ||
superagent | ||
.post('http://dict.bing.com.cn/io.aspx') | ||
.type('form') | ||
.send({ | ||
t: 'dict', | ||
ut: 'default', | ||
q: queryObj.text, | ||
ulang: 'AUTO', | ||
tlang: 'AUTO' | ||
}) | ||
.timeout(that.timeout) | ||
.end(function (err, res) { | ||
if (err) { | ||
reject(err) | ||
} else { | ||
resolve(that.transform(res.text, queryObj)) | ||
} | ||
}) | ||
}) | ||
return superagent | ||
.post('http://dict.bing.com.cn/io.aspx') | ||
.type('form') | ||
.send({ | ||
t: 'dict', | ||
ut: 'default', | ||
q: queryObj.text, | ||
ulang: 'AUTO', | ||
tlang: 'AUTO' | ||
}) | ||
.then(function (res) { | ||
return that.transform(res.text, queryObj) | ||
}) | ||
} | ||
@@ -671,4 +626,3 @@ | ||
try { | ||
var d = [] | ||
ROOT.DEF[0].SENS.forEach(function (v) { | ||
obj.detailed = ROOT.DEF[0].SENS.map(function (v) { | ||
var s = v.$POS + '. ' | ||
@@ -682,5 +636,4 @@ if (Array.isArray(v.SEN)) { | ||
} | ||
d.push(s) | ||
return s | ||
}) | ||
obj.detailed = d | ||
} catch (e) {} | ||
@@ -700,31 +653,30 @@ | ||
/** | ||
* 使用必应翻译检测文本语种。 | ||
* @param queryObj | ||
* @returns {Promise} | ||
*/ | ||
p.detect = function (queryObj) { | ||
return new Promise(function (resolve, reject) { | ||
var from = queryObj.from | ||
if (langTransform(from)) { | ||
resolve(from) | ||
} else { | ||
reject(null) | ||
} | ||
}) | ||
} | ||
module.exports = Bing | ||
/** | ||
* 暂时找不到必应的语音播放的接口。它网页上的语音播放没有规律可循。 | ||
* @returns {Promise} | ||
*/ | ||
p.audio = function () { | ||
return Promise.reject(null) | ||
/***/ }, | ||
/* 6 */ | ||
/***/ function(module, exports, __webpack_require__) { | ||
var _Google = __webpack_require__(7) | ||
function Google () { | ||
_Google.call(this) | ||
this.name = '谷歌翻译' | ||
this.type = 'Google' | ||
this.apiRoot = this.link = 'https://translate.google.com' | ||
// 若想在浏览器中使用谷歌翻译, | ||
// 可以将 this.apiRoot 设为 'https://translate.googleapis.com' | ||
// 这个接口是可跨域访问的 | ||
} | ||
module.exports = Bing | ||
Google.prototype = Object.create(_Google.prototype, { | ||
constructor: { value: Google } | ||
}) | ||
module.exports = Google | ||
/***/ }, | ||
/* 6 */ | ||
/* 7 */ | ||
/***/ function(module, exports, __webpack_require__) { | ||
@@ -736,15 +688,39 @@ | ||
var request = __webpack_require__(2) | ||
var isEmpty = __webpack_require__(8) | ||
var TRANSLATE_PATH = '/translate_a/single' | ||
var AUDIO_PATH = '/translate_tts' | ||
var ERROR = { | ||
UNKNOWN: '发生了一个错误,可能是因为查询文本过长或请求频率太高造成的。', | ||
NO_RESULT: '没有返回翻译结果,请稍后重试。' | ||
} | ||
_Google.TRANSLATE_PATH = TRANSLATE_PATH | ||
_Google.AUDIO_PATH = AUDIO_PATH | ||
_Google.ERROR = ERROR | ||
/** | ||
* 谷歌翻译 | ||
* 谷歌翻译的底层构造函数 | ||
*/ | ||
function Google () { | ||
this.name = '谷歌翻译' | ||
this.type = 'Google' | ||
this.link = 'https://translate.google.com' | ||
this.apiRoot = 'https://translate.googleapis.com' | ||
function _Google () { | ||
// 继承的构造函数需要提供下面的属性 | ||
// this.name | ||
// this.type | ||
// this.link | ||
// this.apiRoot | ||
} | ||
var p = Google.prototype | ||
// ISO839-1 Code from https://cloud.google.com/translate/docs/languages | ||
var supportedLang = ['af', 'sq', 'am', 'ar', 'hy', 'az', 'eu', 'be', 'bn', 'bs', | ||
'bg', 'ca', 'ceb', 'ny', 'zh-CN', 'zh-TW', 'co', 'hr', 'cs', 'da', 'nl', 'en', | ||
'eo', 'et', 'tl', 'fi', 'fr', 'fy', 'gl', 'ka', 'de', 'el', 'gu', 'ht', 'ha', | ||
'haw', 'iw', 'hi', 'hmn', 'hu', 'is', 'ig', 'id', 'ga', 'it', 'ja', 'jw', 'kn', | ||
'kk', 'km', 'ko', 'ku', 'ky', 'lo', 'la', 'lv', 'lt', 'lb', 'mk', 'mg', 'ms', | ||
'ml', 'mt', 'mi', 'mr', 'mn', 'my', 'ne', 'no', 'ps', 'fa', 'pl', 'pt', 'ma', | ||
'ro', 'ru', 'sm', 'gd', 'sr', 'st', 'sn', 'sd', 'si', 'sk', 'sl', 'so', 'es', | ||
'su', 'sw', 'sv', 'tg', 'ta', 'te', 'th', 'tr', 'uk', 'ur', 'uz', 'vi', 'cy', | ||
'xh', 'yi', 'yo', 'zu'] | ||
var p = _Google.prototype | ||
/** | ||
@@ -756,31 +732,53 @@ * 翻译的方法 | ||
p.translate = function (queryObj) { | ||
var acceptLanguage = queryObj.to | ||
? queryObj.to + (queryObj.to.indexOf('-') > -1 ? ',' + queryObj.to.split('-')[0] : '') | ||
: 'en' | ||
acceptLanguage += ';q=0.8,en;q=0.6' | ||
var that = this | ||
return new Promise(function (resolve, reject) { | ||
request | ||
.get(this.apiRoot + '/translate_a/single') | ||
.query('client=gtx&sl=' + (queryObj.from || 'auto') + '&tl=' + (queryObj.to || 'auto') + '&hl=zh-CN&dt=t&dt=bd&ie=UTF-8&oe=UTF-8&dj=1&source=icon&q=' + encodeURI(queryObj.text)) | ||
// { | ||
// client : 'gtx' , | ||
// sl : 'auto' , // 源语言 | ||
// tl : queryObj.to || 'auto' , // 目标语言 | ||
// hl : 'zh-CN' , | ||
// dt : [ 't' , 'bd' ] , // 这个地方必须写成 &dt=t&dt=tl,所以没有用对象的方式声明 | ||
// dj : 1 , | ||
// source : 'icon' , | ||
// q : queryObj.text | ||
// } ) | ||
.timeout(that.timeout) | ||
.end(function (err, res) { | ||
if (err) { | ||
reject(err) | ||
} else { | ||
resolve(that.transform(res.body, queryObj)) | ||
} | ||
}) | ||
}) | ||
return request | ||
.get(this.apiRoot + TRANSLATE_PATH) | ||
.set('Accept-Language', acceptLanguage) // it affects dict language | ||
.query({ | ||
// for detect language, just {client: 'gtx', sl: auto, dj: 1, ie: 'UTF-8', oe: 'UTF-8', q: 'test'} | ||
client: 'gtx', | ||
sl: queryObj.from || 'auto', // source language | ||
tl: queryObj.to || 'auto', // translated language | ||
dj: 1, // ensure return json is GoogleRes structure | ||
ie: 'UTF-8', // input string encoding | ||
oe: 'UTF-8', // output string encoding | ||
source: 'icon', // source | ||
q: queryObj.text, // text to be translated | ||
dt: ['t', 'bd'] // a list to add content to return json | ||
// possible dt values: correspond return json key | ||
// t: sentences | ||
// rm: sentences[1] | ||
// bd: dict | ||
// at: alternative_translations | ||
// ss: synsets | ||
// rw: related_words | ||
// ex: examples | ||
// ld: ld_result | ||
}) | ||
.then(function (res) { | ||
return that.transform(res.body, queryObj) | ||
}) | ||
} | ||
/** | ||
* Google 翻译返回的数据结构 | ||
* @typedef {Object} GoogleRes | ||
* @property {String} src - 原字符串语种,ISO839-1 格式,如果 queryObj dt 为空,则返回 json 只有这一个字段 | ||
* @property {Object[]} sentences | ||
* @property {{trans: String, orig: String, backend: Number}} sentences[0] trans:翻译结果,orig:被翻译的字符串 | ||
* @property {{translit: String, src_translit: String}} sentences[1] translit:翻译结果音标,src_translit:原字符串音标 | ||
* @property {{pos: String, terms: String[], entry: Object[]}[]} dict 查词结果,只有请求单个单词翻译时会有, | ||
* 中翻英经常有,小语种经常没有 pos:词性 terms:词语列表 entry:对每个词的详解 | ||
* @property {{srclangs: String[], srclangs_confidences: Number[], extended_srclangs: String[]}} ld_result | ||
*/ | ||
/** | ||
* 将谷歌翻译的数据转换为统一格式 | ||
* @param rawRes | ||
* @param {GoogleRes} rawRes | ||
* @param queryObj | ||
@@ -793,3 +791,2 @@ * @returns {{}} | ||
to: queryObj.to || 'auto', | ||
from: rawRes.src, | ||
response: rawRes | ||
@@ -801,24 +798,27 @@ } | ||
if (typeof rawRes === 'string') { | ||
obj.error = '谷歌翻译发生了一个错误,可能是因为查询文本过长造成的。' | ||
obj.error = this.name + ERROR.UNKNOWN | ||
} else { | ||
// 尝试获取详细释义 | ||
try { | ||
var d = [] | ||
rawRes.dict.forEach(function (v) { | ||
d.push(v.pos + ':' + (v.terms.slice(0, 3) || []).join(',')) | ||
// 尝试获取详细释义 | ||
obj.detailed = rawRes.dict.map(function (v) { | ||
return v.pos + ':' + (v.terms.slice(0, 3) || []).join(',') | ||
}) | ||
obj.detailed = d | ||
} catch (e) {} | ||
// 尝试取得翻译结果 | ||
try { | ||
var result = [] | ||
rawRes.sentences.forEach(function (v) { | ||
result.push(v.trans) | ||
// 尝试取得翻译结果 | ||
var sentences = rawRes.sentences.filter(function (sentence) { | ||
return sentence.trans !== queryObj.text | ||
}) | ||
obj.result = result | ||
if (isEmpty(sentences)) { | ||
obj.from = null | ||
} else { | ||
obj.from = rawRes.src | ||
obj.result = sentences.map(function (sentence) { | ||
return sentence.trans | ||
}) | ||
} | ||
} catch (e) {} | ||
if (!obj.detailed && !obj.result) { | ||
obj.error = this.name + '没有返回翻译结果,请稍后重试。' | ||
if (isEmpty(obj.detailed) && isEmpty(obj.result)) { | ||
obj.error = this.name + ERROR.NO_RESULT | ||
} | ||
@@ -835,14 +835,11 @@ } | ||
p.detect = function (queryObj) { | ||
var that = this | ||
return new Promise(function (resolve, reject) { | ||
var from = queryObj.from | ||
var from = queryObj.from | ||
if (from) { | ||
return Promise.resolve(supportedLang.indexOf(from) > -1 ? from : null) | ||
} | ||
if (from) { | ||
return resolve(from) | ||
} | ||
that | ||
.translate(queryObj) | ||
.then(function (result) { return resolve(result.from) }, reject) | ||
}) | ||
return this.translate(queryObj) | ||
.then(function (result) { | ||
return Promise.resolve(result.from || null) | ||
}) | ||
} | ||
@@ -859,25 +856,49 @@ | ||
.then(function (lang) { | ||
return encodeURI(that.apiRoot + '/translate_tts?ie=UTF-8&q=' + queryObj.text + '&tl=' + lang + '&client=gtx') | ||
return supportedLang.indexOf(lang) > -1 | ||
? that.link + AUDIO_PATH + '?ie=UTF-8&q=' + encodeURIComponent(queryObj.text) + '&tl=' + lang + '&client=gtx' | ||
: null | ||
}) | ||
} | ||
module.exports = Google | ||
module.exports = _Google | ||
/***/ }, | ||
/* 7 */ | ||
/* 8 */ | ||
/***/ function(module, exports) { | ||
/** | ||
* This things are empty | ||
* 1. undefined, null, void 0, [], {}, | ||
* 2. object with .length === 0, | ||
* 3. object without a own enumerable property | ||
* @param {Object} obj | ||
* @return Boolean | ||
*/ | ||
module.exports = function isEmpty (obj) { | ||
if (obj == null) return true | ||
if (obj.length !== undefined) return obj.length === 0 | ||
return Object.keys(obj).length === 0 | ||
} | ||
/***/ }, | ||
/* 9 */ | ||
/***/ function(module, exports, __webpack_require__) { | ||
var Google = __webpack_require__(6) | ||
var _Google = __webpack_require__(7) | ||
function GoogleCN (config) { | ||
var google = new Google(config) | ||
google.name = '谷歌翻译(国内)' | ||
google.type = 'GoogleCN' | ||
google.link = 'https://translate.google.cn' | ||
google.apiRoot = 'https://translate.google.cn' | ||
return google | ||
function GoogleCN () { | ||
_Google.call(this) | ||
this.name = '谷歌翻译(国内)' | ||
this.type = 'GoogleCN' | ||
this.apiRoot = this.link = 'https://translate.google.cn' | ||
} | ||
GoogleCN.prototype = Object.create(_Google.prototype, { | ||
constructor: { value: GoogleCN } | ||
}) | ||
module.exports = GoogleCN | ||
@@ -884,0 +905,0 @@ |
@@ -47,3 +47,3 @@ /** | ||
this.type = 'BaiDu' | ||
this.link = 'http://fanyi.baidu.com/' | ||
this.link = 'http://fanyi.baidu.com' | ||
} | ||
@@ -70,20 +70,14 @@ | ||
var that = this | ||
return new Promise(function (resolve, reject) { | ||
superagent | ||
.get(that.link + '/v2transapi') | ||
.query({ | ||
from: standard2custom[queryObj.from] || 'auto', | ||
to: standard2custom[queryObj.to] || 'zh', // 非标准接口一定要提供目标语种 | ||
query: queryObj.text, | ||
transtype: 'hash', | ||
simple_means_flag: 3 | ||
}) | ||
.end(function (err, res) { | ||
if (err) { | ||
reject(err) | ||
} else { | ||
resolve(that.transform(res.body, queryObj)) | ||
} | ||
}) | ||
}) | ||
return superagent | ||
.get(this.link + '/v2transapi') | ||
.query({ | ||
from: standard2custom[queryObj.from] || 'auto', | ||
to: standard2custom[queryObj.to] || 'zh', // 非标准接口一定要提供目标语种 | ||
query: queryObj.text, | ||
transtype: 'hash', | ||
simple_means_flag: 3 | ||
}) | ||
.then(function (res) { | ||
return that.transform(res.body, queryObj) | ||
}) | ||
} | ||
@@ -122,7 +116,5 @@ | ||
try { | ||
var detailed = [] | ||
rawRes.dict_result.simple_means.symbols[0].parts.forEach(function (v) { | ||
detailed.push(v.part + ' ' + v.means.join(',')) | ||
obj.detailed = rawRes.dict_result.simple_means.symbols[0].parts.map(function (v) { | ||
return v.part + ' ' + v.means.join(',') | ||
}) | ||
obj.detailed = detailed | ||
} catch (e) {} | ||
@@ -145,3 +137,3 @@ | ||
/** | ||
* 检测语种的方法, 返回的语种为百度自己格式的语种。 | ||
* 检测语种的方法 | ||
* @param {Query} queryObj | ||
@@ -151,30 +143,13 @@ * @returns {Promise} | ||
p.detect = function (queryObj) { | ||
var that = this | ||
return new Promise(function (resolve, reject) { | ||
var from = queryObj.from | ||
if (from) { | ||
if (langResolve(from)) { | ||
resolve(from) | ||
} else { | ||
reject(null) | ||
return superagent | ||
.post(this.link + '/langdetect') | ||
.send('query=' + queryObj.text.slice(0, 73)) | ||
.then(function (res) { | ||
var body = res.body | ||
if (body.error === 0) { | ||
var lang = langResolve(body.lan, true) | ||
if (lang) return lang | ||
} | ||
return | ||
} | ||
superagent | ||
.post(that.link + '/langdetect') | ||
.send('query=' + queryObj.text.slice(0, 73)) | ||
.end(function (err, res) { | ||
if (err) return reject(err) | ||
var body = res.body | ||
if (body.error === 0) { | ||
var lang = langResolve(body.lan, true) | ||
if (lang) return resolve(lang) | ||
} | ||
reject(null) | ||
}) | ||
}) | ||
return null | ||
}) | ||
} | ||
@@ -188,7 +163,18 @@ | ||
p.audio = function (queryObj) { | ||
return this | ||
.detect(queryObj) | ||
.then(function (lang) { return 'http://fanyi.baidu.com/gettts?lan=' + langResolve(lang) + '&text=' + queryObj.text + '&spd=2&source=web' }) | ||
var langPromise | ||
if (queryObj.from) { | ||
langPromise = Promise.resolve(langResolve(queryObj.from)) | ||
} else { | ||
langPromise = this.detect(queryObj) | ||
} | ||
return langPromise | ||
.then(function (lang) { | ||
if (!lang) return null | ||
var l = langResolve(lang) | ||
return l | ||
? this.link + '/gettts?lan=' + l + '&text=' + queryObj.text + '&spd=2&source=web' | ||
: null | ||
}) | ||
} | ||
module.exports = BaiDu |
@@ -46,22 +46,15 @@ /** | ||
var that = this | ||
return new Promise(function (resolve, reject) { | ||
superagent | ||
.post('http://dict.bing.com.cn/io.aspx') | ||
.type('form') | ||
.send({ | ||
t: 'dict', | ||
ut: 'default', | ||
q: queryObj.text, | ||
ulang: 'AUTO', | ||
tlang: 'AUTO' | ||
}) | ||
.timeout(that.timeout) | ||
.end(function (err, res) { | ||
if (err) { | ||
reject(err) | ||
} else { | ||
resolve(that.transform(res.text, queryObj)) | ||
} | ||
}) | ||
}) | ||
return superagent | ||
.post('http://dict.bing.com.cn/io.aspx') | ||
.type('form') | ||
.send({ | ||
t: 'dict', | ||
ut: 'default', | ||
q: queryObj.text, | ||
ulang: 'AUTO', | ||
tlang: 'AUTO' | ||
}) | ||
.then(function (res) { | ||
return that.transform(res.text, queryObj) | ||
}) | ||
} | ||
@@ -97,4 +90,3 @@ | ||
try { | ||
var d = [] | ||
ROOT.DEF[0].SENS.forEach(function (v) { | ||
obj.detailed = ROOT.DEF[0].SENS.map(function (v) { | ||
var s = v.$POS + '. ' | ||
@@ -108,5 +100,4 @@ if (Array.isArray(v.SEN)) { | ||
} | ||
d.push(s) | ||
return s | ||
}) | ||
obj.detailed = d | ||
} catch (e) {} | ||
@@ -126,26 +117,2 @@ | ||
/** | ||
* 使用必应翻译检测文本语种。 | ||
* @param queryObj | ||
* @returns {Promise} | ||
*/ | ||
p.detect = function (queryObj) { | ||
return new Promise(function (resolve, reject) { | ||
var from = queryObj.from | ||
if (langTransform(from)) { | ||
resolve(from) | ||
} else { | ||
reject(null) | ||
} | ||
}) | ||
} | ||
/** | ||
* 暂时找不到必应的语音播放的接口。它网页上的语音播放没有规律可循。 | ||
* @returns {Promise} | ||
*/ | ||
p.audio = function () { | ||
return Promise.reject(null) | ||
} | ||
module.exports = Bing |
@@ -1,12 +0,14 @@ | ||
var Google = require('./google') | ||
var _Google = require('./_google') | ||
function GoogleCN (config) { | ||
var google = new Google(config) | ||
google.name = '谷歌翻译(国内)' | ||
google.type = 'GoogleCN' | ||
google.link = 'https://translate.google.cn' | ||
google.apiRoot = 'https://translate.google.cn' | ||
return google | ||
function GoogleCN () { | ||
_Google.call(this) | ||
this.name = '谷歌翻译(国内)' | ||
this.type = 'GoogleCN' | ||
this.apiRoot = this.link = 'https://translate.google.cn' | ||
} | ||
GoogleCN.prototype = Object.create(_Google.prototype, { | ||
constructor: { value: GoogleCN } | ||
}) | ||
module.exports = GoogleCN |
@@ -1,128 +0,17 @@ | ||
/** | ||
* 谷歌翻译支持几乎所有语种,并且它的语种格式就是标准的。 | ||
*/ | ||
var request = require('superagent') | ||
var _Google = require('./_google') | ||
/** | ||
* 谷歌翻译 | ||
*/ | ||
function Google () { | ||
_Google.call(this) | ||
this.name = '谷歌翻译' | ||
this.type = 'Google' | ||
this.link = 'https://translate.google.com' | ||
this.apiRoot = 'https://translate.googleapis.com' | ||
this.apiRoot = this.link = 'https://translate.google.com' | ||
// 若想在浏览器中使用谷歌翻译, | ||
// 可以将 this.apiRoot 设为 'https://translate.googleapis.com' | ||
// 这个接口是可跨域访问的 | ||
} | ||
var p = Google.prototype | ||
Google.prototype = Object.create(_Google.prototype, { | ||
constructor: { value: Google } | ||
}) | ||
/** | ||
* 翻译的方法 | ||
* @param queryObj | ||
* @returns {Promise} | ||
*/ | ||
p.translate = function (queryObj) { | ||
var that = this | ||
return new Promise(function (resolve, reject) { | ||
request | ||
.get(this.apiRoot + '/translate_a/single') | ||
.query('client=gtx&sl=' + (queryObj.from || 'auto') + '&tl=' + (queryObj.to || 'auto') + '&hl=zh-CN&dt=t&dt=bd&ie=UTF-8&oe=UTF-8&dj=1&source=icon&q=' + encodeURI(queryObj.text)) | ||
// { | ||
// client : 'gtx' , | ||
// sl : 'auto' , // 源语言 | ||
// tl : queryObj.to || 'auto' , // 目标语言 | ||
// hl : 'zh-CN' , | ||
// dt : [ 't' , 'bd' ] , // 这个地方必须写成 &dt=t&dt=tl,所以没有用对象的方式声明 | ||
// dj : 1 , | ||
// source : 'icon' , | ||
// q : queryObj.text | ||
// } ) | ||
.timeout(that.timeout) | ||
.end(function (err, res) { | ||
if (err) { | ||
reject(err) | ||
} else { | ||
resolve(that.transform(res.body, queryObj)) | ||
} | ||
}) | ||
}) | ||
} | ||
/** | ||
* 将谷歌翻译的数据转换为统一格式 | ||
* @param rawRes | ||
* @param queryObj | ||
* @returns {{}} | ||
*/ | ||
p.transform = function (rawRes, queryObj) { | ||
var obj = { | ||
text: queryObj.text, | ||
to: queryObj.to || 'auto', | ||
from: rawRes.src, | ||
response: rawRes | ||
} | ||
obj.linkToResult = this.link + '/#auto/' + obj.to + '/' + queryObj.text | ||
if (typeof rawRes === 'string') { | ||
obj.error = '谷歌翻译发生了一个错误,可能是因为查询文本过长造成的。' | ||
} else { | ||
// 尝试获取详细释义 | ||
try { | ||
var d = [] | ||
rawRes.dict.forEach(function (v) { | ||
d.push(v.pos + ':' + (v.terms.slice(0, 3) || []).join(',')) | ||
}) | ||
obj.detailed = d | ||
} catch (e) {} | ||
// 尝试取得翻译结果 | ||
try { | ||
var result = [] | ||
rawRes.sentences.forEach(function (v) { | ||
result.push(v.trans) | ||
}) | ||
obj.result = result | ||
} catch (e) {} | ||
if (!obj.detailed && !obj.result) { | ||
obj.error = this.name + '没有返回翻译结果,请稍后重试。' | ||
} | ||
} | ||
return obj | ||
} | ||
/** | ||
* 使用谷歌翻译检测文本语种。 | ||
* @param queryObj | ||
* @returns {Promise} | ||
*/ | ||
p.detect = function (queryObj) { | ||
var that = this | ||
return new Promise(function (resolve, reject) { | ||
var from = queryObj.from | ||
if (from) { | ||
return resolve(from) | ||
} | ||
that | ||
.translate(queryObj) | ||
.then(function (result) { return resolve(result.from) }, reject) | ||
}) | ||
} | ||
/** | ||
* 返回语音播放的 url | ||
* @param queryObj | ||
* @returns {Promise} | ||
*/ | ||
p.audio = function (queryObj) { | ||
var that = this | ||
return this.detect(queryObj) | ||
.then(function (lang) { | ||
return encodeURI(that.apiRoot + '/translate_tts?ie=UTF-8&q=' + queryObj.text + '&tl=' + lang + '&client=gtx') | ||
}) | ||
} | ||
module.exports = Google | ||
@@ -27,4 +27,14 @@ // @see http://fanyi.youdao.com/openapi?path=data-mode#bd | ||
YouDao.resolve = langTransform | ||
var TRANSLATE_PATH = '/openapi.do' | ||
var ERROR = { | ||
20: '有道翻译服务一次性只能翻译200个字符', | ||
30: '有道翻译暂时无法翻译这段文本', | ||
40: '有道翻译不支持这种语言', | ||
50: 'api key被封禁', | ||
60: '无词典结果' | ||
} | ||
YouDao.ERROR = ERROR | ||
YouDao.TRANSLATE_PATH = TRANSLATE_PATH | ||
/** | ||
@@ -43,13 +53,7 @@ * 有道翻译构造函数 | ||
this.keyFrom = config.keyFrom | ||
this.apiRoot = 'https://fanyi.youdao.com' | ||
this.name = '有道翻译' | ||
this.link = 'http://fanyi.youdao.com/' | ||
this.link = 'http://fanyi.youdao.com' | ||
this.type = 'YouDao' | ||
this.errMsg = { | ||
20: '有道翻译服务一次性只能翻译200个字符', | ||
30: '有道翻译暂时无法翻译这段文本', | ||
40: '有道翻译不支持这种语言', | ||
50: 'api key被封禁', | ||
60: '无词典结果' | ||
} | ||
} | ||
@@ -66,21 +70,15 @@ | ||
var that = this | ||
return new Promise(function (resolve, reject) { | ||
request | ||
.get('https://fanyi.youdao.com/openapi.do') | ||
.query({ | ||
key: that.apiKey, | ||
keyfrom: that.keyFrom, | ||
type: 'data', | ||
doctype: 'json', | ||
version: '1.1', | ||
q: queryObj.text | ||
}) | ||
.end(function (err, res) { | ||
if (err) { | ||
reject(err) | ||
} else { | ||
resolve(that.transform(res.body, queryObj)) | ||
} | ||
}) | ||
}) | ||
return request | ||
.get(this.apiRoot + TRANSLATE_PATH) | ||
.query({ | ||
key: that.apiKey, | ||
keyfrom: that.keyFrom, | ||
type: 'data', | ||
doctype: 'json', | ||
version: '1.1', | ||
q: queryObj.text | ||
}) | ||
.then(function (res) { | ||
return that.transform(res.body, queryObj) | ||
}) | ||
} | ||
@@ -98,3 +96,3 @@ | ||
response: rawRes, | ||
linkToResult: 'http://fanyi.youdao.com/translate?i=' + queryObj.text | ||
linkToResult: this.link + '/translate?i=' + queryObj.text | ||
} | ||
@@ -106,3 +104,3 @@ | ||
if (rawRes.errorCode !== 0) { | ||
obj.error = this.errMsg[rawRes.errorCode] | ||
obj.error = ERROR[rawRes.errorCode] | ||
} else { | ||
@@ -131,19 +129,2 @@ // 详细释义 | ||
/** | ||
* 检测语种的方法,有道没有,所以若没有提供源语种就总是返回 null | ||
* @param {Query} queryObj | ||
* @returns {Promise} | ||
*/ | ||
p.detect = function (queryObj) { | ||
return new Promise(function (resolve, reject) { | ||
var from = queryObj.from | ||
if (langTransform(from)) { | ||
resolve(from) | ||
} else { | ||
reject(null) | ||
} | ||
}) | ||
} | ||
/** | ||
* 返回语音播放的 url | ||
@@ -154,10 +135,7 @@ * @param queryObj | ||
p.audio = function (queryObj) { | ||
return this | ||
.detect(queryObj) | ||
.then(function (lang) { | ||
var l = langTransform(lang) | ||
return 'http://tts.youdao.com/fanyivoice?keyfrom=fanyi%2Eweb%2Eindex&le=' + l + '&word=' + queryObj.text | ||
}) | ||
var lang = langTransform(queryObj.from) | ||
if (!lang) return Promise.resolve(null) | ||
return Promise.resolve('http://tts.youdao.com/fanyivoice?keyfrom=fanyi%2Eweb%2Eindex&le=' + lang + '&word=' + queryObj.text) | ||
} | ||
module.exports = YouDao |
@@ -8,2 +8,4 @@ // 请求 API 接口时发生了网络错误 | ||
var UNKNOWN_ERROR = 2 | ||
function Translation () { | ||
@@ -73,23 +75,18 @@ this.APIs = {} | ||
p.call = function (method, queryObj) { | ||
var that = this | ||
return new Promise(function (resolve, reject) { | ||
var instances = that.APIs[queryObj.api] | ||
if (!instances) { | ||
return reject('没有注册 ' + queryObj.api + ' API。') | ||
} | ||
var instances = this.APIs[queryObj.api] | ||
if (!instances) return Promise.reject('没有注册 ' + queryObj.api + ' API。') | ||
var a = instances.shift() | ||
instances.push(a) | ||
a[method](queryObj).then(function (resultObj) { | ||
if (method === 'translate') { | ||
resultObj.api = a | ||
} | ||
resolve(resultObj) | ||
var a = instances[0] | ||
if (!a[method]) return Promise.reject(a.name + '不支持' + method + '方法。') | ||
instances.push(instances.shift()) | ||
return a[method](queryObj) | ||
.then(function (resultObj) { | ||
if (method === 'translate') resultObj.api = a | ||
return resultObj | ||
}, function (superAgentError) { | ||
if (superAgentError == null) { | ||
return reject() | ||
} | ||
reject(analyzeErrorType(superAgentError)) | ||
if (superAgentError == null) return Promise.reject(UNKNOWN_ERROR) | ||
return Promise.reject(analyzeErrorType(superAgentError)) | ||
}) | ||
}) | ||
} | ||
@@ -101,4 +98,5 @@ | ||
tjs.SERVER_ERROR = SERVER_ERROR | ||
tjs.UNKNOWN_ERROR = UNKNOWN_ERROR | ||
// 绑定内置构造函数 | ||
// 绑定内置的翻译接口 | ||
tjs.BaiDu = require('./APIs/baidu') | ||
@@ -105,0 +103,0 @@ tjs.YouDao = require('./APIs/youdao') |
{ | ||
"name": "translation.js", | ||
"version": "0.4.0", | ||
"version": "0.4.1", | ||
"description": "集成多种翻译引擎、提供统一的翻译接口", | ||
@@ -20,9 +20,9 @@ "main": "libs/index.js", | ||
"dependencies": { | ||
"superagent": "^2.3.0" | ||
"superagent": "^3.0.0" | ||
}, | ||
"devDependencies": { | ||
"coveralls": "2.11.14", | ||
"eslint": "3.9.1", | ||
"coveralls": "2.11.15", | ||
"eslint": "3.11.1", | ||
"eslint-config-standard": "6.2.1", | ||
"eslint-plugin-promise": "3.3.1", | ||
"eslint-plugin-promise": "3.4.0", | ||
"eslint-plugin-standard": "2.0.1", | ||
@@ -29,0 +29,0 @@ "istanbul": "0.4.5", |
@@ -6,3 +6,3 @@ # translation.js | ||
[![dependencies Status](https://img.shields.io/david/Selection-Translator/translation.js.svg?style=flat-square)](https://david-dm.org/Selection-Translator/translation.js) | ||
[![devDependencies Status](https://img.shields.io/david/dev/Selection-Translator/translation.js.svg?style=flat-square)](https://david-dm.org/Selection-Translator/translation.js#info=devDependencies) | ||
[![devDependencies Status](https://img.shields.io/david/dev/Selection-Translator/translation.js.svg?style=flat-square)](https://david-dm.org/Selection-Translator/translation.js?type=dev) | ||
[![NPM Version](https://img.shields.io/npm/v/translation.js.svg?style=flat-square)](https://www.npmjs.com/package/translation.js) | ||
@@ -59,3 +59,3 @@ | ||
tjs.add(new tjs.Bing()) | ||
tjs.add(new tjs.YouDao({ key: 'xxx', keyFrom: 'xxx' })) | ||
tjs.add(new tjs.YouDao({ apiKey: 'xxx', keyFrom: 'xxx' })) | ||
@@ -62,0 +62,0 @@ // 使用有道翻译 'test' |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
49231
13
1429
0
+ Addedasynckit@0.4.0(transitive)
+ Addeddebug@3.2.7(transitive)
+ Addedform-data@2.5.2(transitive)
+ Addedhas-proto@1.0.3(transitive)
+ Addedhas-symbols@1.0.3(transitive)
+ Addedms@2.1.3(transitive)
+ Addedsafe-buffer@5.2.1(transitive)
+ Addedsuperagent@3.8.3(transitive)
- Removedasync@1.5.2(transitive)
- Removeddebug@2.6.9(transitive)
- Removedform-data@1.0.0-rc4(transitive)
- Removedhas-proto@1.1.0(transitive)
- Removedhas-symbols@1.1.0(transitive)
- Removedms@2.0.0(transitive)
- Removedsuperagent@2.3.0(transitive)
Updatedsuperagent@^3.0.0