├── .npmignore ├── types ├── util.d.ts ├── translate.d.ts ├── index.d.ts └── language.d.ts ├── .travis.yml ├── .editorconfig ├── tsconfig.json ├── src ├── util.ts ├── index.ts ├── translate.ts ├── language.ts └── token.js ├── test ├── help.ts ├── language.test.ts └── translate.test.ts ├── .gitignore ├── package.json ├── README_zh.md ├── README.md └── LICENSE /.npmignore: -------------------------------------------------------------------------------- 1 | /test 2 | /example 3 | /src -------------------------------------------------------------------------------- /types/util.d.ts: -------------------------------------------------------------------------------- 1 | export declare function arrayStringify(data: string[]): string; 2 | export declare function parseMultiple(list: Array): Array; 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | 3 | node_js: 4 | - 10 5 | 6 | script: 7 | - npm run test 8 | - npm run cover 9 | 10 | branches: 11 | only: 12 | - master -------------------------------------------------------------------------------- /types/translate.d.ts: -------------------------------------------------------------------------------- 1 | import { Options } from './index'; 2 | declare function handletranslate(data: string[], extra: Options): Promise; 3 | export default handletranslate; 4 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | 10 | [*.yml] 11 | indent_style = space 12 | indent_size = 2 -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "./dist", 4 | "target": "es5", 5 | "module": "commonjs", 6 | "declaration": true, 7 | "declarationDir": "./types" 8 | }, 9 | "include": [ 10 | "./src/**/*" 11 | ], 12 | "exclude": [ 13 | "node_modules" 14 | ] 15 | } -------------------------------------------------------------------------------- /src/util.ts: -------------------------------------------------------------------------------- 1 | export function arrayStringify(data: string[]): string { 2 | return data.map(item => `q=${encodeURIComponent(item)}`).join('&'); 3 | } 4 | 5 | export function parseMultiple(list: Array):Array { 6 | const translateMap = list.map(item => { 7 | const text = item[0][0][0]; 8 | if(text.indexOf('') > -1) { 9 | return rmHtml(text); 10 | } 11 | return text; 12 | }); 13 | return translateMap; 14 | } 15 | 16 | function rmHtml(value) { 17 | return value.match(/(.*?)<\/b>/g).map(item => item.match(/(.*)<\/b>/)[1]).join(''); 18 | } -------------------------------------------------------------------------------- /types/index.d.ts: -------------------------------------------------------------------------------- 1 | import { parseMultiple } from './util'; 2 | import { isSupport, getAllLanguage, getAllCode } from './language'; 3 | import { AxiosProxyConfig } from 'axios-https-proxy-fix'; 4 | export declare type Tld = 'cn' | 'com'; 5 | export interface Options { 6 | tld?: Tld; 7 | from?: string; 8 | to: string; 9 | proxy?: AxiosProxyConfig; 10 | config?: Object; 11 | browers?: boolean; 12 | browersUrl?: string; 13 | format?: string; 14 | isUserAgent?: boolean; 15 | userAgent?: string; 16 | client?: string; 17 | } 18 | declare function translate(value: string | string[], options: Options): Promise; 19 | export { parseMultiple, isSupport, getAllLanguage, getAllCode }; 20 | export default translate; 21 | -------------------------------------------------------------------------------- /test/help.ts: -------------------------------------------------------------------------------- 1 | import * as http from 'http'; 2 | import * as setup from 'proxy'; 3 | import * as getPort from 'get-port' 4 | 5 | class Proxy { 6 | host: string = '0.0.0.0' 7 | port: number 8 | server: http.Server 9 | isRunning: boolean = false 10 | 11 | async start() { 12 | if (this.isRunning) throw new Error('Proxy is running.') 13 | this.isRunning = true 14 | 15 | this.port = await getPort(); 16 | return new Promise((resolve, reject) => { 17 | this.server = setup(http.createServer(), {}); 18 | this.server.listen(this.port, this.host, resolve); 19 | }) 20 | } 21 | 22 | stop() { 23 | this.server.close(); 24 | this.isRunning = false; 25 | } 26 | } 27 | 28 | export function createProxy() { 29 | return new Proxy(); 30 | } -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import translateService from './translate'; 2 | import { parseMultiple } from './util'; 3 | import { isSupport, getAllLanguage, getAllCode } from './language'; 4 | import { AxiosProxyConfig } from 'axios-https-proxy-fix'; 5 | 6 | export type Tld = 'cn' | 'com' 7 | 8 | export interface Options { 9 | tld?: Tld; 10 | from?: string, 11 | to: string, 12 | proxy?: AxiosProxyConfig, 13 | config?: Object, 14 | browers?: boolean, 15 | browersUrl?: string, 16 | format?: string, 17 | isUserAgent?: boolean, 18 | userAgent?: string, 19 | client?: string 20 | } 21 | 22 | function translate(value: string | string[], options: Options): Promise { 23 | // {tld: "cn"} 24 | let text: string[]; 25 | if(typeof value === 'string') { 26 | text = [value]; 27 | !options.format && (options.format = 'text'); 28 | } else { 29 | text = value; 30 | !options.format && (options.format = 'html'); 31 | } 32 | 33 | return translateService(text, options); 34 | } 35 | 36 | export { 37 | parseMultiple, 38 | isSupport, 39 | getAllLanguage, 40 | getAllCode 41 | }; 42 | 43 | export default translate; 44 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (https://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # TypeScript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | # next.js build output 61 | .next 62 | dist 63 | .vscode 64 | .nyc_output 65 | used.md 66 | test.ts -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "google-translate-open-api", 3 | "version": "1.3.7", 4 | "description": "A free and unlimited API for Google Translate(contains single and multiple) 💵🚫", 5 | "main": "dist/index.js", 6 | "types": "types/index.d.ts", 7 | "scripts": { 8 | "pub": "npm run build && npm run update", 9 | "build": "tsc && cp src/token.js ./dist", 10 | "update": "auto-vers -t", 11 | "test": "nyc mocha test/*.ts", 12 | "cover": "nyc npm test && nyc report --reporter=text-lcov > coverage.lcov && codecov" 13 | }, 14 | "repository": { 15 | "type": "git", 16 | "url": "git+https://github.com/hua1995116/google-translate-open-api.git" 17 | }, 18 | "keywords": [ 19 | "google", 20 | "translate", 21 | "multiple", 22 | "language" 23 | ], 24 | "author": "hua1995116 (https://github.com/hua1995116)", 25 | "license": "Apache License", 26 | "bugs": { 27 | "url": "https://github.com/hua1995116/google-translate-open-api/issues" 28 | }, 29 | "homepage": "https://github.com/hua1995116/google-translate-open-api#readme", 30 | "devDependencies": { 31 | "@types/mocha": "^5.2.7", 32 | "@types/node": "^12.7.4", 33 | "auto-vers": "^1.0.3", 34 | "chai": "^4.2.0", 35 | "codecov": "^3.5.0", 36 | "get-port": "^5.0.0", 37 | "mocha": "^6.1.4", 38 | "nyc": "^14.1.1", 39 | "proxy": "^0.2.4", 40 | "ts-node": "^8.3.0", 41 | "typescript": "^3.5.3" 42 | }, 43 | "nyc": { 44 | "include": [ 45 | "src/**/*.ts", 46 | "src/**/*.tsx" 47 | ], 48 | "extension": [ 49 | ".ts", 50 | ".tsx" 51 | ], 52 | "require": [ 53 | "ts-node/register" 54 | ], 55 | "reporter": [ 56 | "text-summary", 57 | "html" 58 | ], 59 | "sourceMap": true, 60 | "instrument": true 61 | }, 62 | "dependencies": { 63 | "@vitalets/google-translate-token": "^1.1.0", 64 | "axios-https-proxy-fix": "^0.17.1" 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/translate.ts: -------------------------------------------------------------------------------- 1 | const translateToken = require('./token'); 2 | import axios, { AxiosRequestConfig } from 'axios-https-proxy-fix'; 3 | import { arrayStringify } from './util'; 4 | import { Options } from './index'; 5 | import { isSupport, getCode } from './language'; 6 | 7 | function handletranslate(data: string[], extra: Options): Promise { 8 | let e: any; 9 | if(extra.from) { 10 | if(!isSupport(extra.from)) { 11 | e = new Error(); 12 | e.language = extra.from; 13 | } 14 | } 15 | if(!isSupport(extra.to)) { 16 | e = new Error(); 17 | e.language = extra.to; 18 | } 19 | if (e) { 20 | e.code = 400; 21 | e.message = 'The language \'' + e.language + '\' is not supported'; 22 | return new Promise(function (_, reject) { 23 | reject(e); 24 | }); 25 | } 26 | const tld = extra.tld || 'com'; 27 | return translateToken 28 | .get(data.join(''), { 29 | tld, 30 | proxy: extra.proxy || false, 31 | }) 32 | .then(res => { 33 | const query = { 34 | anno: 3, 35 | client: extra.client || "t", 36 | format: extra.format, 37 | v: 1.0, 38 | key: null, 39 | logld: "vTE_20190506_00", 40 | sl: extra.from || 'auto', 41 | tl: extra.to || 'zh-CN', 42 | hl: 'zh-CN', 43 | sp: "nmt", 44 | tc: 2, 45 | sr: 1, 46 | tk: res.value, 47 | mode: 1 48 | }; 49 | 50 | const headers = { 51 | "content-type": "application/x-www-form-urlencoded", 52 | "Accept": "application/json, text/plain, */*", 53 | 'X-Requested-With': 'XMLHttpRequest' 54 | } 55 | 56 | if (typeof extra.isUserAgent === 'undefined' || extra.isUserAgent) { 57 | headers['User-Agent'] = extra.userAgent ? extra.userAgent : 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36'; 58 | } 59 | 60 | const options: AxiosRequestConfig = { 61 | method: "POST", 62 | headers, 63 | data: arrayStringify(data), 64 | url: '/translate_a/t', 65 | baseURL: `https://translate.google.${tld}`, 66 | params: query, 67 | proxy: extra.proxy || false, 68 | ...(extra.config) 69 | }; 70 | let browersUrl = 'https://cors-anywhere.herokuapp.com/'; 71 | if (extra.browersUrl) { 72 | browersUrl = extra.browersUrl; 73 | } 74 | if(extra.browers) { 75 | options.baseURL = browersUrl + options.baseURL; 76 | } 77 | 78 | return axios(options); 79 | }) 80 | } 81 | 82 | export default handletranslate; -------------------------------------------------------------------------------- /test/language.test.ts: -------------------------------------------------------------------------------- 1 | import { expect } from 'chai'; 2 | import { isSupport, getCode, getAllLanguage, getAllCode } from '../src/language'; 3 | 4 | describe('test language', () => { 5 | it('support', () => { 6 | expect(isSupport('zh-CN')).to.equal(true); 7 | }); 8 | 9 | it('not support', () => { 10 | expect(isSupport('javascript')).to.equal(false); 11 | }); 12 | 13 | it('get code full', () => { 14 | expect(getCode('Korean')).to.equal('ko'); 15 | 16 | }) 17 | it('get code simple', () => { 18 | expect(getCode('zu')).to.equal('zu'); 19 | }) 20 | it('get undefined language', () => { 21 | expect(getCode('sss')).to.equal(false); 22 | }); 23 | it('get null language', () => { 24 | expect(getCode('')).to.equal(false); 25 | }); 26 | it('getAllLanguage', () => { 27 | const compare = '["Automatic","Afrikaans","Albanian","Amharic","Arabic","Armenian","Azerbaijani","Basque","Belarusian","Bengali","Bosnian","Bulgarian","Catalan","Cebuano","Chichewa","Chinese Simplified","Chinese Traditional","Corsican","Croatian","Czech","Danish","Dutch","English","Esperanto","Estonian","Filipino","Finnish","French","Frisian","Galician","Georgian","German","Greek","Gujarati","Haitian Creole","Hausa","Hawaiian","Hebrew","Hindi","Hmong","Hungarian","Icelandic","Igbo","Indonesian","Irish","Italian","Japanese","Javanese","Kannada","Kazakh","Khmer","Korean","Kurdish (Kurmanji)","Kyrgyz","Lao","Latin","Latvian","Lithuanian","Luxembourgish","Macedonian","Malagasy","Malay","Malayalam","Maltese","Maori","Marathi","Mongolian","Myanmar (Burmese)","Nepali","Norwegian","Pashto","Persian","Polish","Portuguese","Punjabi","Romanian","Russian","Samoan","Scots Gaelic","Serbian","Sesotho","Shona","Sindhi","Sinhala","Slovak","Slovenian","Somali","Spanish","Sundanese","Swahili","Swedish","Tajik","Tamil","Telugu","Thai","Turkish","Ukrainian","Urdu","Uyghur","Uzbek","Vietnamese","Welsh","Xhosa","Yiddish","Yoruba","Zulu"]'; 28 | expect(JSON.stringify(getAllLanguage())).to.equal(compare); 29 | }) 30 | it('getAllCode', () => { 31 | const compare = '["auto","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","pa","ro","ru","sm","gd","sr","st","sn","sd","si","sk","sl","so","es","su","sw","sv","tg","ta","te","th","tr","uk","ur","ug","uz","vi","cy","xh","yi","yo","zu"]'; 32 | expect(JSON.stringify(getAllCode())).to.equal(compare); 33 | }) 34 | }) -------------------------------------------------------------------------------- /types/language.d.ts: -------------------------------------------------------------------------------- 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 | declare const langs: { 9 | Automatic: string; 10 | Afrikaans: string; 11 | Albanian: string; 12 | Amharic: string; 13 | Arabic: string; 14 | Armenian: string; 15 | Azerbaijani: string; 16 | Basque: string; 17 | Belarusian: string; 18 | Bengali: string; 19 | Bosnian: string; 20 | Bulgarian: string; 21 | Catalan: string; 22 | Cebuano: string; 23 | Chichewa: string; 24 | "Chinese Simplified": string; 25 | "Chinese Traditional": string; 26 | Corsican: string; 27 | Croatian: string; 28 | Czech: string; 29 | Danish: string; 30 | Dutch: string; 31 | English: string; 32 | Esperanto: string; 33 | Estonian: string; 34 | Filipino: string; 35 | Finnish: string; 36 | French: string; 37 | Frisian: string; 38 | Galician: string; 39 | Georgian: string; 40 | German: string; 41 | Greek: string; 42 | Gujarati: string; 43 | "Haitian Creole": string; 44 | Hausa: string; 45 | Hawaiian: string; 46 | Hebrew: string; 47 | Hindi: string; 48 | Hmong: string; 49 | Hungarian: string; 50 | Icelandic: string; 51 | Igbo: string; 52 | Indonesian: string; 53 | Irish: string; 54 | Italian: string; 55 | Japanese: string; 56 | Javanese: string; 57 | Kannada: string; 58 | Kazakh: string; 59 | Khmer: string; 60 | Korean: string; 61 | "Kurdish (Kurmanji)": string; 62 | Kyrgyz: string; 63 | Lao: string; 64 | Latin: string; 65 | Latvian: string; 66 | Lithuanian: string; 67 | Luxembourgish: string; 68 | Macedonian: string; 69 | Malagasy: string; 70 | Malay: string; 71 | Malayalam: string; 72 | Maltese: string; 73 | Maori: string; 74 | Marathi: string; 75 | Mongolian: string; 76 | "Myanmar (Burmese)": string; 77 | Nepali: string; 78 | Norwegian: string; 79 | Pashto: string; 80 | Persian: string; 81 | Polish: string; 82 | Portuguese: string; 83 | Punjabi: string; 84 | Romanian: string; 85 | Russian: string; 86 | Samoan: string; 87 | "Scots Gaelic": string; 88 | Serbian: string; 89 | Sesotho: string; 90 | Shona: string; 91 | Sindhi: string; 92 | Sinhala: string; 93 | Slovak: string; 94 | Slovenian: string; 95 | Somali: string; 96 | Spanish: string; 97 | Sundanese: string; 98 | Swahili: string; 99 | Swedish: string; 100 | Tajik: string; 101 | Tamil: string; 102 | Telugu: string; 103 | Thai: string; 104 | Turkish: string; 105 | Ukrainian: string; 106 | Urdu: string; 107 | Uyghur: string; 108 | Uzbek: string; 109 | Vietnamese: string; 110 | Welsh: string; 111 | Xhosa: string; 112 | Yiddish: string; 113 | Yoruba: string; 114 | Zulu: string; 115 | }; 116 | export declare function isSupport(language: string): boolean; 117 | export declare function getCode(language: string): any; 118 | export declare function getAllLanguage(): string[]; 119 | export declare function getAllCode(): any[]; 120 | export default langs; 121 | -------------------------------------------------------------------------------- /test/translate.test.ts: -------------------------------------------------------------------------------- 1 | import { expect } from 'chai'; 2 | import translate, { parseMultiple } from '../src/index'; 3 | import { createProxy } from './help' 4 | 5 | describe('google-translate-open-api', () => { 6 | it('not support from', async() => { 7 | try { 8 | const result = await translate(`I'm fine.`, { 9 | tld: "cn", 10 | from: "xxx", 11 | to: "zh-CN", 12 | }); 13 | } catch(e) { 14 | expect(e.code).to.equal(400); 15 | } 16 | }) 17 | it('not support to', async() => { 18 | try { 19 | const result = await translate(`I'm fine.`, { 20 | tld: "cn", 21 | to: "xxx", 22 | }); 23 | } catch(e) { 24 | expect(e.code).to.equal(400); 25 | } 26 | }) 27 | it('translate single', async () => { 28 | const result = await translate(`I'm fine.`, { 29 | tld: "cn", 30 | to: "zh-CN", 31 | }); 32 | const data = result.data[0]; 33 | const compare = '我很好。'; 34 | expect(data).to.equal(compare); 35 | }); 36 | it('translate multiple', async () => { 37 | const result = await translate([`I'm fine.`, `I'm ok.`], { 38 | tld: "cn", 39 | to: "zh-CN", 40 | }); 41 | const data = result.data[0]; 42 | const compare = '[[[["我很好。"]],null,"en"],[[["我可以。"]],null,"en"]]'; 43 | expect(JSON.stringify(data)).to.equal(compare); 44 | }); 45 | it('translate parseMultiple', async () => { 46 | const result = await translate([`I'm fine. And you?`,`I'm ok.`], { 47 | tld: "cn", 48 | to: "zh-CN", 49 | }); 50 | const data = result.data[0]; 51 | const parseData = parseMultiple(data); 52 | const compare = '["我很好。你呢?","我可以。"]'; 53 | expect(JSON.stringify(parseData)).to.equal(compare); 54 | }); 55 | 56 | it('translate format text', async () => { 57 | const result = await translate(`I'm fine. And you?\nI'm fine. And you?`, { 58 | tld: "cn", 59 | to: "zh-CN", 60 | format: "text", 61 | }); 62 | const data = result.data[0]; 63 | const compare = '我很好。你呢?\n我很好。你呢?'; 64 | expect(data).to.equal(compare); 65 | }); 66 | 67 | it('translate format html', async () => { 68 | const result = await translate(`I'm fine. And you?\nI'm fine. And you?`, { 69 | tld: "cn", 70 | to: "zh-CN", 71 | format: "html", 72 | }); 73 | const data = result.data[0]; 74 | const compare = '我很好。你呢?我很好。你呢?'; 75 | expect(data).to.equal(compare); 76 | }); 77 | 78 | it('translate browers', async () => { 79 | const result = await translate(`I'm fine.`, { 80 | tld: "cn", 81 | to: "zh-CN", 82 | browers: true 83 | }); 84 | const data = result.data[0]; 85 | const compare = '我很好。'; 86 | expect(data).to.equal(compare); 87 | }); 88 | }) 89 | 90 | describe('proxy', () => { 91 | let proxy 92 | 93 | before(async () => { 94 | proxy = createProxy(); 95 | await proxy.start(); 96 | }); 97 | 98 | after(function () { 99 | proxy.stop(); 100 | }); 101 | 102 | it('translate proxy', async () => { 103 | const result = await translate(`I'm fine.`, { 104 | tld: "cn", 105 | to: "zh-CN", 106 | proxy: { 107 | host: proxy.host, 108 | port: proxy.port 109 | } 110 | }); 111 | 112 | const data = result.data[0]; 113 | const compare = '我很好。'; 114 | expect(data).to.equal(compare); 115 | }); 116 | }); -------------------------------------------------------------------------------- /src/language.ts: -------------------------------------------------------------------------------- 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 | const langs = { 10 | Automatic: "auto", 11 | Afrikaans: "af", 12 | Albanian: "sq", 13 | Amharic: "am", 14 | Arabic: "ar", 15 | Armenian: "hy", 16 | Azerbaijani: "az", 17 | Basque: "eu", 18 | Belarusian: "be", 19 | Bengali: "bn", 20 | Bosnian: "bs", 21 | Bulgarian: "bg", 22 | Catalan: "ca", 23 | Cebuano: "ceb", 24 | Chichewa: "ny", 25 | "Chinese Simplified": "zh-cn", 26 | "Chinese Traditional": "zh-tw", 27 | Corsican: "co", 28 | Croatian: "hr", 29 | Czech: "cs", 30 | Danish: "da", 31 | Dutch: "nl", 32 | English: "en", 33 | Esperanto: "eo", 34 | Estonian: "et", 35 | Filipino: "tl", 36 | Finnish: "fi", 37 | French: "fr", 38 | Frisian: "fy", 39 | Galician: "gl", 40 | Georgian: "ka", 41 | German: "de", 42 | Greek: "el", 43 | Gujarati: "gu", 44 | "Haitian Creole": "ht", 45 | Hausa: "ha", 46 | Hawaiian: "haw", 47 | Hebrew: "iw", 48 | Hindi: "hi", 49 | Hmong: "hmn", 50 | Hungarian: "hu", 51 | Icelandic: "is", 52 | Igbo: "ig", 53 | Indonesian: "id", 54 | Irish: "ga", 55 | Italian: "it", 56 | Japanese: "ja", 57 | Javanese: "jw", 58 | Kannada: "kn", 59 | Kazakh: "kk", 60 | Khmer: "km", 61 | Korean: "ko", 62 | "Kurdish (Kurmanji)": "ku", 63 | Kyrgyz: "ky", 64 | Lao: "lo", 65 | Latin: "la", 66 | Latvian: "lv", 67 | Lithuanian: "lt", 68 | Luxembourgish: "lb", 69 | Macedonian: "mk", 70 | Malagasy: "mg", 71 | Malay: "ms", 72 | Malayalam: "ml", 73 | Maltese: "mt", 74 | Maori: "mi", 75 | Marathi: "mr", 76 | Mongolian: "mn", 77 | "Myanmar (Burmese)": "my", 78 | Nepali: "ne", 79 | Norwegian: "no", 80 | Pashto: "ps", 81 | Persian: "fa", 82 | Polish: "pl", 83 | Portuguese: "pt", 84 | Punjabi: "pa", 85 | Romanian: "ro", 86 | Russian: "ru", 87 | Samoan: "sm", 88 | "Scots Gaelic": "gd", 89 | Serbian: "sr", 90 | Sesotho: "st", 91 | Shona: "sn", 92 | Sindhi: "sd", 93 | Sinhala: "si", 94 | Slovak: "sk", 95 | Slovenian: "sl", 96 | Somali: "so", 97 | Spanish: "es", 98 | Sundanese: "su", 99 | Swahili: "sw", 100 | Swedish: "sv", 101 | Tajik: "tg", 102 | Tamil: "ta", 103 | Telugu: "te", 104 | Thai: "th", 105 | Turkish: "tr", 106 | Ukrainian: "uk", 107 | Urdu: "ur", 108 | Uyghur: "ug", 109 | Uzbek: "uz", 110 | Vietnamese: "vi", 111 | Welsh: "cy", 112 | Xhosa: "xh", 113 | Yiddish: "yi", 114 | Yoruba: "yo", 115 | Zulu: "zu" 116 | }; 117 | 118 | export function isSupport(language: string) { 119 | return Boolean(getCode(language)); 120 | } 121 | 122 | export function getCode(language: string) { 123 | if(!language) { 124 | return false; 125 | } 126 | if(langs[language]) { 127 | return langs[language]; 128 | } 129 | const keys = Object.keys(langs).filter(item => { 130 | const lowerLan = language.toLowerCase(); 131 | return langs[item] === lowerLan; 132 | }); 133 | if(keys[0]) { 134 | return langs[keys[0]]; 135 | } 136 | 137 | return false; 138 | } 139 | 140 | export function getAllLanguage() { 141 | return Object.keys(langs); 142 | } 143 | 144 | export function getAllCode() { 145 | return Object.keys(langs).map(item => (langs[item])); 146 | } 147 | 148 | export default langs; 149 | -------------------------------------------------------------------------------- /src/token.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Last update: 2016/06/26 3 | * https://translate.google.com/translate/releases/twsfe_w_20160620_RC00/r/js/desktop_module_main.js 4 | * 5 | * Everything between 'BEGIN' and 'END' was copied from the url above. 6 | * fork from https://github.com/vitalets/google-translate-token 7 | * for support brower 8 | */ 9 | 10 | var axios = require('axios-https-proxy-fix'); 11 | 12 | /* eslint-disable */ 13 | // BEGIN 14 | 15 | function sM(a) { 16 | var b; 17 | if (null !== yr) 18 | b = yr; 19 | else { 20 | b = wr(String.fromCharCode(84)); 21 | var c = wr(String.fromCharCode(75)); 22 | b = [b(), b()]; 23 | b[1] = c(); 24 | b = (yr = window[b.join(c())] || "") || "" 25 | } 26 | var d = wr(String.fromCharCode(116)) 27 | , c = wr(String.fromCharCode(107)) 28 | , d = [d(), d()]; 29 | d[1] = c(); 30 | c = "&" + d.join("") + "="; 31 | d = b.split("."); 32 | b = Number(d[0]) || 0; 33 | for (var e = [], f = 0, g = 0; g < a.length; g++) { 34 | var l = a.charCodeAt(g); 35 | 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), 36 | e[f++] = l >> 18 | 240, 37 | e[f++] = l >> 12 & 63 | 128) : e[f++] = l >> 12 | 224, 38 | e[f++] = l >> 6 & 63 | 128), 39 | e[f++] = l & 63 | 128) 40 | } 41 | a = b; 42 | for (f = 0; f < e.length; f++) 43 | a += e[f], 44 | a = xr(a, "+-a^+6"); 45 | a = xr(a, "+-3^+b+-f"); 46 | a ^= Number(d[1]) || 0; 47 | 0 > a && (a = (a & 2147483647) + 2147483648); 48 | a %= 1E6; 49 | return c + (a.toString() + "." + (a ^ b)) 50 | } 51 | 52 | var yr = null; 53 | var wr = function(a) { 54 | return function() { 55 | return a 56 | } 57 | } 58 | , xr = function(a, b) { 59 | for (var c = 0; c < b.length - 2; c += 3) { 60 | var d = b.charAt(c + 2) 61 | , d = "a" <= d ? d.charCodeAt(0) - 87 : Number(d) 62 | , d = "+" == b.charAt(c + 1) ? a >>> d : a << d; 63 | a = "+" == b.charAt(c) ? a + d & 4294967295 : a ^ d 64 | } 65 | return a 66 | }; 67 | 68 | // END 69 | /* eslint-enable */ 70 | 71 | var window = { 72 | TKK: '0' 73 | }; 74 | 75 | function updateTKK(opts) { 76 | opts = opts || {tld: 'com'}; 77 | return new Promise(function (resolve, reject) { 78 | var now = Math.floor(Date.now() / 3600000); 79 | 80 | if (Number(window.TKK.split('.')[0]) === now) { 81 | resolve(); 82 | } else { 83 | axios({ 84 | url: 'https://translate.google.' + opts.tld, 85 | proxy: opts.proxy, 86 | }).then(function (res) { 87 | var matches = res.data.match(/tkk:\s?'(.+?)'/i); 88 | 89 | if (matches) { 90 | window.TKK = matches[1]; 91 | } 92 | 93 | /** 94 | * Note: If the regex or the eval fail, there is no need to worry. The server will accept 95 | * relatively old seeds. 96 | */ 97 | 98 | resolve(); 99 | }).catch(function (err) { 100 | var e = new Error(); 101 | e.code = 'BAD_NETWORK'; 102 | e.message = err.message; 103 | reject(e); 104 | }); 105 | } 106 | }); 107 | } 108 | 109 | function get(text, opts) { 110 | return updateTKK(opts).then(function () { 111 | var tk = sM(text); 112 | tk = tk.replace('&tk=', ''); 113 | return {name: 'tk', value: tk}; 114 | }).catch(function (err) { 115 | throw err; 116 | }); 117 | } 118 | 119 | module.exports.get = get; -------------------------------------------------------------------------------- /README_zh.md: -------------------------------------------------------------------------------- 1 | # google-translate-open-api 2 | 一个免费无限制的谷歌翻译api(支持单段文本以及多段文本同时翻译) 💵🚫 3 | 4 |

5 | 6 | 7 | 8 | 9 | 10 | Support me 11 |

12 | 13 |

14 | 中文| 15 | English 16 |

17 | 18 | # Feature 19 | 20 | - 多段文本支持 21 | - 自动识别语言 22 | - 拼写纠正 23 | - 语言修正 24 | - 快速可靠 - 它使用和 [translate.google.com](https://translate.google.com/) 相同的服务器 25 | - 免费无限制 (translate.google.com使用令牌来授权请求。 如果你不是Google,则表示你没有此令牌,并且必须每100万字符的文字支付20美元。) 26 | 27 | # Install 28 | 29 | ```shell 30 | npm install --save google-translate-open-api 31 | ``` 32 | 33 | # Why this repo ? 34 | 35 | 当我有以下一段文本时候 ( 来自 [How Are Function Components Different from Classes?](https://overreacted.io/how-are-function-components-different-from-classes/)) 36 | 37 | ``` 38 | Maybe you’ve heard one of them is better for performance. Which one? Many of such benchmarks are flawed so I’d be careful drawing conclusions from them. 39 | ``` 40 | 我并不想将上述的文本一次性翻译,我更喜欢一句一句翻译。尤其是对于一篇文章翻译时,效果可能没有逐段翻译的效果更佳 41 | 42 | ![1565448193440.jpg](https://s3.qiufengh.com/blog/1565448193440.jpg) 43 | 44 | ![1565516309452.jpg](https://s3.qiufengh.com/blog/1565516309452.jpg) 45 | 46 | 当然用现在已经有的库(像 [google-translate-api](https://github.com/matheuss/google-translate-api)),依然能够实现上述的功能,但是在翻译多段文本时候,需要请求api多次,这严重造成请求的浪费与等待漫长的时间。 47 | 48 | 因此我需要用一个新的 api 来实现上述功能。`google-translate-open-api`就这样诞生了。 49 | 50 | # Usage 51 | 52 | 单段文本 53 | ```javascript 54 | import translate from 'google-translate-open-api'; 55 | const result = await translate(`I'm fine.`, { 56 | tld: "cn", 57 | to: "zh-CN", 58 | }); 59 | const data = result.data[0]; 60 | 61 | // 我很好。 62 | ``` 63 | 64 | 多段文本 65 | ```javascript 66 | import translate from 'google-translate-open-api'; 67 | 68 | const result = await translate([`I'm fine.`, `I'm ok.`], { 69 | tld: "cn", 70 | to: "zh-CN", 71 | }); 72 | const data = result.data[0]; 73 | // [[[["我很好。"]],null,"en"],[[["我可以。"]],null,"en"]] 74 | ``` 75 | 76 | > 注意: 多段文本的返回值和单段文本的返回值不同,你需要额外的注意 77 | 78 | 多段文本中含有多个句子 79 | 80 | ```javascript 81 | import translate, { parseMultiple } from 'google-translate-open-api'; 82 | 83 | const result = await translate([`I'm fine. And you?`,`I'm ok.`], { 84 | tld: "cn", 85 | to: "zh-CN", 86 | }); 87 | // [[[[["I'm fine. 我很好。 And you? 你呢?"]],null,"en"],[[["我可以。"]],null,"en"]]] 88 | 89 | // use parseMultiple 90 | const data = result.data[0]; 91 | const parseData = parseMultiple(data); 92 | // ["我很好。你呢?","我可以。"] 93 | ``` 94 | 95 | 代理 96 | 97 | proxy-config [https://github.com/axios/axios#request-config](https://github.com/axios/axios#request-config) 98 | ```javascript 99 | const result = await translate([`I'm fine. And you?`,`I'm ok.`], { 100 | tld: "cn", 101 | to: "zh-CN", 102 | proxy: { 103 | host: '127.0.0.1', 104 | port: 9000, 105 | auth: { 106 | username: 'mikeymike', 107 | password: 'rapunz3l' 108 | } 109 | } 110 | }); 111 | ``` 112 | 113 | 浏览器 114 | 115 | ```javascript 116 | const result = await translate([`I'm fine. And you?`,`I'm ok.`], { 117 | tld: "cn", 118 | to: "zh-CN", 119 | browers: true 120 | }); 121 | 122 | const data = result.data[0]; 123 | 124 | // 我很好。 125 | ``` 126 | 127 | commonJS 128 | 129 | ```javascript 130 | const translate = require('google-translate-open-api').default; 131 | ``` 132 | 133 | # API 134 | 135 | ## translate(text, options) 136 | 137 | ### text 138 | 139 | Type: `string` 140 | 141 | 要翻译的文本 142 | 143 | ### options 144 | 145 | Type: object 146 | 147 | **from** 148 | Type: `string` Default: auto 149 | 150 | 语言来源. 必须是从 src/languages.ts 中的一种,默认为 auto类型。 151 | 152 | **to** 153 | Type: `string` Default: en 154 | 155 | 将要翻译的语言, 必须是从 src/languages.ts 中的一种。 156 | 157 | ### tld 158 | Type: `string` 'com' | 'cn' 159 | 160 | 中国用 `cn` , 其他国家用 `com` . 161 | 162 | **proxy** 163 | Type: `AxiosProxyConfig` 164 | 165 | 为请求代理 166 | 167 | **config** 168 | Type: `object` 169 | 170 | axios 的配置 [axios](https://github.com/axios/axios) 171 | 172 | **browers** 173 | Type: `boolean` 174 | 175 | 通过 [cors-anywhere](https://github.com/Rob--W/cors-anywhere/) 来实现浏览器的支持 (这是一个公共服务,不能保证100%的稳定) 176 | 177 | **browersUrl** 178 | Type: `string` 179 | 180 | 用户自定义代理链接 181 | 182 | **format** 183 | Type: `string` `` 184 | 185 | When use single translate, default use `text` (but use can set it to `html`) and use batch translate, default and only use `html`. 186 | 当使用单文本处理的时候,默认使用 `text` 去获取文本,保留原格式。(你也可以用`html`来获取默认的文本),使用批处理的时候默认且只能使用 `html` 格式 187 | 188 | 189 | # 相关库 190 | - [vitalets/google-translate-token](https://github.com/vitalets/google-translate-token) 191 | - [google-translate-api](https://github.com/matheuss/google-translate-api) 192 | 193 | # 灵感来源 194 | 195 | - [google translate](https://chrome.google.com/webstore/detail/google-translate/aapbdbdomjkkjkaonfhkkikfgjllcleb?hl=zh-CN) 196 | - [translate-md-viewer](https://github.com/hua1995116/translate-md-viewer) 197 | 198 | # License 199 | 200 | Apache License 201 | 202 | Copyright (c) 2019 蓝色的秋风 -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # google-translate-open-api 2 | A free and unlimited API for Google Translate(support single text and Multi-segment text) 💵🚫 3 | 4 |

5 | 6 | 7 | 8 | 9 | 10 | Support me 11 |

12 | 13 |

14 | 中文| 15 | English 16 |

17 | 18 | # Feature 19 | 20 | - Multi-segment text support 21 | - Auto language detection 22 | - Spelling correction 23 | - Language correction 24 | - Fast and reliable – it uses the same servers that [translate.google.com](https://translate.google.com/) uses 25 | - Free and unlimited (translate.google.com uses a token to authorize the requests. If you are not Google, you do not have this token and will have to pay [$20 per 1 million characters of text](https://cloud.google.com/translate/v2/pricing)) 26 | 27 | # Install 28 | 29 | ```shell 30 | npm install --save google-translate-open-api 31 | ``` 32 | 33 | # Why this repo ? 34 | 35 | when I have the following sentence. ( from [How Are Function Components Different from Classes?](https://overreacted.io/how-are-function-components-different-from-classes/)) 36 | 37 | ``` 38 | Maybe you’ve heard one of them is better for performance. Which one? Many of such benchmarks are flawed so I’d be careful drawing conclusions from them. 39 | ``` 40 | I don't want to translate all the text first and I'd like to translate segment by segment. Especially in an article, the whole translation may not work well. 41 | 42 | ![1565448193440.jpg](https://s3.qiufengh.com/blog/1565448193440.jpg) 43 | 44 | ![1565516309452.jpg](https://s3.qiufengh.com/blog/1565516309452.jpg) 45 | 46 | In the existing library, if I want to translate multi-segment text, I have to request multiple times.(like [google-translate-api](https://github.com/matheuss/google-translate-api)) 47 | 48 | So I have to use the new api to implement, so the `google-translate-open-api` is born. 49 | 50 | # Usage 51 | 52 | Single segment 53 | ```javascript 54 | import translate from 'google-translate-open-api'; 55 | const result = await translate(`I'm fine.`, { 56 | tld: "cn", 57 | to: "zh-CN", 58 | }); 59 | const data = result.data[0]; 60 | 61 | // 我很好。 62 | ``` 63 | 64 | Multi-segment text 65 | ```javascript 66 | import translate from 'google-translate-open-api'; 67 | 68 | const result = await translate([`I'm fine.`, `I'm ok.`], { 69 | tld: "cn", 70 | to: "zh-CN", 71 | }); 72 | const data = result.data[0]; 73 | // [[[["我很好。"]],null,"en"],[[["我可以。"]],null,"en"]] 74 | ``` 75 | 76 | > Note: Multi-segment text result is different from single sentence. You need extra attention. 77 | 78 | Multi-segment text contains mylti-sentence. 79 | 80 | ```javascript 81 | import translate, { parseMultiple } from 'google-translate-open-api'; 82 | 83 | const result = await translate([`I'm fine. And you?`,`I'm ok.`], { 84 | tld: "cn", 85 | to: "zh-CN", 86 | }); 87 | // [[[[["I'm fine. 我很好。 And you? 你呢?"]],null,"en"],[[["我可以。"]],null,"en"]]] 88 | 89 | // use parseMultiple 90 | const data = result.data[0]; 91 | const parseData = parseMultiple(data); 92 | // ["我很好。你呢?","我可以。"] 93 | ``` 94 | 95 | Proxy 96 | 97 | proxy-config [https://github.com/axios/axios#request-config](https://github.com/axios/axios#request-config) 98 | ```javascript 99 | const result = await translate([`I'm fine. And you?`,`I'm ok.`], { 100 | tld: "cn", 101 | to: "zh-CN", 102 | proxy: { 103 | host: '127.0.0.1', 104 | port: 9000, 105 | auth: { 106 | username: 'mikeymike', 107 | password: 'rapunz3l' 108 | } 109 | } 110 | }); 111 | ``` 112 | 113 | Browers 114 | 115 | ```javascript 116 | const result = await translate([`I'm fine. And you?`,`I'm ok.`], { 117 | tld: "cn", 118 | to: "zh-CN", 119 | browers: true 120 | }); 121 | 122 | const data = result.data[0]; 123 | 124 | // 我很好。 125 | ``` 126 | 127 | For commonJS 128 | 129 | ```javascript 130 | const translate = require('google-translate-open-api').default; 131 | ``` 132 | 133 | # Multi-Terminal Example 134 | 135 | https://github.com/hua1995116/google-translate-open-api-demo 136 | 137 | # API 138 | 139 | ## translate(text, options) 140 | 141 | ### text 142 | 143 | Type: `string` 144 | 145 | The text to be translated 146 | 147 | ### options 148 | 149 | Type: object 150 | 151 | **from?** 152 | Type: `string` Default: auto 153 | 154 | The text language. Must be auto or one of the codes/names (not case sensitive) contained in src/languages.ts 155 | 156 | **to** 157 | Type: `string` Default: en 158 | 159 | The language in which the text should be translated. Must be one of the codes/names (not case sensitive) contained in src/languages.ts. 160 | 161 | **tld** 162 | Type: `string` 'com' | 'cn' 163 | 164 | `cn` is for China, `com` for others. 165 | 166 | **proxy** 167 | Type: `AxiosProxyConfig` 168 | 169 | proxy for request. 170 | 171 | **config** 172 | Type: `object` 173 | 174 | config for [axios](https://github.com/axios/axios) 175 | 176 | **browers** 177 | Type: `boolean` 178 | 179 | support browers via [cors-anywhere](https://github.com/Rob--W/cors-anywhere/) (This is a public service, not necessarily stable) 180 | 181 | **browersUrl** 182 | Type: `string` 183 | 184 | custom browers proxy url 185 | 186 | **format** 187 | Type: `string` `` 188 | 189 | When use single translate, default use `text` (but we can set it to `html`) and use batch translate, default and only use `html`. 190 | 191 | 192 | # Related 193 | - [vitalets/google-translate-token](https://github.com/vitalets/google-translate-token) 194 | - [google-translate-api](https://github.com/matheuss/google-translate-api) 195 | - [translate-md-viewer](https://github.com/hua1995116/translate-md-viewer) 196 | 197 | # Inspiration 198 | 199 | - [google translate](https://chrome.google.com/webstore/detail/google-translate/aapbdbdomjkkjkaonfhkkikfgjllcleb?hl=zh-CN) 200 | 201 | # License 202 | 203 | Apache License 204 | 205 | Copyright (c) 2019 蓝色的秋风 -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | --------------------------------------------------------------------------------