Skip to content

Commit 941e68a

Browse files
committed
Basic implementation of Translate API.
Test forthcoming after API discussion.
1 parent 6769364 commit 941e68a

File tree

6 files changed

+378
-0
lines changed

6 files changed

+378
-0
lines changed

docs/index.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,14 @@
122122
monitoring-timeseries
123123
monitoring-label
124124

125+
.. toctree::
126+
:maxdepth: 0
127+
:hidden:
128+
:caption: Translate
129+
130+
translate-usage
131+
Client <translate-client>
132+
125133
.. toctree::
126134
:maxdepth: 0
127135
:hidden:

docs/translate-client.rst

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
Translate Client
2+
================
3+
4+
.. automodule:: gcloud.translate.client
5+
:members:
6+
:undoc-members:
7+
:show-inheritance:
8+
9+
Connection
10+
~~~~~~~~~~
11+
12+
.. automodule:: gcloud.translate.connection
13+
:members:
14+
:undoc-members:
15+
:show-inheritance:

docs/translate-usage.rst

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
Using the API
2+
=============
3+
4+
With `Google Translate`_, you can dynamically translate text
5+
between thousands of language pairs. The Google Translate API
6+
lets websites and programs integrate with Google Translate
7+
programmatically. Google Translate API is available as a
8+
paid service. See the `Pricing`_ and `FAQ`_ pages for details.
9+
10+
Authentication / Configuration
11+
------------------------------
12+
13+
- Use :class:`~gcloud.translate.client.Client` objects to configure
14+
your applications.
15+
16+
- :class:`~gcloud.translate.client.Client` objects hold both a ``key``
17+
and a connection to the Translate service.
18+
19+
- **An API key is required for Translate.** See
20+
`Identifying your application to Google`_ for details. This is
21+
significantly different than the other clients in ``gcloud-python``.
22+
23+
Methods
24+
-------
25+
26+
To create a client:
27+
28+
.. code::
29+
30+
>>> from gcloud import translate
31+
>>> client = translate.Client('my-api-key')
32+
33+
The API has three supported methods:
34+
:meth:`~gcloud.translate.client.Client.get_languages`,
35+
:meth:`~gcloud.translate.client.Client.detect_language` and
36+
:meth:`~gcloud.translate.client.Client.translate`.
37+
38+
To get a list of languages supported by Google Translate
39+
40+
.. code::
41+
42+
>>> from gcloud import translate
43+
>>> client = translate.Client('my-api-key')
44+
>>> client.get_languages('en')
45+
[
46+
{
47+
'language': 'af',
48+
'name': 'Afrikaans',
49+
},
50+
...
51+
]
52+
53+
To detect the language that some given text is written in:
54+
55+
.. code::
56+
57+
>>> from gcloud import translate
58+
>>> client = translate.Client('my-api-key')
59+
>>> client.detect_language(['Me llamo', 'I am'])
60+
[
61+
{
62+
'confidence': 0.25830904,
63+
'input': 'Me llamo',
64+
'language': 'es',
65+
}, {
66+
'confidence': 0.17112699,
67+
'input': 'I am',
68+
'language': 'en',
69+
},
70+
]
71+
72+
To translate text:
73+
74+
.. code::
75+
76+
>>> from gcloud import translate
77+
>>> client = translate.Client('my-api-key')
78+
>>> client.translate('koszula')
79+
[
80+
{
81+
'translatedText': 'shirt',
82+
'detectedSourceLanguage': 'pl',
83+
'input': 'koszula',
84+
},
85+
]
86+
87+
or to use a non-default target language:
88+
89+
.. code::
90+
91+
>>> from gcloud import translate
92+
>>> client = translate.Client('my-api-key')
93+
>>> client.translate(['Me llamo Jeff', 'My name is Jeff'], 'de')
94+
[
95+
{
96+
'translatedText': 'Mein Name ist Jeff',
97+
'detectedSourceLanguage': 'es',
98+
'input': 'Me llamo Jeff',
99+
}, {
100+
'translatedText': 'Mein Name ist Jeff',
101+
'detectedSourceLanguage': 'en',
102+
'input': 'My name is Jeff',
103+
},
104+
]
105+
106+
.. _Google Translate: https://cloud.google.com/translate
107+
.. _Pricing: https://cloud.google.com/translate/v2/pricing.html
108+
.. _FAQ: https://cloud.google.com/translate/v2/faq.html
109+
.. _Identifying your application to Google: https://cloud.google.com/translate/v2/using_rest#auth

