Skip to content

Commit 6027026

Browse files
authored
Translate API updates (#1393)
* Update translate endpoint, use POST and support credentials * Add support for the model option * Add integration test for Translate's model * Switch back to GET for translate and update docs
1 parent a187b11 commit 6027026

10 files changed

Lines changed: 135 additions & 47 deletions

File tree

README.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -610,9 +610,10 @@ Google Translate
610610
#### Preview
611611
612612
Here's a snippet showing a simple usage example. The example shows how to detect the language of
613-
some text and how to translate some text. The example assumes that the `GOOGLE_API_KEY` is set and
614-
contains a valid API key. Alternatively, you can use the `apiKey(String)` setter in
615-
`TranslateOptions.Builder` to set the API key. Complete source code can be found at
613+
some text and how to translate some text. The example assumes that either default application
614+
credentials or a valid api key are available. An api key stored in the `GOOGLE_API_KEY` environment
615+
variable will be automatically detected. Alternatively, you can use the `apiKey(String)` setter in
616+
`TranslateOptions.Builder`. Complete source code can be found at
616617
[DetectLanguageAndTranslate.java](./google-cloud-examples/src/main/java/com/google/cloud/examples/translate/snippets/DetectLanguageAndTranslate.java).
617618
618619
```java

google-cloud-examples/src/main/java/com/google/cloud/examples/translate/snippets/DetectLanguageAndTranslate.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,8 @@ public class DetectLanguageAndTranslate {
3636

3737
public static void main(String... args) {
3838
// Create a service object
39-
// API key is read from the GOOGLE_API_KEY environment variable
39+
// Default application credentials or an API key from the GOOGLE_API_KEY environment variable
40+
// are used to authenticate requests
4041
Translate translate = TranslateOptions.getDefaultInstance().getService();
4142

4243
// Detect the language of some text

google-cloud-translate/README.md

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -64,20 +64,25 @@ Getting Started
6464
---------------
6565
#### Prerequisites
6666
For this tutorial, you need a [Google Developers Console](https://console.developers.google.com/)
67-
project with "Translate API" enabled via the console's API Manager. You will also need a to enable
68-
billing via the [Google Developers Console](https://console.developers.google.com/) project and to
69-
retrieve an API key. See [Translate quickstart](https://cloud.google.com/translate/v2/quickstart)
70-
for more details.
67+
project with "Translate API" enabled via the console's API Manager. You also need to enable
68+
billing via the [Google Developers Console](https://console.developers.google.com/).
69+
70+
Finally, you must set up the local development environment by
71+
[installing the Google Cloud SDK](https://cloud.google.com/sdk/) and running the following command
72+
in command line: `gcloud auth application-default login`. Alternatively, you can authenticate
73+
Translate requests using an API key. See
74+
[Translate quickstart](https://cloud.google.com/translate/v2/quickstart) for more details.
7175

7276
#### Installation and setup
7377
You'll need to obtain the `google-cloud-translate` library. See the [Quickstart](#quickstart)
7478
section to add `google-cloud-translate` as a dependency in your code.
7579

7680
#### Creating an authorized service object
77-
To make authenticated requests to Google Translates, you must create a service object with an API
78-
key. By default, API key is looked for in the `GOOGLE_API_KEY` environment variable. Once the API
79-
key is set, you can make API calls by invoking methods on the Translate service object. To create a
80-
service object, given that `GOOGLE_API_KEY` is set, use the following code:
81+
To make authenticated requests to Google Translate, you must create a service object with
82+
credentials or with an API key. The simplest way to authenticate is to use
83+
[Application Default Credentials](https://developers.google.com/identity/protocols/application-default-credentials).
84+
These credentials are automatically inferred from your environment, so you only need the following
85+
code to create your service object:
8186

8287
```java
8388
import com.google.cloud.translate.Translate;
@@ -86,7 +91,11 @@ import com.google.cloud.translate.TranslateOptions;
8691
Translate translate = TranslateOptions.getDefaultInstance().getService();
8792
```
8893

89-
Or you can explicitly set the API key as follows:
94+
Notice that this code can be also used with an API key. By default, an API key is looked for in the
95+
`GOOGLE_API_KEY` environment variable. Once the API key is set, you can make API calls by invoking
96+
methods on the Translate service created via `TranslateOptions.getDefaultInstance().getService()`.
97+
98+
You can also explicitly set the API key as follows:
9099
```java
91100
Translate translate = TranslateOptions.newBuilder().setApiKey("myKey").getService();
92101
```
@@ -131,8 +140,8 @@ Translation translation = translate.translate(
131140

132141
In
133142
[DetectLanguageAndTranslate.java](../google-cloud-examples/src/main/java/com/google/cloud/examples/translate/snippets/DetectLanguageAndTranslate.java)
134-
we put together all the code shown above into one program. The program assumes that a valid api key
135-
is available.
143+
we put together all the code shown above into one program. The program assumes that either default
144+
application credentials or a valid api key are available.
136145

137146
Troubleshooting
138147
---------------

google-cloud-translate/src/main/java/com/google/cloud/translate/Translate.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,19 @@ public static TranslateOption sourceLanguage(String sourceLanguage) {
8181
public static TranslateOption targetLanguage(String targetLanguage) {
8282
return new TranslateOption(TranslateRpc.Option.TARGET_LANGUAGE, targetLanguage);
8383
}
84+
85+
/**
86+
* Sets the language translation model. You can use this parameter to take advantage of Neural
87+
* Machine Translation. Possible values are {@code base} and {@code nmt}. Google Translate could
88+
* use a different model to translate your text, use {@link Translation#getModel()} to know
89+
* which model was used for translation. Please notice that you must be whitelisted to use this
90+
* option, otherwise translation will fail.
91+
*
92+
* @param model the language translation model
93+
*/
94+
public static TranslateOption model(String model) {
95+
return new TranslateOption(TranslateRpc.Option.MODEL, model);
96+
}
8497
}
8598

8699
/**
@@ -170,6 +183,8 @@ public static TranslateOption targetLanguage(String targetLanguage) {
170183
* @param texts the texts to translate
171184
* @return a list of objects containing information on the language translation, one for each
172185
* provided text, in order.
186+
* @throws TranslateException upon failure or if {@link TranslateOption#model(String)} is used by
187+
* a non-whitelisted user
173188
*/
174189
List<Translation> translate(List<String> texts, TranslateOption... options);
175190

@@ -189,6 +204,8 @@ public static TranslateOption targetLanguage(String targetLanguage) {
189204
*
190205
* @param text the text to translate
191206
* @return an object containing information on the language translation
207+
* @throws TranslateException upon failure or if {@link TranslateOption#model(String)} is used by
208+
* a non-whitelisted user
192209
*/
193210
Translation translate(String text, TranslateOption... options);
194211
}

google-cloud-translate/src/main/java/com/google/cloud/translate/TranslateOptions.java

Lines changed: 11 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -30,14 +30,17 @@
3030

3131
import java.util.List;
3232
import java.util.Locale;
33+
import java.util.Objects;
3334
import java.util.Set;
3435

3536
public class TranslateOptions extends
3637
HttpServiceOptions<Translate, TranslateRpc, TranslateOptions> {
3738

3839
private static final long serialVersionUID = -572597134540398216L;
40+
private static final String DEFAULT_HOST = "https://translation.googleapis.com";
3941
private static final String API_KEY_ENV_NAME = "GOOGLE_API_KEY";
40-
private static final Set<String> SCOPES = ImmutableSet.of();
42+
private static final Set<String> SCOPES =
43+
ImmutableSet.of("https://www.googleapis.com/auth/cloud-platform");
4144

4245
private final String apiKey;
4346
private final String targetLanguage;
@@ -97,17 +100,6 @@ public Builder setProjectId(String projectId) {
97100
return self();
98101
}
99102

100-
/**
101-
* Sets the service authentication credentials. Setting credentials has no impact on the
102-
* {@link Translate} service.
103-
*
104-
* @return the builder
105-
*/
106-
public Builder setCredentials(Credentials credentials) {
107-
super.setCredentials(credentials);
108-
return self();
109-
}
110-
111103
/**
112104
* Sets the API key used to issue requets. If not set, the API key is looked for in the
113105
* {@code GOOGLE_API_KEY} environment variable. For instructions on how to get an API key see
@@ -158,18 +150,13 @@ public Builder setTargetLanguage(String targetLanguage) {
158150

159151
@Override
160152
public TranslateOptions build() {
161-
// Auth credentials are not used by Translate
162-
setCredentials(NoCredentials.getInstance());
163153
return new TranslateOptions(this);
164154
}
165155
}
166156

167157
private TranslateOptions(Builder builder) {
168158
super(TranslateFactory.class, TranslateRpcFactory.class, builder);
169159
this.apiKey = builder.apiKey != null ? builder.apiKey : getDefaultApiKey();
170-
checkArgument(this.apiKey != null,
171-
"An API key is required for this service but could not be determined from the builder "
172-
+ "or the environment. Please set an API key using the builder.");
173160
this.targetLanguage = firstNonNull(builder.targetLanguage, Locale.ENGLISH.getLanguage());
174161
}
175162

@@ -193,6 +180,11 @@ protected Set<String> getScopes() {
193180
return SCOPES;
194181
}
195182

183+
@Override
184+
protected String getDefaultHost() {
185+
return DEFAULT_HOST;
186+
}
187+
196188
@Deprecated
197189
protected String defaultApiKey() {
198190
return getDefaultApiKey();
@@ -250,8 +242,8 @@ public boolean equals(Object obj) {
250242
}
251243
TranslateOptions options = (TranslateOptions) obj;
252244
return baseEquals(options)
253-
&& apiKey.equals(options.apiKey)
254-
&& targetLanguage.equals(options.targetLanguage);
245+
&& Objects.equals(apiKey, options.apiKey)
246+
&& Objects.equals(targetLanguage, options.targetLanguage);
255247
}
256248

257249
/**

google-cloud-translate/src/main/java/com/google/cloud/translate/Translation.java

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import com.google.common.base.MoreObjects;
2222

2323
import java.io.Serializable;
24+
import java.util.List;
2425
import java.util.Objects;
2526

2627
/**
@@ -43,10 +44,12 @@ public Translation apply(TranslationsResource translationPb) {
4344

4445
private final String translatedText;
4546
private final String sourceLanguage;
47+
private final String model;
4648

47-
private Translation(String translatedText, String sourceLanguage) {
49+
private Translation(String translatedText, String sourceLanguage, String model) {
4850
this.translatedText = translatedText;
4951
this.sourceLanguage = sourceLanguage;
52+
this.model = model;
5053
}
5154

5255
/**
@@ -81,6 +84,18 @@ public String getSourceLanguage() {
8184
return sourceLanguage;
8285
}
8386

87+
/**
88+
* Returns the translation model used to translate the text. This value is only available if
89+
* {@link Translate.TranslateOption#model(String)} was passed to
90+
* {@link Translate#translate(List, Translate.TranslateOption...)}.
91+
*
92+
* <p>Please notice that you must be whitelisted to use the
93+
* {@link Translate.TranslateOption#model(String)} option, otherwise translation will fail.
94+
*/
95+
public String getModel() {
96+
return model;
97+
}
98+
8499
@Override
85100
public String toString() {
86101
return MoreObjects.toStringHelper(this)
@@ -108,7 +123,8 @@ public final boolean equals(Object obj) {
108123
}
109124

110125
static Translation fromPb(TranslationsResource translationPb) {
126+
// TODO remove get("model") as soon as REST apiary supports model
111127
return new Translation(translationPb.getTranslatedText(),
112-
translationPb.getDetectedSourceLanguage());
128+
translationPb.getDetectedSourceLanguage(), (String) translationPb.get("model"));
113129
}
114130
}

google-cloud-translate/src/main/java/com/google/cloud/translate/spi/DefaultTranslateRpc.java

Lines changed: 42 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,21 +16,28 @@
1616

1717
package com.google.cloud.translate.spi;
1818

19+
import static com.google.cloud.translate.spi.TranslateRpc.Option.MODEL;
1920
import static com.google.cloud.translate.spi.TranslateRpc.Option.SOURCE_LANGUAGE;
2021
import static com.google.cloud.translate.spi.TranslateRpc.Option.TARGET_LANGUAGE;
2122
import static com.google.common.base.MoreObjects.firstNonNull;
2223

24+
import com.google.api.client.http.GenericUrl;
25+
import com.google.api.client.http.HttpRequest;
2326
import com.google.api.client.http.HttpRequestInitializer;
2427
import com.google.api.client.http.HttpTransport;
28+
import com.google.api.client.http.json.JsonHttpContent;
2529
import com.google.api.client.json.jackson.JacksonFactory;
2630
import com.google.api.services.translate.Translate;
31+
import com.google.api.services.translate.model.DetectionsListResponse;
2732
import com.google.api.services.translate.model.DetectionsResourceItems;
33+
import com.google.api.services.translate.model.LanguagesListResponse;
2834
import com.google.api.services.translate.model.LanguagesResource;
2935
import com.google.api.services.translate.model.TranslationsResource;
3036
import com.google.cloud.translate.TranslateException;
3137
import com.google.cloud.translate.TranslateOptions;
3238
import com.google.common.base.Function;
3339
import com.google.common.collect.ImmutableList;
40+
import com.google.common.collect.ImmutableMap;
3441
import com.google.common.collect.Lists;
3542

3643
import java.io.IOException;
@@ -56,11 +63,27 @@ private static TranslateException translate(IOException exception) {
5663
return new TranslateException(exception);
5764
}
5865

66+
private GenericUrl buildTargetUrl(String path) {
67+
GenericUrl genericUrl = new GenericUrl(translate.getBaseUrl() + "v2/" + path);
68+
if (options.getApiKey() != null) {
69+
genericUrl.put("key", options.getApiKey());
70+
}
71+
return genericUrl;
72+
}
73+
5974
@Override
6075
public List<List<DetectionsResourceItems>> detect(List<String> texts) {
6176
try {
77+
Map<String, ?> content = ImmutableMap.of("q", texts);
78+
HttpRequest httpRequest = translate.getRequestFactory()
79+
.buildPostRequest(buildTargetUrl("detect"),
80+
new JsonHttpContent(translate.getJsonFactory(), content))
81+
.setParser(translate.getObjectParser());
6282
List<List<DetectionsResourceItems>> detections =
63-
translate.detections().list(texts).setKey(options.getApiKey()).execute().getDetections();
83+
httpRequest.execute().parseAs(DetectionsListResponse.class).getDetections();
84+
// TODO use REST apiary as soon as it supports POST
85+
// List<List<DetectionsResourceItems>> detections =
86+
// translate.detections().list(texts).setKey(options.getApiKey()).execute().getDetections();
6487
return detections != null ? detections : ImmutableList.<List<DetectionsResourceItems>>of();
6588
} catch (IOException ex) {
6689
throw translate(ex);
@@ -70,12 +93,21 @@ public List<List<DetectionsResourceItems>> detect(List<String> texts) {
7093
@Override
7194
public List<LanguagesResource> listSupportedLanguages(Map<Option, ?> optionMap) {
7295
try {
73-
List<LanguagesResource> languages = translate.languages()
74-
.list()
75-
.setKey(options.getApiKey())
76-
.setTarget(
77-
firstNonNull(TARGET_LANGUAGE.getString(optionMap), options.getTargetLanguage()))
78-
.execute().getLanguages();
96+
Map<String, ?> content = ImmutableMap.of("target",
97+
firstNonNull(TARGET_LANGUAGE.getString(optionMap), options.getTargetLanguage()));
98+
HttpRequest httpRequest = translate.getRequestFactory()
99+
.buildPostRequest(buildTargetUrl("languages"),
100+
new JsonHttpContent(translate.getJsonFactory(), content))
101+
.setParser(translate.getObjectParser());
102+
List<LanguagesResource> languages =
103+
httpRequest.execute().parseAs(LanguagesListResponse.class).getLanguages();
104+
// TODO use REST apiary as soon as it supports POST
105+
// List<LanguagesResource> languages = translate.languages()
106+
// .list()
107+
// .setKey(options.getApiKey())
108+
// .setTarget(
109+
// firstNonNull(TARGET_LANGUAGE.getString(optionMap), options.getTargetLanguage()))
110+
// .execute().getLanguages();
79111
return languages != null ? languages : ImmutableList.<LanguagesResource>of();
80112
} catch (IOException ex) {
81113
throw translate(ex);
@@ -85,6 +117,8 @@ public List<LanguagesResource> listSupportedLanguages(Map<Option, ?> optionMap)
85117
@Override
86118
public List<TranslationsResource> translate(List<String> texts, Map<Option, ?> optionMap) {
87119
try {
120+
// TODO use POST as soon as usage of "model" correctly reports an error in non-whitelisted
121+
// projects
88122
String targetLanguage =
89123
firstNonNull(TARGET_LANGUAGE.getString(optionMap), options.getTargetLanguage());
90124
final String sourceLanguage = SOURCE_LANGUAGE.getString(optionMap);
@@ -93,6 +127,7 @@ public List<TranslationsResource> translate(List<String> texts, Map<Option, ?> o
93127
.list(texts, targetLanguage)
94128
.setSource(sourceLanguage)
95129
.setKey(options.getApiKey())
130+
.set("model", MODEL.getString(optionMap))
96131
.execute()
97132
.getTranslations();
98133
return Lists.transform(

google-cloud-translate/src/main/java/com/google/cloud/translate/spi/TranslateRpc.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ public interface TranslateRpc {
2727

2828
enum Option {
2929
SOURCE_LANGUAGE("source"),
30-
TARGET_LANGUAGE("target");
30+
TARGET_LANGUAGE("target"),
31+
MODEL("model");
3132

3233
private final String value;
3334

google-cloud-translate/src/test/java/com/google/cloud/translate/TranslateImplTest.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -86,9 +86,11 @@ public class TranslateImplTest {
8686
TranslateOption.targetLanguage("en");
8787
private static final TranslateOption SOURCE_LANGUAGE_OPTION =
8888
TranslateOption.sourceLanguage("de");
89+
private static final TranslateOption MODEL_OPTION = TranslateOption.model("nmt");
8990
private static final Map<TranslateRpc.Option, ?> TRANSLATE_OPTIONS = ImmutableMap.of(
9091
TranslateRpc.Option.TARGET_LANGUAGE, TARGET_LANGUAGE_OPTION.getValue(),
91-
TranslateRpc.Option.SOURCE_LANGUAGE, SOURCE_LANGUAGE_OPTION.getValue());
92+
TranslateRpc.Option.SOURCE_LANGUAGE, SOURCE_LANGUAGE_OPTION.getValue(),
93+
TranslateRpc.Option.MODEL, "nmt");
9294

9395
private TranslateOptions options;
9496
private TranslateRpcFactory rpcFactoryMock;
@@ -285,7 +287,7 @@ public void testTranslateWithOptions() {
285287
EasyMock.replay(translateRpcMock);
286288
initializeService();
287289
assertEquals(TRANSLATION2,
288-
translate.translate(text, TARGET_LANGUAGE_OPTION, SOURCE_LANGUAGE_OPTION));
290+
translate.translate(text, TARGET_LANGUAGE_OPTION, SOURCE_LANGUAGE_OPTION, MODEL_OPTION));
289291
}
290292

291293
@Test
@@ -309,7 +311,7 @@ public void testTranslateListWithOptions() {
309311
EasyMock.replay(translateRpcMock);
310312
initializeService();
311313
assertEquals(ImmutableList.of(TRANSLATION2),
312-
translate.translate(texts, TARGET_LANGUAGE_OPTION, SOURCE_LANGUAGE_OPTION));
314+
translate.translate(texts, TARGET_LANGUAGE_OPTION, SOURCE_LANGUAGE_OPTION, MODEL_OPTION));
313315
}
314316

315317
@Test

0 commit comments

Comments
 (0)