v2/index.js

"use strict";
// Copyright 2017, Google LLC All rights reserved.
//
// 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.
Object.defineProperty(exports, "__esModule", { value: true });
const common_1 = require("@google-cloud/common");
const promisify_1 = require("@google-cloud/promisify");
const arrify = require("arrify");
const extend = require("extend");
const is = require("is");
const isHtml = require('is-html');
const PKG = require('../../../package.json');
/**
 * @typedef {object} ClientConfig
 * @memberof v2
 * @property {string} [projectId] The project ID from the Google Developer's
 *     Console, e.g. 'grape-spaceship-123'. We will also check the environment
 *     variable `GCLOUD_PROJECT` for your project ID. If your app is running in
 *     an environment which supports {@link
 * https://cloud.google.com/docs/authentication/production#providing_credentials_to_your_application
 * Application Default Credentials}, your project ID will be detected
 * automatically.
 * @property {string} [key] An API key. You should prefer using a Service
 *     Account key file instead of an API key.
 * @property {string} [keyFilename] Full path to the a .json, .pem, or .p12 key
 *     downloaded from the Google Developers Console. If you provide a path to a
 *     JSON file, the `projectId` option above is not necessary. NOTE: .pem and
 *     .p12 require you to specify the `email` option as well.
 * @property {string} [email] Account email address. Required when using a .pem
 *     or .p12 keyFilename.
 * @property {object} [credentials] Credentials object.
 * @property {string} [credentials.client_email]
 * @property {string} [credentials.private_key]
 * @property {boolean} [autoRetry=true] Automatically retry requests if the
 *     response is related to rate limits or certain intermittent server errors.
 *     We will exponentially backoff subsequent requests by default.
 * @property {number} [maxRetries=3] Maximum number of automatic retries
 *     attempted before returning the error.
 * @property {Constructor} [promise] Custom promise module to use instead of
 *     native Promises.
 */
/**
 * With [Google Translate](https://cloud.google.com/translate), you can
 * dynamically translate text between thousands of language pairs.
 *
 * The Google Cloud Translation API lets websites and programs integrate with
 * Google Translate programmatically.
 *
 * @class
 * @memberof v2
 *
 * @see [Getting Started]{@link https://cloud.google.com/translate/v2/getting_started}
 * @see [Identifying your application to Google]{@link https://cloud.google.com/translate/v2/using_rest#auth}
 *
 * @param {ClientConfig} [options] Configuration options.
 *
 * @example
 * //-
 * // <h3>Custom Translation API</h3>
 * //
 * // The environment variable, `GOOGLE_CLOUD_TRANSLATE_ENDPOINT`, is honored as
 * // a custom backend which our library will send requests to.
 * //-
 *
 * @example <caption>include:samples/quickstart.js</caption>
 * region_tag:translate_quickstart
 * Full quickstart example:
 */
