diff --git a/src/ServiceBuilder.php b/src/ServiceBuilder.php index 81da31d92ee7..090b5d4ad8b2 100644 --- a/src/ServiceBuilder.php +++ b/src/ServiceBuilder.php @@ -268,10 +268,10 @@ public function vision(array $config = []) * more information at * [Google Translate docs](https://cloud.google.com/translate/docs/). * - * Please note that unlike most other Cloud Platform services Google - * Translate requires a public API access key and cannot currently be - * accessed with a service account or application default credentials. - * Follow the + * Please note that while Google Translate supports authentication via service + * account and application default credentials like other Cloud Platform APIs, + * it also supports authentication via a public API access key. If you wish to + * authenticate using an API key, follow the * [before you begin](https://cloud.google.com/translate/v2/translating-text-with-rest#before-you-begin) * instructions to learn how to generate a key. * diff --git a/src/Translate/Connection/Rest.php b/src/Translate/Connection/Rest.php index f418ff37962d..7990a7a8882a 100644 --- a/src/Translate/Connection/Rest.php +++ b/src/Translate/Connection/Rest.php @@ -31,7 +31,7 @@ class Rest implements ConnectionInterface use RestTrait; use UriTrait; - const BASE_URI = 'https://www.googleapis.com/language/translate/'; + const BASE_URI = 'https://translation.googleapis.com/language/translate/'; /** * @param array $config diff --git a/src/Translate/Connection/ServiceDefinition/translate-v2.json b/src/Translate/Connection/ServiceDefinition/translate-v2.json index ab4ff673a694..b2db0ec998a1 100644 --- a/src/Translate/Connection/ServiceDefinition/translate-v2.json +++ b/src/Translate/Connection/ServiceDefinition/translate-v2.json @@ -16,9 +16,9 @@ }, "documentationLink": "https://developers.google.com/translate/v2/using_rest", "protocol": "rest", - "baseUrl": "https://www.googleapis.com/language/translate/", + "baseUrl": "https://translation.googleapis.com/language/translate/", "basePath": "/language/translate/", - "rootUrl": "https://www.googleapis.com/", + "rootUrl": "https://translation.googleapis.com/", "servicePath": "language/translate/", "batchPath": "batch", "parameters": { @@ -221,6 +221,12 @@ "repeated": true, "location": "query" }, + "model": { + "type": "string", + "description": "The model to use", + "repeated": false, + "location": "query" + }, "format": { "type": "string", "description": "The format of the text", @@ -264,4 +270,4 @@ } } } -} \ No newline at end of file +} diff --git a/src/Translate/TranslateClient.php b/src/Translate/TranslateClient.php index 50eb98664e7a..e50d5f8a016a 100644 --- a/src/Translate/TranslateClient.php +++ b/src/Translate/TranslateClient.php @@ -31,9 +31,10 @@ * information at * [Google Translate docs](https://cloud.google.com/translate/docs/). * - * Please note that unlike most other Cloud Platform services Google Translate - * requires a public API access key and cannot currently be accessed with a - * service account or application default credentials. Follow the + * Please note that while Google Translate supports authentication via service + * account and application default credentials like other Cloud Platform APIs, + * it also supports authentication via a public API access key. If you wish to + * authenticate using an API key, follow the * [before you begin](https://cloud.google.com/translate/v2/translating-text-with-rest#before-you-begin) * instructions to learn how to generate a key. * @@ -41,9 +42,7 @@ * ``` * use Google\Cloud\ServiceBuilder; * - * $cloud = new ServiceBuilder([ - * 'key' => 'YOUR_KEY' - * ]); + * $cloud = new ServiceBuilder(); * * $translate = $cloud->translate(); * ``` @@ -52,9 +51,7 @@ * // TranslateClient can be instantiated directly. * use Google\Cloud\Translate\TranslateClient; * - * $translate = new TranslateClient([ - * 'key' => 'YOUR_KEY' - * ]); + * $translate = new TranslateClient(); * ``` */ class TranslateClient @@ -63,6 +60,8 @@ class TranslateClient const ENGLISH_LANGUAGE_CODE = 'en'; + const FULL_CONTROL_SCOPE = 'https://www.googleapis.com/auth/cloud-platform'; + /** * @var ConnectionInterface */ @@ -83,30 +82,53 @@ class TranslateClient * @type string $target The target language to assign to the client. * Must be a valid ISO 639-1 language code. **Defaults to** `"en"` * (English). + * @type int $retries Number of retries for a failed request. + * **Defaults to** `3`. + * @type string $projectId The project ID from the Google Developer's + * Console. + * @type CacheItemPoolInterface $authCache A cache used storing access + * tokens. **Defaults to** a simple in memory implementation. + * @type array $authCacheOptions Cache configuration options. + * @type callable $authHttpHandler A handler used to deliver Psr7 + * requests specifically for authentication. * @type callable $httpHandler A handler used to deliver Psr7 requests. * Only valid for requests sent over REST. + * @type string $keyFile The contents of the service account + * credentials .json file retrieved from the Google Developers + * Console. + * @type string $keyFilePath The full path to your service account + * credentials .json file retrieved from the Google Developers + * Console. * @type int $retries Number of retries for a failed request. * **Defaults to** `3`. + * @type array $scopes Scopes to be used for the request. * } * @throws \InvalidArgumentException */ public function __construct(array $config = []) { - if (!isset($config['key'])) { - throw new \InvalidArgumentException('A key is required.'); - } + $this->key = (isset($config['key'])) + ? $config['key'] + : null; - $this->key = $config['key']; $this->targetLanguage = isset($config['target']) ? $config['target'] : self::ENGLISH_LANGUAGE_CODE; + if (!isset($config['scopes'])) { + $config['scopes'] = [self::FULL_CONTROL_SCOPE]; + } + + if (!$this->key) { + $config = $this->configureAuthentication($config); + } else { + $config['shouldSignRequest'] = false; + } + unset($config['key']); unset($config['target']); - $this->connection = new Rest($config + [ - 'shouldSignRequest' => false - ]); + $this->connection = new Rest($config); } /** @@ -134,15 +156,20 @@ public function __construct(array $config = []) * @type string $format Indicates whether the string to be translated is * either plain-text or HTML. Acceptable values are `html` or * `text`. **Defaults to** `"html"`. + * @type string $model The model to use for the translation request. May + * be `nmt` or `base`. **Defaults to** an empty string. * } - * @return array A translation result including a `source` key containing + * @return array|null A translation result including a `source` key containing * the detected or provided langauge of the provided input, an * `input` key containing the original string, and a `text` key * containing the translated result. */ public function translate($string, array $options = []) { - return $this->translateBatch([$string], $options)[0]; + $res = $this->translateBatch([$string], $options); + if (count($res) > 0) { + return $res[0]; + } } /** @@ -175,6 +202,8 @@ public function translate($string, array $options = []) * @type string $format Indicates whether the string to be translated is * either plain-text or HTML. Acceptable values are `html` or * `text`. **Defaults to** `"html"`. + * @type string $model The model to use for the translation request. May + * be `nmt` or `base`. **Defaults to** an empty string. * } * @return array A set of translation results. Each result includes a * `source` key containing the detected or provided language of the @@ -183,24 +212,36 @@ public function translate($string, array $options = []) */ public function translateBatch(array $strings, array $options = []) { + $options += [ + 'model' => '', + ]; + $response = $this->connection->listTranslations($options + [ 'q' => $strings, 'key' => $this->key, - 'target' => $this->targetLanguage + 'target' => $this->targetLanguage, + 'model' => $options['model'] ]); $translations = []; - foreach ($response['data']['translations'] as $key => $translation) { - $source = isset($translation['detectedSourceLanguage']) - ? $translation['detectedSourceLanguage'] - : $options['source']; + if (isset($response['data']['translations'])) { + foreach ($response['data']['translations'] as $key => $translation) { + $source = isset($translation['detectedSourceLanguage']) + ? $translation['detectedSourceLanguage'] + : $options['source']; - $translations[] = [ - 'source' => $source, - 'input' => $strings[$key], - 'text' => $translation['translatedText'] - ]; + $model = (isset($translation['model'])) + ? $translation['model'] + : null; + + $translations[] = [ + 'source' => $source, + 'input' => $strings[$key], + 'text' => $translation['translatedText'], + 'model' => $model + ]; + } } return $translations; diff --git a/tests/unit/Translate/TranslateClientTest.php b/tests/unit/Translate/TranslateClientTest.php index 4178a570dca7..be27d058ca2b 100644 --- a/tests/unit/Translate/TranslateClientTest.php +++ b/tests/unit/Translate/TranslateClientTest.php @@ -36,12 +36,18 @@ public function setUp() $this->connection = $this->prophesize(ConnectionInterface::class); } - /** - * @expectedException \InvalidArgumentException - */ - public function testThrowsExceptionWithNoKey() + public function testWithNoKey() { - $client = new TranslateClient(); + $client = new TranslateTestClient(); + + $this->connection->listTranslations(Argument::that(function($args) { + if (!is_null($args['key'])) return false; + return true; + }))->shouldBeCalled()->willReturn([]); + + $client->setConnection($this->connection->reveal()); + + $client->translate('foo'); } public function testTranslate() @@ -50,7 +56,8 @@ public function testTranslate() $options = [ 'source' => $expected['source'], 'target' => 'de', - 'format' => 'text' + 'format' => 'text', + 'model' => 'base' ]; $this->connection ->listTranslations($options + [ @@ -71,6 +78,35 @@ public function testTranslate() $this->assertEquals($expected, $translation); } + public function testTranslateWithNmtModel() + { + $expected = $this->getTranslateExpectedData('translate', 'translated', 'en', 'nmt'); + + $options = [ + 'source' => $expected['source'], + 'target' => 'de', + 'format' => 'text', + 'model' => 'nmt' + ]; + $this->connection + ->listTranslations($options + [ + 'q' => [$expected['input']], + 'key' => $this->key + ]) + ->willReturn([ + 'data' => [ + 'translations' => [ + $this->getTranslateApiData($expected['text'], null, 'nmt') + ] + ] + ]) + ->shouldBeCalledTimes(1); + $this->client->setConnection($this->connection->reveal()); + $translation = $this->client->translate($expected['input'], $options); + + $this->assertEquals($expected, $translation); + } + public function testTranslateBatch() { $expected1 = $this->getTranslateExpectedData('translate', 'translated', 'en'); @@ -81,7 +117,8 @@ public function testTranslateBatch() ->listTranslations([ 'target' => $target, 'q' => $stringsToTranslate, - 'key' => $this->key + 'key' => $this->key, + 'model' => 'base' ]) ->willReturn([ 'data' => [ @@ -94,7 +131,7 @@ public function testTranslateBatch() ->shouldBeCalledTimes(1); $client = new TranslateTestClient(['key' => $this->key, 'target' => $target]); $client->setConnection($this->connection->reveal()); - $translations = $client->translateBatch($stringsToTranslate); + $translations = $client->translateBatch($stringsToTranslate, ['model' => 'base']); $this->assertEquals($expected1, $translations[0]); $this->assertEquals($expected2, $translations[1]); @@ -197,20 +234,22 @@ public function testLanguages() $this->assertEquals($expectedLanguage, $languages[0]); } - private function getTranslateApiData($translatedText, $source = null) + private function getTranslateApiData($translatedText, $source = null, $model = 'base') { return array_filter([ 'translatedText' => $translatedText, - 'detectedSourceLanguage' => $source + 'detectedSourceLanguage' => $source, + 'model' => $model ]); } - private function getTranslateExpectedData($textToTranslate, $translatedText, $source) + private function getTranslateExpectedData($textToTranslate, $translatedText, $source, $model = 'base') { return [ 'text' => $translatedText, 'source' => $source, - 'input' => $textToTranslate + 'input' => $textToTranslate, + 'model' => $model ]; }