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