├── .travis.yml ├── .gitignore ├── package.json ├── LICENSE ├── index.js ├── README.md ├── languages.js └── test.js /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - '6' 4 | - '5' 5 | - '4' 6 | 7 | after_success: 8 | - npm run coverage -------------------------------------------------------------------------------- /.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 | 39 | .idea -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "google-translate-free", 3 | "version": "2.4.5", 4 | "description": "A free and unlimited API for Google Translate", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "xo && nyc ava", 8 | "coverage": "nyc report --reporter=text-lcov | coveralls && nyc report --reporter=text-lcov > coverage.lcov && codecov" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "https://github.com/sufeiweb/google-translate-free.git" 13 | }, 14 | "keywords": [ 15 | "translate", 16 | "translator", 17 | "google", 18 | "translate", 19 | "api", 20 | "free", 21 | "language" 22 | ], 23 | "author": { 24 | "name": "xerath", 25 | "email": "sufei_web@163.com", 26 | "url": "https://webapp.xerath.top" 27 | }, 28 | "license": "MIT", 29 | "dependencies": { 30 | "@vitalets/google-translate-token": "^1.1.0", 31 | "configstore": "^2.0.0", 32 | "got": "^9.6.0" 33 | }, 34 | "devDependencies": { 35 | "ava": "^0.15.2", 36 | "codecov": "^1.0.1", 37 | "coveralls": "^2.11.11", 38 | "nyc": "^7.0.0", 39 | "xo": "^0.16.0" 40 | }, 41 | "xo": { 42 | "space": 4 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Matheus Fernandes – http://matheus.top – hi@matheus.top 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. -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var querystring = require('querystring'); 2 | 3 | var got = require('got'); 4 | var token = require('@vitalets/google-translate-token'); 5 | 6 | var languages = require('./languages'); 7 | 8 | function translate(text, opts, gotopts) { 9 | opts = opts || {}; 10 | gotopts = gotopts || {}; 11 | var e; 12 | [opts['form'], opts['to']].forEach(function (lang) { 13 | if (lang && !languages.isSupported(lang)) { 14 | e = new Error(); 15 | e.code = 400; 16 | e.message = 'The language \'' + lang + '\' is not supported'; 17 | } 18 | }); 19 | if (e) { 20 | return new Promise(function (resolve, reject) { 21 | reject(e); 22 | }); 23 | } 24 | 25 | opts.from = opts.from || 'auto'; 26 | opts.to = opts.to || 'en'; 27 | opts.tld = opts.tld || 'com'; 28 | 29 | opts.from = languages.getCode(opts.from); 30 | opts.to = languages.getCode(opts.to); 31 | return token.get(text, { tld: opts.tld }).then(function (token) { 32 | var url = 'https://translate.google.' + opts.tld + '/translate_a/single'; 33 | var data = { 34 | client: opts.client || 't', 35 | sl: opts.from, 36 | tl: opts.to, 37 | hl: opts.to, 38 | dt: ['at', 'bd', 'ex', 'ld', 'md', 'qca', 'rw', 'rm', 'ss', 't'], 39 | ie: 'UTF-8', 40 | oe: 'UTF-8', 41 | otf: 1, 42 | ssel: 0, 43 | tsel: 0, 44 | kc: 7, 45 | q: text 46 | }; 47 | data[token.name] = token.value; 48 | 49 | return url + '?' + querystring.stringify(data); 50 | }).then(function (url) { 51 | return got(url, gotopts).then(function (res) { 52 | var result = { 53 | text: '', 54 | pronunciation: '', 55 | from: { 56 | language: { 57 | didYouMean: false, 58 | iso: '' 59 | }, 60 | text: { 61 | autoCorrected: false, 62 | value: '', 63 | didYouMean: false 64 | } 65 | }, 66 | raw: '' 67 | }; 68 | 69 | if (opts.raw) { 70 | result.raw = res.body; 71 | } 72 | 73 | var body = JSON.parse(res.body); 74 | body[0] && body[0].forEach(function (obj) { 75 | if (obj[0]) { 76 | result.text += obj[0]; 77 | } 78 | if (obj[2]) { 79 | result.pronunciation += obj[2]; 80 | } 81 | }); 82 | 83 | if (body[2] === body[8][0][0]) { 84 | result.from.language.iso = body[2]; 85 | } else { 86 | result.from.language.didYouMean = true; 87 | result.from.language.iso = body[8][0][0]; 88 | } 89 | 90 | if (body[7] && body[7][0]) { 91 | var str = body[7][0]; 92 | 93 | str = str.replace(//g, '['); 94 | str = str.replace(/<\/i><\/b>/g, ']'); 95 | 96 | result.from.text.value = str; 97 | 98 | if (body[7][5] === true) { 99 | result.from.text.autoCorrected = true; 100 | } else { 101 | result.from.text.didYouMean = true; 102 | } 103 | } 104 | 105 | return result; 106 | }).catch(function (err) { 107 | err.message += `\nUrl: ${url}`; 108 | if (err.statusCode !== undefined && err.statusCode !== 200) { 109 | err.code = 'BAD_REQUEST'; 110 | } else { 111 | err.code = 'BAD_NETWORK'; 112 | } 113 | throw err; 114 | }); 115 | }); 116 | } 117 | 118 | module.exports = translate; 119 | module.exports.languages = languages; 120 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | A **free** and **unlimited** API for Google Translate :dollar::no_entry_sign: 3 | 4 | ## Features 5 | 6 | - Auto language detection 7 | - Spelling correction 8 | - Language correction 9 | - Fast and reliable – it uses the same servers that [translate.google.com](https://translate.google.com) uses 10 | 11 | ## Install 12 | 13 | ``` 14 | npm install --save google-translate-free 15 | ``` 16 | 17 | ## Usage 18 | 19 | From automatic language detection to English: 20 | 21 | ``` js 22 | const translate = require('google-translate-free'); 23 | 24 | translate('Ik spreek Engels', {to: 'en' }).then(res => { 25 | console.log(res.text); 26 | //=> I speak English 27 | console.log(res.from.language.iso); 28 | //=> nl 29 | }).catch(err => { 30 | console.error(err); 31 | }); 32 | ``` 33 | 34 | From English to Dutch with a typo: 35 | 36 | ``` js 37 | translate('I spea Dutch!', {from: 'en', to: 'nl'}).then(res => { 38 | console.log(res.text); 39 | //=> Ik spreek Nederlands! 40 | console.log(res.from.text.autoCorrected); 41 | //=> true 42 | console.log(res.from.text.value); 43 | //=> I [speak] Dutch! 44 | console.log(res.from.text.didYouMean); 45 | //=> false 46 | }).catch(err => { 47 | console.error(err); 48 | }); 49 | ``` 50 | 51 | Sometimes, the API will not use the auto corrected text in the translation: 52 | 53 | ``` js 54 | translate('I spea Dutch!', {from: 'en', to: 'nl'}).then(res => { 55 | console.log(res); 56 | console.log(res.text); 57 | //=> Ik spea Nederlands! 58 | console.log(res.from.text.autoCorrected); 59 | //=> false 60 | console.log(res.from.text.value); 61 | //=> I [speak] Dutch! 62 | console.log(res.from.text.didYouMean); 63 | //=> true 64 | }).catch(err => { 65 | console.error(err); 66 | }); 67 | ``` 68 | 69 | ## API 70 | 71 | ### translate(text, options) 72 | 73 | #### text 74 | 75 | Type: `string` 76 | 77 | The text to be translated 78 | 79 | #### options 80 | 81 | Type: `object` 82 | 83 | ##### from 84 | 85 | Type: `string` Default: `auto` 86 | 87 | The `text` language. Must be `auto` or one of the codes/names (not case sensitive) contained in [languages.js](https://github.com/matheuss/google-translate-api/blob/master/languages.js) 88 | 89 | ##### to 90 | 91 | Type: `string` Default: `en` 92 | 93 | 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/matheuss/google-translate-api/blob/master/languages.js). 94 | 95 | ##### raw 96 | 97 | Type: `boolean` Default: `false` 98 | 99 | If `true`, the returned object will have a `raw` property with the raw response (`string`) from Google Translate. 100 | 101 | ### Returns an `object`: 102 | 103 | - `text` *(string)* – The translated text. 104 | - `from` *(object)* 105 | - `language` *(object)* 106 | - `didYouMean` *(boolean)* - `true` if the API suggest a correction in the source language 107 | - `iso` *(string)* - The [code of the language](https://github.com/matheuss/google-translate-free/blob/master/languages.js) that the API has recognized in the `text` 108 | - `text` *(object)* 109 | - `autoCorrected` *(boolean)* – `true` if the API has auto corrected the `text` 110 | - `value` *(string)* – The auto corrected `text` or the `text` with suggested corrections 111 | - `didYouMean` *(booelan)* – `true` if the API has suggested corrections to the `text` 112 | - `raw` *(string)* - If `options.raw` is true, the raw response from Google Translate servers. Otherwise, `''`. 113 | 114 | Note that `res.from.text` will only be returned if `from.text.autoCorrected` or `from.text.didYouMean` equals to `true`. In this case, it will have the corrections delimited with brackets (`[ ]`): 115 | 116 | ``` js 117 | translate('I spea Dutch').then(res => { 118 | console.log(res.from.text.value); 119 | //=> I [speak] Dutch 120 | }).catch(err => { 121 | console.error(err); 122 | }); 123 | ``` 124 | Otherwise, it will be an empty `string` (`''`). 125 | 126 | ## Related 127 | 128 | - [`vertaler`](https://github.com/matheuss/vertaler) – CLI for this module 129 | 130 | ## License 131 | 132 | MIT © [Matheus Fernandes](http://matheus.top) 133 | -------------------------------------------------------------------------------- /languages.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Generated from https://translate.google.com 4 | * 5 | * The languages that Google Translate supports (as of 5/15/16) alongside with their ISO 639-1 codes 6 | * See https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes 7 | */ 8 | 9 | var langs = { 10 | 'auto': 'Automatic', 11 | 'af': 'Afrikaans', 12 | 'sq': 'Albanian', 13 | 'am': 'Amharic', 14 | 'ar': 'Arabic', 15 | 'hy': 'Armenian', 16 | 'az': 'Azerbaijani', 17 | 'eu': 'Basque', 18 | 'be': 'Belarusian', 19 | 'bn': 'Bengali', 20 | 'bs': 'Bosnian', 21 | 'bg': 'Bulgarian', 22 | 'ca': 'Catalan', 23 | 'ceb': 'Cebuano', 24 | 'ny': 'Chichewa', 25 | 'zh': 'Chinese (Simplified)', 26 | 'zh-cn': 'Chinese (Simplified)', 27 | 'zh-tw': 'Chinese (Traditional)', 28 | 'co': 'Corsican', 29 | 'hr': 'Croatian', 30 | 'cs': 'Czech', 31 | 'da': 'Danish', 32 | 'nl': 'Dutch', 33 | 'en': 'English', 34 | 'eo': 'Esperanto', 35 | 'et': 'Estonian', 36 | 'tl': 'Filipino', 37 | 'fi': 'Finnish', 38 | 'fr': 'French', 39 | 'fy': 'Frisian', 40 | 'gl': 'Galician', 41 | 'ka': 'Georgian', 42 | 'de': 'German', 43 | 'el': 'Greek', 44 | 'gu': 'Gujarati', 45 | 'ht': 'Haitian Creole', 46 | 'ha': 'Hausa', 47 | 'haw': 'Hawaiian', 48 | 'he': 'Hebrew', 49 | 'iw': 'Hebrew', 50 | 'hi': 'Hindi', 51 | 'hmn': 'Hmong', 52 | 'hu': 'Hungarian', 53 | 'is': 'Icelandic', 54 | 'ig': 'Igbo', 55 | 'id': 'Indonesian', 56 | 'ga': 'Irish', 57 | 'it': 'Italian', 58 | 'ja': 'Japanese', 59 | 'jw': 'Javanese', 60 | 'kn': 'Kannada', 61 | 'kk': 'Kazakh', 62 | 'km': 'Khmer', 63 | 'ko': 'Korean', 64 | 'ku': 'Kurdish (Kurmanji)', 65 | 'ky': 'Kyrgyz', 66 | 'lo': 'Lao', 67 | 'la': 'Latin', 68 | 'lv': 'Latvian', 69 | 'lt': 'Lithuanian', 70 | 'lb': 'Luxembourgish', 71 | 'mk': 'Macedonian', 72 | 'mg': 'Malagasy', 73 | 'ms': 'Malay', 74 | 'ml': 'Malayalam', 75 | 'mt': 'Maltese', 76 | 'mi': 'Maori', 77 | 'mr': 'Marathi', 78 | 'mn': 'Mongolian', 79 | 'my': 'Myanmar (Burmese)', 80 | 'ne': 'Nepali', 81 | 'no': 'Norwegian', 82 | 'ps': 'Pashto', 83 | 'fa': 'Persian', 84 | 'pl': 'Polish', 85 | 'pt': 'Portuguese', 86 | 'pa': 'Punjabi', 87 | 'ro': 'Romanian', 88 | 'ru': 'Russian', 89 | 'sm': 'Samoan', 90 | 'gd': 'Scots Gaelic', 91 | 'sr': 'Serbian', 92 | 'st': 'Sesotho', 93 | 'sn': 'Shona', 94 | 'sd': 'Sindhi', 95 | 'si': 'Sinhala', 96 | 'sk': 'Slovak', 97 | 'sl': 'Slovenian', 98 | 'so': 'Somali', 99 | 'es': 'Spanish', 100 | 'su': 'Sundanese', 101 | 'sw': 'Swahili', 102 | 'sv': 'Swedish', 103 | 'tg': 'Tajik', 104 | 'ta': 'Tamil', 105 | 'te': 'Telugu', 106 | 'th': 'Thai', 107 | 'tr': 'Turkish', 108 | 'uk': 'Ukrainian', 109 | 'ur': 'Urdu', 110 | 'uz': 'Uzbek', 111 | 'vi': 'Vietnamese', 112 | 'cy': 'Welsh', 113 | 'xh': 'Xhosa', 114 | 'yi': 'Yiddish', 115 | 'yo': 'Yoruba', 116 | 'zu': 'Zulu' 117 | }; 118 | /** 119 | * Returns the ISO 639-1 code of the desiredLang – if it is supported by Google Translate 120 | * @param {string} desiredLang – the name or the code of the desired language 121 | * @returns {string|boolean} The ISO 639-1 code of the language or false if the language is not supported 122 | */ 123 | function getCode(desiredLang) { 124 | if (!desiredLang) { 125 | return false; 126 | } 127 | desiredLang = desiredLang.toLowerCase(); 128 | 129 | if (langs[desiredLang]) { 130 | return desiredLang; 131 | } 132 | 133 | var keys = Object.keys(langs).filter(function (key) { 134 | if (typeof langs[key] !== 'string') { 135 | return false; 136 | } 137 | 138 | return langs[key].toLowerCase() === desiredLang; 139 | }); 140 | 141 | return keys[0] || false; 142 | } 143 | 144 | /** 145 | * Returns true if the desiredLang is supported by Google Translate and false otherwise 146 | * @param desiredLang – the ISO 639-1 code or the name of the desired language 147 | * @returns {boolean} 148 | */ 149 | function isSupported(desiredLang) { 150 | return Boolean(getCode(desiredLang)); 151 | } 152 | 153 | module.exports = langs; 154 | module.exports.isSupported = isSupported; 155 | module.exports.getCode = getCode; 156 | -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | import test from 'ava'; 2 | 3 | import languages from './languages'; 4 | import translate from './index'; 5 | 6 | test('translate without any options', async t => { 7 | try { 8 | const res = await translate('vertaler'); 9 | 10 | t.is(res.text, 'translator'); 11 | t.false(res.from.language.didYouMean); 12 | t.is(res.from.language.iso, 'nl'); 13 | t.false(res.from.text.autoCorrected); 14 | t.is(res.from.text.value, ''); 15 | t.false(res.from.text.didYouMean); 16 | } catch (err) { 17 | t.fail(err.code); 18 | } 19 | }); 20 | 21 | test('translate from auto to dutch', async t => { 22 | try { 23 | const res = await translate('translator', {from: 'auto', to: 'nl'}); 24 | 25 | t.is(res.text, 'vertaler'); 26 | t.false(res.from.language.didYouMean); 27 | t.is(res.from.language.iso, 'en'); 28 | t.false(res.from.text.autoCorrected); 29 | t.is(res.from.text.value, ''); 30 | t.false(res.from.text.didYouMean); 31 | } catch (err) { 32 | t.fail(err.code); 33 | } 34 | }); 35 | 36 | test('translate some english text setting the source language as portuguese', async t => { 37 | try { 38 | const res = await translate('translator', {from: 'pt', to: 'nl'}); 39 | 40 | t.true(res.from.language.didYouMean); 41 | t.is(res.from.language.iso, 'en'); 42 | } catch (err) { 43 | t.fail(err.code); 44 | } 45 | }); 46 | 47 | test('translate some misspelled english text to dutch', async t => { 48 | try { 49 | const res = await translate('I spea Dutch', {from: 'en', to: 'nl'}); 50 | 51 | if (res.from.text.autoCorrected || res.from.text.didYouMean) { 52 | t.is(res.from.text.value, 'I [speak] Dutch'); 53 | } else { 54 | t.fail(); 55 | } 56 | } catch (err) { 57 | t.fail(err.code); 58 | } 59 | }); 60 | 61 | test.todo('try to translate some text without an internet connection'); 62 | 63 | test('translate some text and get the raw output alongside', async t => { 64 | try { 65 | const res = await translate('vertaler', {raw: true}); 66 | t.truthy(res.raw); 67 | } catch (err) { 68 | t.fail(err.code); 69 | } 70 | }); 71 | 72 | test('test a supported language – by code', t => { 73 | t.true(languages.isSupported('en')); 74 | }); 75 | 76 | test('test an unsupported language – by code', t => { 77 | t.false(languages.isSupported('js')); 78 | }); 79 | 80 | test('test a supported language – by name', t => { 81 | t.true(languages.isSupported('english')); 82 | }); 83 | 84 | test('test an unsupported language – by name', t => { 85 | t.false(languages.isSupported('javascript')); 86 | }); 87 | 88 | test('get a language code by its name', t => { 89 | t.is(languages.getCode('english'), 'en'); 90 | }); 91 | 92 | test('get an unsupported language code by its name', t => { 93 | t.false(languages.getCode('javascript')); 94 | }); 95 | 96 | test('get a supported language code by code', t => { 97 | t.is(languages.getCode('en'), 'en'); 98 | }); 99 | 100 | test('call getCode with \'undefined\'', t => { 101 | t.is(languages.getCode(undefined), false); 102 | }); 103 | 104 | test('call getCode with \'null\'', t => { 105 | t.is(languages.getCode(null), false); 106 | }); 107 | 108 | test('call getCode with an empty string', t => { 109 | t.is(languages.getCode(''), false); 110 | }); 111 | 112 | test('call getCode with no arguments', t => { 113 | t.is(languages.getCode(), false); 114 | }); 115 | 116 | test('try to translate from an unsupported language', async t => { 117 | try { 118 | await translate('something', {from: 'js', to: 'en'}); 119 | t.fail(); 120 | } catch (err) { 121 | t.is(err.code, 400); 122 | t.is(err.message, 'The language \'js\' is not supported'); 123 | } 124 | }); 125 | 126 | test('try to translate to an unsupported language', async t => { 127 | try { 128 | await translate('something', {from: 'en', to: 'js'}); 129 | t.fail(); 130 | } catch (err) { 131 | t.is(err.code, 400); 132 | t.is(err.message, 'The language \'js\' is not supported'); 133 | } 134 | }); 135 | 136 | test('translate from dutch to english using language names instead of codes', async t => { 137 | try { 138 | const res = await translate('iets', {from: 'dutch', to: 'english'}); 139 | t.is(res.from.language.iso, 'nl'); 140 | t.is(res.text, 'something'); 141 | } catch (err) { 142 | t.fail(err.code); 143 | } 144 | }); 145 | --------------------------------------------------------------------------------