class Translate extends common_1.Service {
    constructor(options = {}) {
        options.apiEndpoint = options.apiEndpoint || 'translation.googleapis.com';
        let baseUrl = `https://${options.apiEndpoint}/language/translate/v2`;
        if (process.env.GOOGLE_CLOUD_TRANSLATE_ENDPOINT) {
            baseUrl = process.env.GOOGLE_CLOUD_TRANSLATE_ENDPOINT.replace(/\/+$/, '');
        }
        const config = {
            apiEndpoint: options.apiEndpoint,
            baseUrl,
            scopes: ['https://www.googleapis.com/auth/cloud-platform'],
            packageJson: require('../../../package.json'),
            projectIdRequired: false,
        };
        super(config, options);
        this.options = options || {};
        if (this.options.key) {
            this.key = this.options.key;
        }
    }
    /**
     * @typedef {object} DetectResult
     * @memberof v2
     * @property {string} 0.language The language code matched from the input.
     * @property {number} [0.confidence] A float 0 - 1. The higher the number, the
     *     higher the confidence in language detection. Note, this is not always
     *     returned from the API.
     * @property {object} 1 The full API response.
     */
    /**
     * @callback DetectCallback
     * @memberof v2
     * @param {?Error} err Request error, if any.
     * @param {object|object[]} results The detection results.
     * @param {string} results.language The language code matched from the input.
     * @param {number} [results.confidence] A float 0 - 1. The higher the number, the
     *     higher the confidence in language detection. Note, this is not always
     *     returned from the API.
     * @param {object} apiResponse The full API response.
     */
    /**
     * Detect the language used in a string or multiple strings.
     *
     * @see [Detect Language]{@link https://cloud.google.com/translate/v2/using_rest#detect-language}
     *
     * @param {string|string[]} input - The source string input.
     * @param {DetectCallback} [callback] Callback function.
     * @returns {Promise<DetectResponse>}
     *
     * @example
     * const {Translate} = require('@google-cloud/translate');
     *
     * const translate = new Translate();
     *
     * //-
     * // Detect the language from a single string input.
     * //-
     * translate.detect('Hello', (err, results) => {
     *   if (!err) {
     *     // results = {
     *     //   language: 'en',
     *     //   confidence: 1,
     *     //   input: 'Hello'
     *     // }
     *   }
     * });
     *
     * //-
     * // Detect the languages used in multiple strings. Note that the results are
     * // now provided as an array.
     * //-
     * translate.detect([
     *   'Hello',
     *   'Hola'
     * ], (err, results) => {
     *   if (!err) {
     *     // results = [
     *     //   {
     *     //     language: 'en',
     *     //     confidence: 1,
     *     //     input: 'Hello'
     *     //   },
     *     //   {
     *     //     language: 'es',
     *     //     confidence: 1,
     *     //     input: 'Hola'
     *     //   }
     *     // ]
     *   }
     * });
     *
     * //-
     * // If the callback is omitted, we'll return a Promise.
     * //-
     * translate.detect('Hello').then((data) => {
     *   const results = data[0];
     *   const apiResponse = data[2];
     * });
     *
     * @example <caption>include:samples/translate.js</caption>
     * region_tag:translate_detect_language
     * Here's a full example:
     */
    detect(input, callback) {
        const inputIsArray = Array.isArray(input);
        input = arrify(input);
        this.request({
            method: 'POST',
            uri: '/detect',
            json: {
                q: input,
            },
        }, (err, resp) => {
            if (err) {
                callback(err, null, resp);
                return;
            }
            let results = resp.data.detections.map((detection, index) => {
                const result = extend({}, detection[0], {
                    input: input[index],
                });
                // Deprecated.
                // tslint:disable-next-line no-any
                delete result.isReliable;
                return result;
            });
            if (input.length === 1 && !inputIsArray) {
                results = results[0];
            }
            callback(null, results, resp);
        });
    }
    /**
     * @typedef {object} LanguageResult
     * @memberof v2
     * @property {string} code The [ISO 639-1](https://en.wikipedia.org/wiki/ISO_639-1)
     *     language code.
     * @property {string} name The language name. This can be translated into your
     *     preferred language with the `target` option.
     */
    /**
     * @callback GetLanguagesCallback
     * @memberof v2
     * @param {?Error} err Request error, if any.
     * @param {object[]} results The languages supported by the API.
     * @param {string} results.code The [ISO 639-1](https://en.wikipedia.org/wiki/ISO_639-1)
     *     language code.
     * @param {string} results.name The language name. This can be translated into your
     *     preferred language with the `target` option.
     * @param {object} apiResponse The full API response.
     */
    /**
     * Get an array of all supported languages.
     *
     * @see [Discovering Supported Languages]{@link https://cloud.google.com/translate/v2/discovering-supported-languages-with-rest}
     *
     * @param {string} [target] Get the language names in a language other than
     *     English.
     * @param {GetLanguagesCallback} [callback] Callback function.
     * @returns {Promise<GetLanguagesResponse>}
     *
     * @example <caption>include:samples/translate.js</caption>
     * region_tag:translate_list_codes
     * Gets the language names in English:
     *
     * @example <caption>include:samples/translate.js</caption>
     * region_tag:translate_list_language_names
     * Gets the language names in a language other than English:
     */
    getLanguages(targetOrCallback, callback) {
        let target;
        if (is.fn(targetOrCallback)) {
            callback = targetOrCallback;
            target = 'en';
        }
        else {
            target = targetOrCallback;
        }
        const reqOpts = {
            uri: '/languages',
            useQuerystring: true,
            qs: {},
        };
        if (target && is.string(target)) {
            reqOpts.qs.target = target;
        }
        this.request(reqOpts, (err, resp) => {
            if (err) {
                callback(err, null, resp);
                return;
            }
            const languages = resp.data.languages.map((language) => {
                return {
                    code: language.language,
                    name: language.name,
                };
            });
            callback(null, languages, resp);
        });
    }
    /**
     * Translate request options.
     *
     * @typedef {object} TranslateRequest
     * @memberof v2
     * @property {string} [format] Set the text's format as `html` or `text`.
     *     If not provided, we will try to auto-detect if the text given is HTML.
     * If not, we set the format as `text`.
     * @property {string} [from] The ISO 639-1 language code the source input
     *     is written in.
     * @property {string} [model] Set the model type requested for this
     *     translation. Please refer to the upstream documentation for possible
     *     values.
     * @property {string} to The ISO 639-1 language code to translate the
     *     input to.
     */
    /**
     * @callback TranslateCallback
     * @memberof v2
     * @param {?Error} err Request error, if any.
     * @param {object|object[]} translations If a single string input was given, a
     *     single translation is given. Otherwise, it is an array of translations.
     * @param {object} apiResponse The full API response.
     */
    /**
     * Translate a string or multiple strings into another language.
     *
     * @see [Translate Text](https://cloud.google.com/translate/v2/using_rest#Translate)
     *
     * @throws {Error} If `options` is provided as an object without a `to`
     *     property.
     *
     * @param {string|string[]} input The source string input.
     * @param {string|TranslateRequest} [options] If a string, it is interpreted as the
     *     target ISO 639-1 language code to translate the source input to. (e.g.
     *     `en` for English). If an object, you may also specify the source
     *     language.
     * @param {TranslateCallback} [callback] Callback function.
     * @returns {Promise<TranslateResponse>}
     *
     * @example
     * //-
     * // Pass a string and a language code to get the translation.
     * //-
     * translate.translate('Hello', 'es', (err, translation) => {
     *   if (!err) {
     *     // translation = 'Hola'
     *   }
     * });
     *
     * //-
     * // The source language is auto-detected by default. To manually set it,
     * // provide an object.
     * //-
     * const options = {
     *   from: 'en',
     *   to: 'es'
     * };
     *
     * translate.translate('Hello', options, (err, translation) => {
     *   if (!err) {
     *     // translation = 'Hola'
     *   }
     * });
     *
     * //-
     * // Translate multiple strings of input. Note that the results are
     * // now provided as an array.
     * //-
     * const input = [
     *   'Hello',
     *   'How are you today?'
     * ];
     *
     * translate.translate(input, 'es', (err, translations) => {
     *   if (!err) {
     *     // translations = [
     *     //   'Hola',
     *     //   'Como estas hoy?'
     *     // ]
     *   }
     * });
     *
     * //-
     * // If the callback is omitted, we'll return a Promise.
     * //-
     * translate.translate('Hello', 'es').then((data) => {
     *   const translation = data[0];
     *   const apiResponse = data[1];
     * });
     *
     * @example <caption>include:samples/translate.js</caption>
     * region_tag:translate_translate_text
     * Full translation example:
     *
     * @example <caption>include:samples/translate.js</caption>
     * region_tag:translate_text_with_model
     * Translation using the premium model:
     */
    translate(inputs, optionsOrTo, callback) {
        const inputIsArray = Array.isArray(inputs);
        const input = arrify(inputs);
        let options = {};
        if (typeof optionsOrTo === 'object') {
            options = optionsOrTo;
        }
        else if (typeof optionsOrTo === 'string') {
            options = { to: optionsOrTo };
        }
        // tslint:disable-next-line no-any
        const body = {
            q: input,
            format: options.format || (isHtml(input[0]) ? 'html' : 'text'),
        };
        if (is.string(options)) {
            body.target = options;
        }
        else {
            if (options.from) {
                body.source = options.from;
            }
            if (options.to) {
                body.target = options.to;
            }
            if (options.model) {
                body.model = options.model;
            }
        }
        if (!body.target) {
            throw new Error('A target language is required to perform a translation.');
        }
        this.request({
            method: 'POST',
            uri: '',
            json: body,
        }, (err, resp) => {
            if (err) {
                callback(err, null, resp);
                return;
            }
            let translations = resp.data.translations.map((x) => x.translatedText);
            if (body.q.length === 1 && !inputIsArray) {
                translations = translations[0];
            }
            callback(err, translations, resp);
        });
    }
    /**
     * A custom request implementation. Requests to this API may optionally use an
     * API key for an application, not a bearer token from a service account. This
     * means it is possible to skip the `makeAuthenticatedRequest` portion of the
     * typical request lifecycle, and manually authenticate the request here.
     *
     * @private
     *
     * @param {object} reqOpts - Request options that are passed to `request`.
     * @param {function} callback - The callback function passed to `request`.
     */
    request(reqOpts, callback) {
        if (!this.key) {
            super.request(reqOpts, callback);
            return;
        }
        reqOpts.uri = this.baseUrl + reqOpts.uri;
        reqOpts = extend(true, {}, reqOpts, {
            qs: {
                key: this.key,
            },
            headers: {
                'User-Agent': common_1.util.getUserAgentFromPackageJson(PKG),
            },
        });
        common_1.util.makeRequest(reqOpts, this.options, callback);
    }
}
exports.Translate = Translate;
/*! Developer Documentation
 *
 * All async methods (except for streams) will return a Promise in the event
 * that a callback is omitted.
 */
promisify_1.promisifyAll(Translate, { exclude: ['request'] });
//# sourceMappingURL=index.js.map