gcloud/translate/__init__.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Copyright 2015 Google Inc. All rights reserved.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
"""Google Cloud Translate API wrapper."""
16+
17+
from gcloud.translate.client import Client
18+
from gcloud.translate.connection import Connection

gcloud/translate/client.py

Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
# Copyright 2016 Google Inc. All rights reserved.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
"""Client for interacting with the Google Cloud Translate API."""
16+
17+
18+
import httplib2
19+
import six
20+
21+
from gcloud._helpers import _to_bytes
22+
from gcloud.translate.connection import Connection
23+
24+
25+
ENGLISH_ISO_639 = 'en'
26+
"""ISO 639-1 language code for English."""
27+
28+
29+
def _zip_assert(values1, values2):
30+
"""Zips two iterables and asserts they are the same length.
31+
32+
:type values1: list
33+
:param values1: Iterable (with a length) to be zipped.
34+
35+
:type values2: list
36+
:param values2: Iterable (with a length) to be zipped.
37+
38+
:rtype: iterator
39+
:returns: Zip iterator for each pair of values.
40+
:raises: :class:`ValueError <exceptions.ValueError>` if the number of
41+
values in each list is not the same.
42+
"""
43+
if len(values1) != len(values2):
44+
raise ValueError('Expected iterations to have same length',
45+
values1, values2)
46+
47+
return six.moves.zip(values1, values2)
48+
49+
50+
class Client(object):
51+
"""Client to bundle configuration needed for API requests.
52+
53+
:type key: string
54+
:param key: The key used to send with requests as a query
55+
parameter.
56+
57+
:type http: :class:`httplib2.Http` or class that defines ``request()``.
58+
:param http: (Optional) HTTP object to make requests. If not
59+
passed, an :class:`httplib.Http` object is created.
60+
"""
61+
62+
def __init__(self, key, http=None):
63+
self.key = key
64+
if http is None:
65+
http = httplib2.Http()
66+
self.connection = Connection(http=http)
67+
68+
def get_languages(self, target_language=None):
69+
"""Get list of supported languages for translation.
70+
71+
Response
72+
73+
See: https://cloud.google.com/translate/v2/\
74+
discovering-supported-languages-with-rest
75+
76+
:type target_language: str
77+
:param target_language: (Optional) The language used to localize
78+
returned language names.
79+
80+
:rtype: list
81+
:returns: List of dictionaries. Each dictionary contains a supported
82+
ISO 639-1 language code (using the dictionary key
83+
``language``). If ``target_language`` is passed, each
84+
dictionary will also contain the name of each supported
85+
language (localized to the target language).
86+
"""
87+
query_params = {'key': self.key}
88+
if target_language is not None:
89+
query_params['target'] = target_language
90+
response = self.connection.api_request(
91+
method='GET', path='/languages', query_params=query_params)
92+
return response.get('data', {}).get('languages', ())
93+
94+
def detect_language(self, values):
95+
"""Detect the language of a string or list of strings.
96+
97+
See: https://cloud.google.com/translate/v2/\
98+
detecting-language-with-rest
99+
100+
:type values: str or list
101+
:param values: String or list of strings that will have
102+
language detected.
103+
104+
:rtype: list
105+
:returns: A list of dictionaries for each queried value. Each
106+
dictionary typically contains four keys (though not
107+
all will be present in all cases)
108+
109+
* ``confidence``: The confidence in language detection, a
110+
float between 0 and 1.
111+
* ``input``: The corresponding input value.
112+
* ``language``: The detected language (as an ISO 639-1
113+
language code).
114+
115+
If multiple languages are detected for a given input, then
116+
the there will be a list of dictionaries (instead of a single
117+
dictionary) for that queried value.
118+
:raises: :class:`ValueError <exceptions.ValueError>` if the number of
119+
detections is not equal to the number of values.
120+
"""
121+
if isinstance(values, six.string_types):
122+
values = [values]
123+
query_params = [('key', self.key)]
124+
query_params.extend(('q', _to_bytes(value, 'utf-8'))
125+
for value in values)
126+
response = self.connection.api_request(
127+
method='GET', path='/detect', query_params=query_params)
128+
detections = response.get('data', {}).get('detections', ())
129+
130+
if len(values) != len(detections):
131+
raise ValueError('Expected same number of values and detections',
132+
values, detections)
133+
134+
for index, value in enumerate(values):
135+
for detection in detections[index]:
136+
detection['input'] = value
137+
# The ``isReliable`` field is deprecated.
138+
detection.pop('isReliable', None)
139+
# If there was only one detected match, replace the
140+
# list of matches with the single match. Empirically, even
141+
# clearly ambiguous text like "no" only returns a single
142+
# detection.
143+
if len(detections[index]) == 1:
144+
detections[index] = detections[index][0]
145+
146+
return detections
147+
148+
def translate(self, values, target_language=ENGLISH_ISO_639,
149+
format_=None, source_language=None,
150+
customization_ids=()):
151+
"""Translate a string or list of strings.
152+
153+
See: https://cloud.google.com/translate/v2/\
154+
translating-text-with-rest
155+
156+
:type values: str or list
157+
:param values: String or list of strings that will have
158+
language detected.
159+
160+
:type target_language: str
161+
:param target_language: The language to translate results into. This
162+
is required by the API and defaults to
163+
:data:`ENGLISH_ISO_639`.
164+
165+
:type format_: str
166+
:param format_: (Optional) One of ``text`` or ``html``, to specify
167+
if the input text is plain text or HTML.
168+
169+
:type source_language: str
170+
:param source_language: (Optional) The language of the text to
171+
be translated.
172+
173+
:type customization_ids: list
174+
:param customization_ids: (Optional) List of customization IDs for
175+
translation. Sets the ``cid`` parameter
176+
in the query.
177+
"""
178+
if isinstance(values, six.string_types):
179+
values = [values]
180+
if isinstance(customization_ids, six.string_types):
181+
customization_ids = [customization_ids]
182+
183+
query_params = [('key', self.key), ('target', target_language)]
184+
query_params.extend(('q', _to_bytes(value, 'utf-8'))
185+
for value in values)
186+
query_params.extend(('cid', cid) for cid in customization_ids)
187+
if format_ is not None:
188+
query_params.append(('format', format_))
189+
if source_language is not None:
190+
query_params.append(('source', source_language))
191+
192+
response = self.connection.api_request(
193+
method='GET', path='', query_params=query_params)
194+
195+
translations = response.get('data', {}).get('translations', ())
196+
for value, translation in _zip_assert(values, translations):
197+
translation['input'] = value
198+
return translations

gcloud/translate/connection.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# Copyright 2016 Google Inc. All rights reserved.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
"""Create / interact with Google Cloud Translate connections."""
16+
17+
from gcloud import connection as base_connection
18+
19+
20+
class Connection(base_connection.JSONConnection):
21+
"""A connection to Google Cloud Translate via the JSON REST API."""
22+
23+
API_BASE_URL = 'https://www.googleapis.com'
24+
"""The base of the API call URL."""
25+
26+
API_VERSION = 'v2'
27+
"""The version of the API, used in building the API call's URL."""
28+
29+
API_URL_TEMPLATE = '{api_base_url}/language/translate/{api_version}{path}'
30+
"""A template for the URL of a particular API call."""

0 commit comments

Comments
 (0)