1// Copyright 2016 Google LLC
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// Package translate is the v2 client for the Google Translation API.
16//
17// PLEASE NOTE: We recommend using the new v3 client for new projects:
18// https://cloud.google.com/go/translate/apiv3.
19//
20// See https://cloud.google.com/translation for details.
21package translate
22
23import (
24	"context"
25	"fmt"
26	"net/http"
27
28	"cloud.google.com/go/internal/version"
29	"golang.org/x/text/language"
30	"google.golang.org/api/option"
31	raw "google.golang.org/api/translate/v2"
32	htransport "google.golang.org/api/transport/http"
33)
34
35const userAgent = "gcloud-golang-translate/20161115"
36
37// Scope is the OAuth2 scope required by the Google Cloud Vision API.
38const Scope = raw.CloudPlatformScope
39
40// Client is a client for the translate API.
41type Client struct {
42	raw *raw.Service
43}
44
45const prodAddr = "https://translation.googleapis.com/language/translate/"
46
47// NewClient constructs a new Client that can perform Translation operations.
48//
49// You can find or create API key for your project from the Credentials page of
50// the Developers Console (console.developers.google.com).
51func NewClient(ctx context.Context, opts ...option.ClientOption) (*Client, error) {
52	o := []option.ClientOption{
53		option.WithEndpoint(prodAddr),
54		option.WithScopes(Scope),
55		option.WithUserAgent(userAgent),
56	}
57	o = append(o, opts...)
58	httpClient, endpoint, err := htransport.NewClient(ctx, o...)
59	if err != nil {
60		return nil, fmt.Errorf("dialing: %v", err)
61	}
62	rawService, err := raw.New(httpClient)
63	if err != nil {
64		return nil, fmt.Errorf("translate client: %v", err)
65	}
66	rawService.BasePath = endpoint
67	return &Client{raw: rawService}, nil
68}
69
70// Close closes any resources held by the client.
71// Close should be called when the client is no longer needed.
72// It need not be called at program exit.
73func (c *Client) Close() error { return nil }
74
75// Translate one or more strings of text from a source language to a target
76// language. All inputs must be in the same language.
77//
78// The target parameter supplies the language to translate to. The supported
79// languages are listed at
80// https://cloud.google.com/translation/v2/translate-reference#supported_languages.
81// You can also call the SupportedLanguages method.
82//
83// The returned Translations appear in the same order as the inputs.
84func (c *Client) Translate(ctx context.Context, inputs []string, target language.Tag, opts *Options) ([]Translation, error) {
85	call := c.raw.Translations.List(inputs, target.String()).Context(ctx)
86	setClientHeader(call.Header())
87	if opts != nil {
88		if s := opts.Source; s != language.Und {
89			call.Source(s.String())
90		}
91		if f := opts.Format; f != "" {
92			call.Format(string(f))
93		}
94		if m := opts.Model; m != "" {
95			call.Model(m)
96		}
97	}
98	res, err := call.Do()
99	if err != nil {
100		return nil, err
101	}
102	var ts []Translation
103	for _, t := range res.Translations {
104		var source language.Tag
105		if t.DetectedSourceLanguage != "" {
106			source, err = language.Parse(t.DetectedSourceLanguage)
107			if err != nil {
108				return nil, err
109			}
110		}
111		ts = append(ts, Translation{
112			Text:   t.TranslatedText,
113			Source: source,
114			Model:  t.Model,
115		})
116	}
117	return ts, nil
118}
119
120// Options contains options for Translate.
121type Options struct {
122	// Source is the language of the input strings. If empty, the service will
123	// attempt to identify the source language automatically and return it within
124	// the response.
125	Source language.Tag
126
127	// Format describes the format of the input texts. The choices are HTML or
128	// Text. The default is HTML.
129	Format Format
130
131	// The model to use for translation. The choices are "nmt" or "base". The
132	// default is "base".
133	Model string
134}
135
136// Format is the format of the input text. Used in Options.Format.
137type Format string
138
139// Constants for Options.Format.
140const (
141	HTML Format = "html"
142	Text Format = "text"
143)
144
145// Translation contains the results of translating a piece of text.
146type Translation struct {
147	// Text is the input text translated into the target language.
148	Text string
149
150	// Source is the detected language of the input text, if source was
151	// not supplied to Client.Translate. If source was supplied, this field
152	// will be empty.
153	Source language.Tag
154
155	// Model is the model that was used for translation.
156	// It may not match the model provided as an option to Client.Translate.
157	Model string
158}
159
160// DetectLanguage attempts to determine the language of the inputs. Each input
161// string may be in a different language.
162//
163// Each slice of Detections in the return value corresponds with one input
164// string. A slice of Detections holds multiple hypotheses for the language of
165// a single input string.
166func (c *Client) DetectLanguage(ctx context.Context, inputs []string) ([][]Detection, error) {
167	call := c.raw.Detections.List(inputs).Context(ctx)
168	setClientHeader(call.Header())
169	res, err := call.Do()
170	if err != nil {
171		return nil, err
172	}
173	var result [][]Detection
174	for _, raws := range res.Detections {
175		var ds []Detection
176		for _, rd := range raws {
177			tag, err := language.Parse(rd.Language)
178			if err != nil {
179				return nil, err
180			}
181			ds = append(ds, Detection{
182				Language:   tag,
183				Confidence: rd.Confidence,
184				IsReliable: rd.IsReliable,
185			})
186		}
187		result = append(result, ds)
188	}
189	return result, nil
190}
191
192// Detection represents information about a language detected in an input.
193type Detection struct {
194	// Language is the code of the language detected.
195	Language language.Tag
196
197	// Confidence is a number from 0 to 1, with higher numbers indicating more
198	// confidence in the detection.
199	Confidence float64
200
201	// IsReliable indicates whether the language detection result is reliable.
202	IsReliable bool
203}
204
205// SupportedLanguages returns a list of supported languages for translation.
206// The target parameter is the language to use to return localized, human
207// readable names of supported languages.
208func (c *Client) SupportedLanguages(ctx context.Context, target language.Tag) ([]Language, error) {
209	call := c.raw.Languages.List().Context(ctx).Target(target.String())
210	setClientHeader(call.Header())
211	res, err := call.Do()
212	if err != nil {
213		return nil, err
214	}
215	var ls []Language
216	for _, l := range res.Languages {
217		tag, err := language.Parse(l.Language)
218		if err != nil {
219			return nil, err
220		}
221		ls = append(ls, Language{
222			Name: l.Name,
223			Tag:  tag,
224		})
225	}
226	return ls, nil
227}
228
229// A Language describes a language supported for translation.
230type Language struct {
231	// Name is the human-readable name of the language.
232	Name string
233
234	// Tag is a standard code for the language.
235	Tag language.Tag
236}
237
238func setClientHeader(headers http.Header) {
239	headers.Set("x-goog-api-client", fmt.Sprintf("gl-go/%s gccl/%s", version.Go(), version.Repo))
240}
241