├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── package.json ├── test.js ├── token.js ├── README.md ├── languages.js └── index.js /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Runtime data 7 | pids 8 | *.pid 9 | *.seed 10 | 11 | # Directory for instrumented libs generated by jscoverage/JSCover 12 | lib-cov 13 | 14 | # Coverage directory used by tools like istanbul 15 | coverage 16 | 17 | # nyc test coverage 18 | .nyc_output 19 | 20 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 21 | .grunt 22 | 23 | # node-waf configuration 24 | .lock-wscript 25 | 26 | # Compiled binary addons (http://nodejs.org/api/addons.html) 27 | build/Release 28 | 29 | # Dependency directories 30 | node_modules 31 | jspm_packages 32 | 33 | # Optional npm cache directory 34 | .npm 35 | 36 | # Optional REPL history 37 | .node_repl_history 38 | package-lock.json 39 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | ## 1.4.3 [2020-08-10] 4 | ### Fixed 5 | - Clone a new input so that it will not affect the original variable 6 | 7 | ## 1.4.2 [2020-07-28] 8 | ### Changed 9 | - Changed url params processing method 10 | 11 | ## 1.4.1 [2020-07-27] 12 | ### Changed 13 | - Change the submission module of post 14 | 15 | ## 1.4.0 [2020-07-16] 16 | ### Fixed 17 | - Fixed Lots of translations bug 18 | 19 | ## 1.3.4 [2019-01-15] 20 | ### Added 21 | - Added object translation exclusions 22 | - Remove the duplicate part of the translation 23 | ### Fixed 24 | - Fixed Object bug 25 | 26 | ## 1.3.4 [2019-01-12] 27 | ### Fixed 28 | - Translate a large number of characters using POST method 29 | 30 | ## 1.3.3 [2019-01-10] 31 | ### Added 32 | - Object mode translation: Excluding non-words that do not participate in translation 33 | 34 | ## 1.3.2 [2019-01-10] 35 | ### Added 36 | - Object mode translation: Exclude numbers, URLs, keywords in translate 37 | 38 | ## 1.3.1 [2019-01-09] 39 | ### Fixed 40 | - Fixed some bug 41 | 42 | ## 1.3.0 [2019-01-09] 43 | ### Changed 44 | - Perfect support object 45 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 shikar 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "translate-google", 3 | "version": "1.4.3", 4 | "description": "A free and unlimited for Google Translate, Multi-translation support object input", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "nyc ava" 8 | }, 9 | "keywords": [ 10 | "translate", 11 | "google", 12 | "translator", 13 | "free", 14 | "unlimited", 15 | "language", 16 | "batch", 17 | "multi", 18 | "object", 19 | "token" 20 | ], 21 | "author": { 22 | "name": "shikar", 23 | "email": "qzh.shi@gmail.com" 24 | }, 25 | "repository": { 26 | "type": "git", 27 | "url": "https://github.com/shikar/NODE_GOOGLE_TRANSLATE.git" 28 | }, 29 | "bugs": { 30 | "url": "https://github.com/shikar/NODE_GOOGLE_TRANSLATE/issues" 31 | }, 32 | "homepage": "https://github.com/shikar/NODE_GOOGLE_TRANSLATE#readme", 33 | "license": "MIT", 34 | "dependencies": { 35 | "configstore": "^5.0.1", 36 | "got": "^11.5.1", 37 | "is-keyword-js": "^1.0.3", 38 | "is-url": "^1.2.4", 39 | "lodash": "^4.17.19", 40 | "num-or-not": "^1.0.1", 41 | "safe-eval": "^0.4.1", 42 | "user-agents": "^1.0.559" 43 | }, 44 | "devDependencies": { 45 | "ava": "^3.10.1", 46 | "nyc": "^15.1.0" 47 | }, 48 | "xo": { 49 | "space": 2, 50 | "semicolon": false, 51 | "ignore": [ 52 | "token.js" 53 | ] 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | const test = require('ava') 2 | const languages = require('./languages') 3 | const translate = require('./index') 4 | 5 | test('translate with string', async t => { 6 | try { 7 | const res = await translate('你好') 8 | t.is(res, 'Hello there') 9 | } catch (err) { 10 | t.log(err) 11 | t.fail() 12 | } 13 | }) 14 | 15 | test('translate with array', async t => { 16 | try { 17 | const res = await translate(['你好', '你好']) 18 | t.is(res[0], 'Hello there') 19 | t.is(res[1], 'Hello there') 20 | } catch (err) { 21 | t.log(err) 22 | t.fail() 23 | } 24 | }) 25 | 26 | test('translate with object', async t => { 27 | try { 28 | const res = await translate({a: '你好', b: '你好'}) 29 | t.is(res.a, 'Hello there') 30 | t.is(res.b, 'Hello there') 31 | } catch (err) { 32 | t.log(err) 33 | t.fail() 34 | } 35 | }) 36 | 37 | test('translate with full object', async t => { 38 | try { 39 | const obj = { a: 1, b: '1', c: "How are you?\nI'm nice.", d: [true, 'true', 'hi', { a: 'hello', b: ['world']}] } 40 | const res = await translate(obj, {from: 'en', to: 'zh-cn'}) 41 | t.is(res.a, 1) 42 | t.is(res.b, '1') 43 | t.is(res.c, '你好吗?\n我很好。') 44 | t.is(res.d[0], true) 45 | t.is(res.d[1], 'true') 46 | t.is(res.d[2], '嗨') 47 | t.is(res.d[3].a, '你好') 48 | t.is(res.d[3].b[0], '世界') 49 | } catch (err) { 50 | t.log(err) 51 | t.fail() 52 | } 53 | }) 54 | 55 | test('translate with array and object', async t => { 56 | try { 57 | const res = await translate({a: '你好', b: ['你好', '你好']}) 58 | t.is(res.a, 'Hello there') 59 | t.is(res.b[0], 'Hello there') 60 | t.is(res.b[1], 'Hello there') 61 | } catch (err) { 62 | t.log(err) 63 | t.fail() 64 | } 65 | }) 66 | 67 | test('translate with \\n', async t => { 68 | try { 69 | const res = await translate({a: '你\n好', b: ['你\n好', '你好']}) 70 | t.is(res.a, 'you\nit is good') 71 | t.is(res.b[0], 'you\nit is good') 72 | t.is(res.b[1], 'Hello there') 73 | } catch (err) { 74 | t.log(err) 75 | t.fail() 76 | } 77 | }) 78 | 79 | test('translate with except', async t => { 80 | try { 81 | const res = await translate({a: '你\n好', b: ['你\n好', '你好']}, {except:['a']}) 82 | t.is(res.a, '你\n好') 83 | t.is(res.b[0], 'you\nit is good') 84 | t.is(res.b[1], 'Hello there') 85 | } catch (err) { 86 | t.log(err) 87 | t.fail() 88 | } 89 | }) 90 | 91 | test('translate with option', async t => { 92 | try { 93 | const res = await translate('Hello', {from: 'en', to: 'zh-cn'}) 94 | t.is(res, '你好') 95 | } catch (err) { 96 | t.log(err) 97 | t.fail() 98 | } 99 | }) 100 | 101 | test('test a supported language – by code', t => { 102 | t.true(languages.isSupported('en')) 103 | }) 104 | 105 | test('test an unsupported language – by code', t => { 106 | t.false(languages.isSupported('js')) 107 | }) 108 | -------------------------------------------------------------------------------- /token.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: shikar 3 | * @Date: 2017-02-05 13:50:16 4 | * @Last Modified by: shikar 5 | * @Last Modified time: 2019-01-16 17:30:16 6 | */ 7 | 'use strict' 8 | const got = require('got') 9 | const Configstore = require('configstore') 10 | 11 | const config = new Configstore('google-translate') 12 | const window = { 13 | TKK: config.get('TKK') || '0' 14 | } 15 | 16 | function sM(a) { 17 | var b 18 | if (null !== yr) { 19 | b = yr 20 | } else { 21 | b = wr(String.fromCharCode(84)) 22 | var c = wr(String.fromCharCode(75)) 23 | b = [b(), b()] 24 | b[1] = c() 25 | b = (yr = window[b.join(c())] || "") || "" 26 | } 27 | var d = wr(String.fromCharCode(116)) 28 | , c = wr(String.fromCharCode(107)) 29 | , d = [d(), d()] 30 | d[1] = c() 31 | c = "&" + d.join("") + "=" 32 | d = b.split(".") 33 | b = Number(d[0]) || 0 34 | for (var e = [], f = 0, g = 0; g < a.length; g++) { 35 | var l = a.charCodeAt(g) 36 | 128 > l ? e[f++] = l : (2048 > l ? e[f++] = l >> 6 | 192 : (55296 == (l & 64512) && g + 1 < a.length && 56320 == (a.charCodeAt(g + 1) & 64512) ? (l = 65536 + ((l & 1023) << 10) + (a.charCodeAt(++g) & 1023), 37 | e[f++] = l >> 18 | 240, 38 | e[f++] = l >> 12 & 63 | 128) : e[f++] = l >> 12 | 224, 39 | e[f++] = l >> 6 & 63 | 128), 40 | e[f++] = l & 63 | 128) 41 | } 42 | a = b 43 | for (f = 0; f < e.length; f++) 44 | a += e[f], 45 | a = xr(a, "+-a^+6") 46 | a = xr(a, "+-3^+b+-f") 47 | a ^= Number(d[1]) || 0 48 | 0 > a && (a = (a & 2147483647) + 2147483648) 49 | a %= 1E6 50 | return c + (a.toString() + "." + (a ^ b)) 51 | } 52 | 53 | var yr = null; 54 | var wr = function(a) { 55 | return function() { 56 | return a 57 | } 58 | } 59 | , xr = function(a, b) { 60 | for (var c = 0; c < b.length - 2; c += 3) { 61 | var d = b.charAt(c + 2) 62 | , d = "a" <= d ? d.charCodeAt(0) - 87 : Number(d) 63 | , d = "+" == b.charAt(c + 1) ? a >>> d : a << d 64 | a = "+" == b.charAt(c) ? a + d & 4294967295 : a ^ d 65 | } 66 | return a 67 | } 68 | 69 | 70 | async function updateTKK(domain) { 71 | const now = Math.floor(Date.now() / 3600000) 72 | if (Number(window.TKK.split('.')[0]) === now) { 73 | return 74 | } else { 75 | try { 76 | const res = await got(`https://${domain}`) 77 | const code = res.body.match(/tkk:\'(.*?)\'/ig) 78 | if (code) { 79 | const TKK = code[0].match(/\d+\.\d+/)[0] 80 | if (typeof TKK !== 'undefined') { 81 | config.set('TKK', TKK) 82 | } 83 | } 84 | return 85 | } catch (error) { 86 | error.code = 'BAD_NETWORK' 87 | throw error 88 | } 89 | } 90 | } 91 | 92 | async function get(text, domain = 'translate.google.cn') { 93 | try { 94 | await updateTKK(domain) 95 | const tk = sM(text).replace('&tk=', '') 96 | return {name: 'tk', value: tk} 97 | } catch (error) { 98 | throw error 99 | } 100 | } 101 | 102 | module.exports.get = get -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Translate Google 2 | free google translate 3 | 4 | ## Change Log 5 | - [changelog.md](https://github.com/shikar/NODE_GOOGLE_TRANSLATE/blob/master/CHANGELOG.md) 6 | 7 | 8 | ## Features 9 | 10 | - Auto language detection 11 | - Spelling correction 12 | - Language correction 13 | - Fast and reliable – it uses the same servers that [translate.google.com](https://translate.google.com) uses 14 | 15 | ## Install 16 | ``` 17 | npm install --save translate-google 18 | ``` 19 | 20 | ## Usage 21 | 22 | Perfect support object: 23 | 24 | ``` js 25 | const translate = require('translate-google') 26 | const tranObj = { 27 | a: 1, 28 | b: '1', 29 | c: "How are you?\nI'm nice.", 30 | d: [true, 'true', 'hi', { a: 'hello', b: ['world']}], 31 | } 32 | 33 | translate(tranObj, {to: 'zh-cn', except:['a']}).then(res => { 34 | console.log(res) 35 | }).catch(err => { 36 | console.error(err) 37 | }) 38 | 39 | // => { a: 1, b: '1', c: "你好吗?\n我很好。", d: [true, 'true', '嗨', { a: 'hello', b: ['世界']}] } 40 | ``` 41 | 42 | From automatic language detection to English: 43 | 44 | ``` js 45 | const translate = require('translate-google') 46 | 47 | translate('I speak Chinese', {to: 'zh-cn'}).then(res => { 48 | console.log(res) 49 | }).catch(err => { 50 | console.error(err) 51 | }) 52 | ``` 53 | 54 | From English to Dutch with a typo: 55 | 56 | ``` js 57 | translate('I speak Chinese!', {from: 'en', to: 'zh-cn'}).then(res => { 58 | console.log(res) 59 | }).catch(err => { 60 | console.error(err) 61 | }) 62 | ``` 63 | 64 | 65 | translate for array or object: 66 | ``` js 67 | translate({a: 'I speak Chinese!', b: ['hello', 'world']}, {from: 'en', to: 'zh-cn'}).then(res => { 68 | console.log(res) 69 | }).catch(err => { 70 | console.error(err) 71 | }) 72 | ``` 73 | 74 | ## API 75 | 76 | ### translate(text, options) 77 | 78 | #### text 79 | Type: `string`, `object`, `array` 80 | The text to be translated 81 | 82 | #### options 83 | Type: `object` 84 | 85 | ##### from 86 | Type: `string` Default: `auto` 87 | The `text` language. Must be `auto` or one of the codes/names (not case sensitive) contained in [languages.js](https://github.com/shikar/NODE_GOOGLE_TRANSLATE/blob/master/languages.js) 88 | 89 | ##### to 90 | Type: `string` Default: `en` 91 | The language in which the text should be translated. Must be one of the codes/names (not case sensitive) contained in [languages.js](https://github.com/shikar/NODE_GOOGLE_TRANSLATE/blob/master/languages.js). 92 | 93 | ##### except 94 | Type: `array` Default:`[]` 95 | Attributes in excluded objects do not participate in translation 96 | 97 | ### Returns an `object`: 98 | 99 | - `text` *(string, object, array)* – The translated text. 100 | 101 | ``` js 102 | translate(['I speak Chinese\nHello world', 'hello'], {from: 'en', to: 'nl'}).then(res => { 103 | console.log(res); 104 | //=> ["我说中文\n你好世界","你好"] 105 | }).catch(err => { 106 | console.error(err); 107 | }); 108 | ``` 109 | 110 | ## License 111 | 112 | MIT © [Shikar](qzh.shi@gmail.com) 113 | -------------------------------------------------------------------------------- /languages.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: shikar 3 | * @Date: 2017-02-05 15:29:19 4 | * @Last Modified by: shikar 5 | * @Last Modified time: 2017-02-06 13:40:25 6 | */ 7 | 'use strict' 8 | const langs = { 9 | auto: 'Automatic', 10 | af: 'Afrikaans', 11 | sq: 'Albanian', 12 | ar: 'Arabic', 13 | hy: 'Armenian', 14 | az: 'Azerbaijani', 15 | eu: 'Basque', 16 | be: 'Belarusian', 17 | bn: 'Bengali', 18 | bs: 'Bosnian', 19 | bg: 'Bulgarian', 20 | ca: 'Catalan', 21 | ceb: 'Cebuano', 22 | ny: 'Chichewa', 23 | 'zh-cn': 'Chinese Simplified', 24 | 'zh-tw': 'Chinese Traditional', 25 | co: 'Corsican', 26 | hr: 'Croatian', 27 | cs: 'Czech', 28 | da: 'Danish', 29 | nl: 'Dutch', 30 | en: 'English', 31 | eo: 'Esperanto', 32 | et: 'Estonian', 33 | tl: 'Filipino', 34 | fi: 'Finnish', 35 | fr: 'French', 36 | fy: 'Frisian', 37 | gl: 'Galician', 38 | ka: 'Georgian', 39 | de: 'German', 40 | el: 'Greek', 41 | gu: 'Gujarati', 42 | ht: 'Haitian Creole', 43 | ha: 'Hausa', 44 | haw: 'Hawaiian', 45 | iw: 'Hebrew', 46 | hi: 'Hindi', 47 | hmn: 'Hmong', 48 | hu: 'Hungarian', 49 | is: 'Icelandic', 50 | ig: 'Igbo', 51 | id: 'Indonesian', 52 | ga: 'Irish', 53 | it: 'Italian', 54 | ja: 'Japanese', 55 | jw: 'Javanese', 56 | kn: 'Kannada', 57 | kk: 'Kazakh', 58 | km: 'Khmer', 59 | ko: 'Korean', 60 | ku: 'Kurdish (Kurmanji)', 61 | ky: 'Kyrgyz', 62 | lo: 'Lao', 63 | la: 'Latin', 64 | lv: 'Latvian', 65 | lt: 'Lithuanian', 66 | lb: 'Luxembourgish', 67 | mk: 'Macedonian', 68 | mg: 'Malagasy', 69 | ms: 'Malay', 70 | ml: 'Malayalam', 71 | mt: 'Maltese', 72 | mi: 'Maori', 73 | mr: 'Marathi', 74 | mn: 'Mongolian', 75 | my: 'Myanmar (Burmese)', 76 | ne: 'Nepali', 77 | no: 'Norwegian', 78 | ps: 'Pashto', 79 | fa: 'Persian', 80 | pl: 'Polish', 81 | pt: 'Portuguese', 82 | ma: 'Punjabi', 83 | ro: 'Romanian', 84 | ru: 'Russian', 85 | sm: 'Samoan', 86 | gd: 'Scots Gaelic', 87 | sr: 'Serbian', 88 | st: 'Sesotho', 89 | sn: 'Shona', 90 | sd: 'Sindhi', 91 | si: 'Sinhala', 92 | sk: 'Slovak', 93 | sl: 'Slovenian', 94 | so: 'Somali', 95 | es: 'Spanish', 96 | su: 'Sudanese', 97 | sw: 'Swahili', 98 | sv: 'Swedish', 99 | tg: 'Tajik', 100 | ta: 'Tamil', 101 | te: 'Telugu', 102 | th: 'Thai', 103 | tr: 'Turkish', 104 | uk: 'Ukrainian', 105 | ur: 'Urdu', 106 | uz: 'Uzbek', 107 | vi: 'Vietnamese', 108 | cy: 'Welsh', 109 | xh: 'Xhosa', 110 | yi: 'Yiddish', 111 | yo: 'Yoruba', 112 | zu: 'Zulu' 113 | } 114 | 115 | function getCode(desiredLang) { 116 | if (!desiredLang) { 117 | return false 118 | } 119 | desiredLang = desiredLang.toLowerCase() 120 | if (langs[desiredLang]) { 121 | return desiredLang 122 | } 123 | var keys = Object.keys(langs).filter(key => { 124 | if (typeof langs[key] !== 'string') { 125 | return false 126 | } 127 | return langs[key].toLowerCase() === desiredLang 128 | }) 129 | return keys[0] || false 130 | } 131 | 132 | function isSupported(desiredLang) { 133 | return Boolean(getCode(desiredLang)) 134 | } 135 | 136 | module.exports = langs 137 | module.exports.isSupported = isSupported 138 | module.exports.getCode = getCode 139 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: shikar 3 | * @Date: 2017-02-05 15:28:31 4 | * @Last Modified by: shikar 5 | * @Last Modified time: 2020-07-28 17:20:29 6 | */ 7 | 'use strict' 8 | const _ = require("lodash") 9 | const isUrl = require("is-url") 10 | const isNumber = require("num-or-not") 11 | const isKeyword = require('is-keyword-js') 12 | const got = require('got') 13 | const userAgents = require("user-agents") 14 | const token = require('./token') 15 | const languages = require('./languages') 16 | 17 | function checkSame(v, maps) { 18 | for (const idx in maps) { 19 | if (maps[idx].v === v) { 20 | return idx 21 | } 22 | } 23 | return -1 24 | } 25 | 26 | function enMap(obj, except=[], path='', map=[]) { 27 | if (_.isObject(obj) == true) { 28 | _.forEach(obj, (v, k) => { 29 | const furKeyStr = _.isNumber(k) ? `[${k}]` : ( path && '.' ) + k 30 | const curPath = path + furKeyStr 31 | if (_.isObject(v) == true) { 32 | enMap(v, except, curPath, map) 33 | } else { 34 | const exceptReg = except.length > 0 ? new RegExp(`(^|\\.)(${_.map(except, _.escapeRegExp).join('|')})(\\.|\\[|$)`, 'i') : false 35 | if ( 36 | _.isString(v) && 37 | !isNumber(v) && 38 | !isUrl(v) && 39 | !isKeyword(v) && 40 | !/^(?!([a-z]+|\d+|[\?=\.\*\[\]~!@#\$%\^&\(\)_+`\/\-={}:";'<>,]+)$)[a-z\d\?=\.\*\[\]~!@#\$%\^&\(\)_+`\/\-={}:";'<>,]+$/i.test(v) && 41 | (!exceptReg || !exceptReg.test(curPath)) 42 | ) { 43 | const idx = checkSame(v, map) 44 | if (idx > -1) { 45 | map.splice(idx+1, 0, { 46 | p: curPath, 47 | v: v, 48 | i: map[idx].i, 49 | l: map[idx].l, 50 | s: true 51 | }) 52 | } else { 53 | const lastMap = _.last(map) 54 | map.push({ 55 | p: curPath, 56 | v: v, 57 | i: lastMap ? lastMap.i + lastMap.l : 0, 58 | l: v.split("\n").length, 59 | s: false 60 | }) 61 | } 62 | } 63 | } 64 | }) 65 | } else { 66 | map.push({ 67 | p: '', 68 | v: obj, 69 | i: 0, 70 | l: obj.split("\n").length 71 | }) 72 | } 73 | return map 74 | } 75 | 76 | function deMap(src, maps, dest) { 77 | if (_.isObject(src) == true) { 78 | src = _.clone(src) 79 | dest = dest.split("\n") 80 | for (const map of maps) { 81 | _.set(src, map.p, _.slice(dest, map.i, map.i+map.l).join("\n")) 82 | } 83 | } else { 84 | src = dest 85 | } 86 | return src 87 | } 88 | 89 | async function translate(input, opts = {}, domain='translate.google.cn') { 90 | const langs = [opts.from, opts.to] 91 | const except = opts.except || [] 92 | input = _.cloneDeep(input) 93 | for (const lang of langs) { 94 | if (lang && !languages.isSupported(lang)) { 95 | const e = new Error('The language \'' + lang + '\' is not supported') 96 | e.code = 400 97 | throw e 98 | } 99 | } 100 | 101 | opts.from = languages.getCode(opts.from || 'auto') 102 | opts.to = languages.getCode(opts.to || 'en') 103 | 104 | const strMap = enMap(input, except) 105 | const text = _.map(_.differenceBy(strMap, [{ s: true }], 's'), 'v').join("\n") 106 | const tokenRet = await token.get(text, domain) 107 | const url = `https://${domain}/translate_a/single` 108 | const searchParams = new URLSearchParams([ 109 | ['client', 't'], 110 | ['sl', opts.from], 111 | ['tl', opts.to], 112 | ['hl', opts.to], 113 | ['dt', 'at'], ['dt', 'bd'], ['dt', 'ex'], ['dt', 'ld'], ['dt', 'md'], ['dt', 'qca'], ['dt', 'rw'], ['dt', 'rm'], ['dt', 'ss'], ['dt', 't'], 114 | ['ie', 'UTF-8'], 115 | ['oe', 'UTF-8'], 116 | ['otf', 1], 117 | ['ssel', 0], 118 | ['tsel', 0], 119 | ['kc', 7], 120 | ['q', text], 121 | [tokenRet.name, tokenRet.value] 122 | ]) 123 | const opt = { responseType: 'json', headers: {'User-Agent': new userAgents({ deviceCategory: 'desktop' }).toString()} } 124 | if (searchParams.toString().length <= 1980) { 125 | opt.method = 'GET' 126 | } else { 127 | searchParams.delete('q') 128 | opt.method = 'POST' 129 | opt.form = { q: text } 130 | } 131 | opt.searchParams = searchParams 132 | try { 133 | const { body } = await got(url, opt) 134 | const retString = _.map(body[0], 0).join('') 135 | return deMap(input, strMap, retString) 136 | } catch (error) { 137 | let e = new Error(error.message) 138 | if (error.statusCode !== undefined && error.statusCode !== 200) { 139 | e.code = 'BAD_REQUEST' 140 | } else { 141 | e.code = 'BAD_NETWORK' 142 | } 143 | throw e 144 | } 145 | } 146 | 147 | module.exports = translate 148 | module.exports.languages = languages 149 | --------------------------------------------------------------------------------