From 47baf19fa68a47b9e9438570dd719b2c441c54f1 Mon Sep 17 00:00:00 2001 From: Jason Dobry Date: Sat, 20 Aug 2016 18:10:57 -0700 Subject: [PATCH] Add Translate API samples. Fixes #176. (#184) * Add Translate API samples. Fixes #176. * Fix build. * Add comment. * Address comments --- .travis.yml | 2 + README.md | 11 ++ circle.yml | 1 + scripts/install | 1 + scripts/uninstall | 1 + translate/README.md | 57 +++++++ translate/package.json | 19 +++ translate/system-test/translate.test.js | 66 +++++++++ translate/test/translate.test.js | 188 ++++++++++++++++++++++++ translate/translate.js | 170 +++++++++++++++++++++ 10 files changed, 516 insertions(+) create mode 100644 translate/README.md create mode 100644 translate/package.json create mode 100644 translate/system-test/translate.test.js create mode 100644 translate/test/translate.test.js create mode 100644 translate/translate.js diff --git a/.travis.yml b/.travis.yml index 058c012624..d59119159f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -75,6 +75,7 @@ cache: - speech/node_modules/ - storage/node_modules/ - trace/node_modules/ + - translate/node_modules/ - vision/node_modules/ env: @@ -85,6 +86,7 @@ env: before_install: - openssl aes-256-cbc -K $encrypted_fda0b707c7d5_key -iv $encrypted_fda0b707c7d5_iv -in key.json.enc -out key.json -d + - npm install -g npm - npm set progress=false before_script: diff --git a/README.md b/README.md index a4b321a4d7..610a5ee71a 100644 --- a/README.md +++ b/README.md @@ -37,6 +37,7 @@ on Google Cloud Platform. * [Google Cloud Pub/Sub](#google-cloud-pubsub) * [Google Cloud Speech API (Beta)](#google-cloud-speech-api-beta) * [Google Cloud Storage](#google-cloud-storage) + * [Google Translate API](#google-translate-api) * [Google Cloud Vision API](#google-cloud-vision-api) * [Stackdriver Debugger (Beta)](#stackdriver-debugger-beta) * [Stackdriver Logging (Beta)](#stackdriver-logging-beta) @@ -321,6 +322,16 @@ View the [Cloud Storage Node.js samples][storage_samples]. [storage_docs]: https://cloud.google.com/storage/docs/ [storage_samples]: storage +### Google Translate API + +With the [Google Translate API][translate_docs], you can dynamically translate +text between thousands of language pairs. + +View the [Translate API Node.js samples][translate_samples]. + +[translate_docs]: https://cloud.google.com/translate/docs/ +[translate_samples]: translate + ### Google Cloud Vision API The [Cloud Vision API][vision_docs] allows developers to easily integrate vision diff --git a/circle.yml b/circle.yml index 042ae5ca1e..adaa7b57ad 100644 --- a/circle.yml +++ b/circle.yml @@ -62,4 +62,5 @@ dependencies: - speech/node_modules/ - storage/node_modules/ - trace/node_modules/ + - translate/node_modules/ - vision/node_modules/ diff --git a/scripts/install b/scripts/install index c03a19d12f..ab110f922d 100755 --- a/scripts/install +++ b/scripts/install @@ -38,6 +38,7 @@ queue.push('pubsub'); queue.push('speech'); queue.push('storage'); queue.push('trace'); +queue.push('translate'); queue.push('vision'); /** diff --git a/scripts/uninstall b/scripts/uninstall index 2fa9c45eae..413c814c15 100755 --- a/scripts/uninstall +++ b/scripts/uninstall @@ -38,6 +38,7 @@ queue.push('pubsub'); queue.push('speech'); queue.push('storage'); queue.push('trace'); +queue.push('translate'); queue.push('vision'); /** diff --git a/translate/README.md b/translate/README.md new file mode 100644 index 0000000000..173ff49046 --- /dev/null +++ b/translate/README.md @@ -0,0 +1,57 @@ +Google Cloud Platform logo + +# Google Translate API Node.js Samples + +With the [Google Translate API][translate_docs], you can dynamically translate +text between thousands of language pairs. + +[translate_docs]: https://cloud.google.com/translate/docs/ + +## Table of Contents + +* [Setup](#setup) +* [Samples](#samples) + * [Translate](#translate) + +## Setup + +1. Read [Prerequisites][prereq] and [How to run a sample][run] first. +1. Install dependencies: + + npm install + +[prereq]: ../README.md#prerequisities +[run]: ../README.md#how-to-run-a-sample + +## Samples + +### Translate + +View the [documentation][translate_docs] or the [source code][translate_code]. + +__Usage:__ `node translate --help` + +``` +Commands: + detect Detect the language of the provided text + list List available translation languages. + translate Translate the provided text to the target language. + +Options: + --apiKey, -k Your Translate API key. Defaults to the value of the TRANSLATE_API_KEY environment + variable. [string] + --help Show help [boolean] + +Examples: + node translate detect -k your-key "Hello world!" Detect the language of "Hello world!". + node translate list -k your-key List available translation languages. + node translate translate -k your-key --to ru "Good Translate "Good morning!" to Russian, + morning!" auto-detecting English. + node translate translate -k your-key --to ru Translate "Good morning!" to Russian from + --from en "Good morning!" English. + +For more information, see https://cloud.google.com/translate/docs +``` + +[translate_docs]: https://cloud.google.com/translate/docs +[translate_code]: translate.js diff --git a/translate/package.json b/translate/package.json new file mode 100644 index 0000000000..79251cdc1e --- /dev/null +++ b/translate/package.json @@ -0,0 +1,19 @@ +{ + "name": "nodejs-docs-samples-translate", + "version": "0.0.1", + "private": true, + "license": "Apache Version 2.0", + "author": "Google Inc.", + "scripts": { + "test": "mocha -R spec -t 120000 --require intelli-espower-loader ../test/_setup.js test/*.test.js", + "system-test": "mocha -R spec -t 120000 --require intelli-espower-loader ../system-test/_setup.js system-test/*.test.js" + }, + "dependencies": { + "@google-cloud/translate": "^0.1.1", + "iso-639-1": "^1.2.1", + "yargs": "^5.0.0" + }, + "devDependencies": { + "mocha": "^3.0.2" + } +} diff --git a/translate/system-test/translate.test.js b/translate/system-test/translate.test.js new file mode 100644 index 0000000000..4fa7d6ae7d --- /dev/null +++ b/translate/system-test/translate.test.js @@ -0,0 +1,66 @@ +// Copyright 2015-2016, Google, Inc. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +'use strict'; + +var program = require('../translate'); +var apiKey = process.env.TRANSLATE_API_KEY; +var text = 'Hello world!'; + +describe('translate:translate', function () { + if (!process.env.TRANSLATE_API_KEY) { + process.stdout.write('Skipping Translate API tests...\n'); + return; + } + describe('detectLanguage', function () { + it('should detect language', function (done) { + program.detectLanguage(text, apiKey, function (err, result) { + assert.ifError(err); + assert(result, 'should have received a result'); + assert.equal(result.language, 'en', 'should have detected english'); + assert(console.log.calledWith('Detected %s with confidence %d', 'English', result.confidence)); + done(); + }); + }); + }); + + describe('listLanguages', function () { + it('should list languages', function (done) { + program.listLanguages(apiKey, function (err, languages) { + assert.ifError(err); + assert(Array.isArray(languages)); + assert(languages.length > 0); + assert(console.log.calledWith('Found %d language(s)!', languages.length)); + done(); + }); + }); + }); + + describe('translateText', function () { + it('should translate text', function (done) { + var options = { + text: text, + apiKey: apiKey, + to: 'ru' + }; + var expected = 'Привет мир!'; + + program.translateText(options, function (err, translation) { + assert.ifError(err); + assert.equal(translation, expected); + assert(console.log.calledWith('Translated text to %s', 'Russian')); + done(); + }); + }); + }); +}); diff --git a/translate/test/translate.test.js b/translate/test/translate.test.js new file mode 100644 index 0000000000..e7226d84bd --- /dev/null +++ b/translate/test/translate.test.js @@ -0,0 +1,188 @@ +// Copyright 2016, Google, Inc. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +'use strict'; + +var proxyquire = require('proxyquire').noCallThru(); +var text = 'Hello world!'; +var apiKey = 'key'; + +function getSample () { + var languagesMock = [ + 'en', + 'ru' + ]; + var resultMock = { + language: 'en', + confidence: 0.75, + input: text + }; + var translationMock = 'Привет мир!'; + var translateMock = { + getLanguages: sinon.stub().callsArgWith(0, null, languagesMock), + detect: sinon.stub().callsArgWith(1, null, resultMock), + translate: sinon.stub().callsArgWith(2, null, translationMock) + }; + var TranslateMock = sinon.stub().returns(translateMock); + + return { + program: proxyquire('../translate', { + '@google-cloud/translate': TranslateMock, + yargs: proxyquire('yargs', {}) + }), + mocks: { + Translate: TranslateMock, + translate: translateMock, + languages: languagesMock, + result: resultMock, + translation: translationMock + } + }; +} + +describe('translate:translate', function () { + describe('detectLanguage', function () { + it('should detect language', function () { + var sample = getSample(); + var callback = sinon.stub(); + + sample.program.detectLanguage(text, apiKey, callback); + + assert(sample.mocks.translate.detect.calledOnce, 'method called once'); + assert.equal(sample.mocks.translate.detect.firstCall.args.length, 2, 'method received 2 arguments'); + assert.equal(sample.mocks.translate.detect.firstCall.args[0], text, 'method received correct argument'); + assert(callback.calledOnce, 'callback called once'); + assert.equal(callback.firstCall.args.length, 2, 'callback received 2 arguments'); + assert.ifError(callback.firstCall.args[0], 'callback did not receive error'); + assert.strictEqual(callback.firstCall.args[1], sample.mocks.result, 'callback received result'); + assert(console.log.calledWith('Detected %s with confidence %d', 'English', sample.mocks.result.confidence)); + }); + + it('should handle error', function () { + var error = 'error'; + var sample = getSample(); + var callback = sinon.stub(); + sample.mocks.translate.detect = sinon.stub().callsArgWith(1, error); + + sample.program.detectLanguage(text, apiKey, callback); + + assert(callback.calledOnce, 'callback called once'); + assert.equal(callback.firstCall.args.length, 1, 'callback received 1 argument'); + assert(callback.firstCall.args[0], 'callback received error'); + assert.equal(callback.firstCall.args[0].message, error.message, 'error has correct message'); + }); + }); + + describe('listLanguages', function () { + it('should list languages', function () { + var sample = getSample(); + var callback = sinon.stub(); + + sample.program.listLanguages(apiKey, callback); + + assert(sample.mocks.translate.getLanguages.calledOnce, 'method called once'); + assert.equal(sample.mocks.translate.getLanguages.firstCall.args.length, 1, 'method received 1 argument'); + assert(callback.calledOnce, 'callback called once'); + assert.equal(callback.firstCall.args.length, 2, 'callback received 2 arguments'); + assert.ifError(callback.firstCall.args[0], 'callback did not receive error'); + assert.strictEqual(callback.firstCall.args[1], sample.mocks.languages, 'callback received result'); + assert(console.log.calledWith('Found %d language(s)!', sample.mocks.languages.length)); + }); + + it('should handle error', function () { + var error = 'error'; + var sample = getSample(); + var callback = sinon.stub(); + sample.mocks.translate.getLanguages = sinon.stub().callsArgWith(0, error); + + sample.program.listLanguages(apiKey, callback); + + assert(callback.calledOnce, 'callback called once'); + assert.equal(callback.firstCall.args.length, 1, 'callback received 1 argument'); + assert(callback.firstCall.args[0], 'callback received error'); + assert.equal(callback.firstCall.args[0].message, error.message, 'error has correct message'); + }); + }); + + describe('translateText', function () { + it('should translate text', function () { + var sample = getSample(); + var callback = sinon.stub(); + var options = { + text: text, + to: 'ru', + apiKey: apiKey + }; + + sample.program.translateText(options, callback); + + assert(sample.mocks.translate.translate.calledOnce, 'method called once'); + assert.equal(sample.mocks.translate.translate.firstCall.args.length, 3, 'method received 3 arguments'); + assert.equal(sample.mocks.translate.translate.firstCall.args[0], text, 'method received correct first argument'); + assert.deepEqual(sample.mocks.translate.translate.firstCall.args[1], { + to: 'ru', + from: undefined + }, 'method received correct second argument'); + assert(callback.calledOnce, 'callback called once'); + assert.equal(callback.firstCall.args.length, 2, 'callback received 2 arguments'); + assert.ifError(callback.firstCall.args[0], 'callback did not receive error'); + assert.strictEqual(callback.firstCall.args[1], sample.mocks.translation, 'callback received result'); + assert(console.log.calledWith('Translated text to %s', 'Russian')); + }); + + it('should handle error', function () { + var error = 'error'; + var sample = getSample(); + var callback = sinon.stub(); + var options = { + text: text, + to: 'ru', + apiKey: apiKey + }; + sample.mocks.translate.translate = sinon.stub().callsArgWith(2, error); + + sample.program.translateText(options, callback); + + assert(callback.calledOnce, 'callback called once'); + assert.equal(callback.firstCall.args.length, 1, 'callback received 1 argument'); + assert(callback.firstCall.args[0], 'callback received error'); + assert.equal(callback.firstCall.args[0].message, error.message, 'error has correct message'); + }); + }); + + describe('main', function () { + it('should call detectLanguage', function () { + var program = getSample().program; + + sinon.stub(program, 'detectLanguage'); + program.main(['detect', text, '-k', apiKey]); + assert(program.detectLanguage.calledOnce); + }); + + it('should call listLanguages', function () { + var program = getSample().program; + + sinon.stub(program, 'listLanguages'); + program.main(['list', '-k', apiKey]); + assert(program.listLanguages.calledOnce); + }); + + it('should call translateText', function () { + var program = getSample().program; + + sinon.stub(program, 'translateText'); + program.main(['translate', text, '-k', apiKey, '-t', 'ru']); + assert(program.translateText.calledOnce); + }); + }); +}); diff --git a/translate/translate.js b/translate/translate.js new file mode 100644 index 0000000000..d2be6ec0f8 --- /dev/null +++ b/translate/translate.js @@ -0,0 +1,170 @@ +// Copyright 2016, Google, Inc. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +'use strict'; + +// [START all] +// [START setup] +// By default, the client will authenticate using the service account file +// specified by the GOOGLE_APPLICATION_CREDENTIALS environment variable and use +// the project specified by the GCLOUD_PROJECT environment variable. See +// https://googlecloudplatform.github.io/gcloud-node/#/docs/google-cloud/latest/guides/authentication +var Translate = require('@google-cloud/translate'); + +// Helper library for language codes +var ISO6391 = require('iso-639-1'); +// [END setup] + +// [START detect_language] +/** + * Detect the language of the provided text. + * + * @param {string} text The text for which to detect the language. + * @param {string} apiKey Your Translate API key. + * @param {function} cb The callback function. + */ +function detectLanguage (text, apiKey, callback) { + // Instantiate a translate client + var translate = Translate({ + key: apiKey + }); + + // See https://googlecloudplatform.github.io/gcloud-node/#/docs/translate/latest/translate + translate.detect(text, function (err, result) { + if (err) { + return callback(err); + } + + console.log('Detected %s with confidence %d', ISO6391.getName(result.language), result.confidence); + return callback(null, result); + }); +} +// [END detect_language] + +// [START list_languages] +/** + * List all of the authenticated project's buckets. + * + * @param {string} apiKey Your Translate API key. + * @param {function} cb The callback function. + */ +function listLanguages (apiKey, callback) { + // Instantiate a translate client + var translate = Translate({ + key: apiKey + }); + + // See https://googlecloudplatform.github.io/gcloud-node/#/docs/translate/latest/translate + translate.getLanguages(function (err, languages) { + if (err) { + return callback(err); + } + + console.log('Found %d language(s)!', languages.length); + return callback(null, languages); + }); +} +// [END list_languages] + +// [START translate_text] +/** + * Translate the provided text. + * + * @param {object} options Configuration options. + * @param {string} options.text The text to translate. + * @param {string} options.from The language of the source text. + * @param {string} options.to The language to which to translate the text. + * @param {string} options.apiKey Your Translate API key. + * @param {function} cb The callback function. + */ +function translateText (options, callback) { + // Instantiate a translate client + var translate = Translate({ + key: options.apiKey + }); + + var config = { + from: options.from, + to: options.to + }; + + // See https://googlecloudplatform.github.io/gcloud-node/#/docs/translate/latest/translate + translate.translate(options.text, config, function (err, translation) { + if (err) { + return callback(err); + } + + console.log('Translated text to %s', ISO6391.getName(options.to)); + return callback(null, translation); + }); +} +// [END translate_text] +// [END all] + +// The command-line program +var cli = require('yargs'); + +var program = module.exports = { + detectLanguage: detectLanguage, + listLanguages: listLanguages, + translateText: translateText, + main: function (args) { + // Run the command-line program + cli.help().strict().parse(args).argv; + } +}; + +cli + .demand(1) + .command('detect ', 'Detect the language of the provided text', {}, function (options) { + program.detectLanguage(options.text, options.apiKey, console.log); + }) + .command('list', 'List available translation languages.', {}, function (options) { + program.listLanguages(options.apiKey, console.log); + }) + .command('translate ', 'Translate the provided text to the target language.', { + to: { + alias: 't', + demand: true, + requiresArg: true, + type: 'string', + description: 'The language to which to translate the text.' + }, + from: { + alias: 'f', + requiresArg: true, + type: 'string', + description: 'The language of the source text.' + } + }, function (options) { + program.translateText(options, console.log); + }) + .option('apiKey', { + alias: 'k', + global: true, + requiresArg: true, + default: process.env.TRANSLATE_API_KEY, + type: 'string', + description: 'Your Translate API key. Defaults to the value of the TRANSLATE_API_KEY environment variable.' + }) + .example('node $0 detect -k your-key "Hello world!"', 'Detect the language of "Hello world!".') + .example('node $0 list -k your-key', 'List available translation languages.') + .example('node $0 translate -k your-key --to ru "Good morning!"', 'Translate "Good morning!" to Russian, auto-detecting English.') + .example('node $0 translate -k your-key --to ru --from en "Good morning!"', 'Translate "Good morning!" to Russian from English.') + .wrap(100) + .recommendCommands() + .epilogue('For more information, see https://cloud.google.com/translate/docs'); + +if (module === require.main) { + program.main(process.argv.slice(2)); +}