feat: translator
This commit is contained in:
20
translator/google/options.go
Normal file
20
translator/google/options.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package google
|
||||
|
||||
type Option func(*options)
|
||||
|
||||
type options struct {
|
||||
version string
|
||||
apiKey string
|
||||
}
|
||||
|
||||
func WithVersion(version string) Option {
|
||||
return func(o *options) {
|
||||
o.version = version
|
||||
}
|
||||
}
|
||||
|
||||
func WithApiKey(key string) Option {
|
||||
return func(o *options) {
|
||||
o.apiKey = key
|
||||
}
|
||||
}
|
||||
40
translator/google/translator.go
Normal file
40
translator/google/translator.go
Normal file
@@ -0,0 +1,40 @@
|
||||
package google
|
||||
|
||||
import (
|
||||
translateV2 "cloud.google.com/go/translate"
|
||||
translateV3 "cloud.google.com/go/translate/apiv3"
|
||||
)
|
||||
|
||||
type Translator struct {
|
||||
options *options
|
||||
|
||||
clientV2 *translateV2.Client
|
||||
clientV3 *translateV3.TranslationClient
|
||||
}
|
||||
|
||||
func NewTranslator(opts ...Option) *Translator {
|
||||
op := options{}
|
||||
for _, o := range opts {
|
||||
o(&op)
|
||||
}
|
||||
|
||||
return &Translator{
|
||||
options: &op,
|
||||
}
|
||||
}
|
||||
|
||||
func (t *Translator) Translate(source, sourceLang, targetLang string) (string, error) {
|
||||
switch t.options.version {
|
||||
default:
|
||||
fallthrough
|
||||
case "v1":
|
||||
return t.TranslateV1(source, sourceLang, targetLang)
|
||||
|
||||
case "v2":
|
||||
text, _, err := t.TranslateV2(source, sourceLang, targetLang)
|
||||
return text, err
|
||||
|
||||
case "v3":
|
||||
return t.TranslateV3(source, sourceLang, targetLang)
|
||||
}
|
||||
}
|
||||
98
translator/google/translator_test.go
Normal file
98
translator/google/translator_test.go
Normal file
@@ -0,0 +1,98 @@
|
||||
package google
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"golang.org/x/text/language"
|
||||
)
|
||||
|
||||
func TestTranslateV1(t *testing.T) {
|
||||
translator := NewTranslator(
|
||||
WithVersion("v1"),
|
||||
)
|
||||
assert.NotNil(t, translator)
|
||||
|
||||
const text string = `Hello, World!`
|
||||
// you can use "auto" for source language
|
||||
// so, translator will detect language
|
||||
result, err := translator.TranslateV1(text, "en", "es")
|
||||
assert.Nil(t, err)
|
||||
fmt.Println(result)
|
||||
// Output: "Hola, Mundo!"
|
||||
|
||||
result, err = translator.TranslateV1(text, "en", "zh-CN")
|
||||
assert.Nil(t, err)
|
||||
fmt.Println(result)
|
||||
|
||||
result, err = translator.TranslateV1(text, "en", "lo")
|
||||
assert.Nil(t, err)
|
||||
fmt.Println(result)
|
||||
|
||||
result, err = translator.TranslateV1(text, "en", "my")
|
||||
assert.Nil(t, err)
|
||||
fmt.Println(result)
|
||||
}
|
||||
|
||||
func TestTranslateV2(t *testing.T) {
|
||||
translator := NewTranslator(
|
||||
WithVersion("v2"),
|
||||
WithApiKey("apikey"),
|
||||
)
|
||||
assert.NotNil(t, translator)
|
||||
|
||||
const text string = `Hello, World!`
|
||||
|
||||
result, _, err := translator.TranslateV2(text, "en", "es")
|
||||
assert.Nil(t, err)
|
||||
fmt.Println(result)
|
||||
// Output: "Hola, Mundo!"
|
||||
|
||||
result, _, err = translator.TranslateV2(text, "en", "zh-CN")
|
||||
assert.Nil(t, err)
|
||||
fmt.Println(result)
|
||||
|
||||
result, _, err = translator.TranslateV2(text, "en", "lo")
|
||||
assert.Nil(t, err)
|
||||
fmt.Println(result)
|
||||
|
||||
result, _, err = translator.TranslateV2(text, "en", "my")
|
||||
assert.Nil(t, err)
|
||||
fmt.Println(result)
|
||||
}
|
||||
|
||||
func TestTranslateV3(t *testing.T) {
|
||||
translator := NewTranslator(
|
||||
WithVersion("v3"),
|
||||
WithApiKey("apikey"),
|
||||
)
|
||||
assert.NotNil(t, translator)
|
||||
|
||||
const text string = `Hello, World!`
|
||||
// you can use "auto" for source language
|
||||
// so, translator will detect language
|
||||
result, err := translator.TranslateV3(text, "en", "es")
|
||||
assert.Nil(t, err)
|
||||
fmt.Println(result)
|
||||
// Output: "Hola, Mundo!"
|
||||
|
||||
result, err = translator.TranslateV3(text, "en", "zh-CN")
|
||||
assert.Nil(t, err)
|
||||
fmt.Println(result)
|
||||
|
||||
result, err = translator.TranslateV3(text, "en", "lo")
|
||||
assert.Nil(t, err)
|
||||
fmt.Println(result)
|
||||
|
||||
result, err = translator.TranslateV3(text, "en", "my")
|
||||
assert.Nil(t, err)
|
||||
fmt.Println(result)
|
||||
}
|
||||
|
||||
func TestLanguageParse(t *testing.T) {
|
||||
lang, _ := language.Parse("en")
|
||||
assert.Equal(t, lang, language.English)
|
||||
fmt.Println(lang)
|
||||
fmt.Println(language.English.String())
|
||||
}
|
||||
9
translator/google/utils.go
Normal file
9
translator/google/utils.go
Normal file
@@ -0,0 +1,9 @@
|
||||
package google
|
||||
|
||||
import "net/url"
|
||||
|
||||
// javascript "encodeURI()"
|
||||
// so we embed js to our golang program
|
||||
func encodeURI(s string) string {
|
||||
return url.QueryEscape(s)
|
||||
}
|
||||
55
translator/google/v1.go
Normal file
55
translator/google/v1.go
Normal file
@@ -0,0 +1,55 @@
|
||||
package google
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func (t *Translator) TranslateV1(source, sourceLang, targetLang string) (string, error) {
|
||||
var text []string
|
||||
var result []interface{}
|
||||
|
||||
encodedSource := encodeURI(source)
|
||||
uri := "https://translate.googleapis.com/translate_a/single?client=gtx&sl=" +
|
||||
sourceLang + "&tl=" + targetLang + "&dt=t&q=" + encodedSource
|
||||
|
||||
r, err := http.Get(uri)
|
||||
if err != nil {
|
||||
return "err", errors.New("error getting translate.googleapis.com")
|
||||
}
|
||||
defer r.Body.Close()
|
||||
|
||||
body, err := io.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
return "err", errors.New("error reading response body")
|
||||
}
|
||||
|
||||
bReq := strings.Contains(string(body), `<title>Error 400 (Bad Request)`)
|
||||
if bReq {
|
||||
return "err", errors.New("error 400 (Bad Request)")
|
||||
}
|
||||
|
||||
err = json.Unmarshal(body, &result)
|
||||
if err != nil {
|
||||
return "err", errors.New("error unmarshalling data")
|
||||
}
|
||||
|
||||
if len(result) > 0 {
|
||||
inner := result[0]
|
||||
for _, slice := range inner.([]interface{}) {
|
||||
for _, translatedText := range slice.([]interface{}) {
|
||||
text = append(text, fmt.Sprintf("%v", translatedText))
|
||||
break
|
||||
}
|
||||
}
|
||||
cText := strings.Join(text, "")
|
||||
|
||||
return cText, nil
|
||||
} else {
|
||||
return "err", errors.New("no translated data in response")
|
||||
}
|
||||
}
|
||||
33
translator/google/v2.go
Normal file
33
translator/google/v2.go
Normal file
@@ -0,0 +1,33 @@
|
||||
package google
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
translateV2 "cloud.google.com/go/translate"
|
||||
"golang.org/x/text/language"
|
||||
"google.golang.org/api/option"
|
||||
)
|
||||
|
||||
func (t *Translator) TranslateV2(source, _, targetLanguage string) (string, language.Tag, error) {
|
||||
ctx := context.Background()
|
||||
|
||||
lang, err := language.Parse(targetLanguage)
|
||||
if err != nil {
|
||||
return "", language.English, err
|
||||
}
|
||||
|
||||
if t.clientV2 == nil {
|
||||
client, err := translateV2.NewClient(ctx, option.WithAPIKey(t.options.apiKey))
|
||||
if err != nil {
|
||||
return "", language.English, err
|
||||
}
|
||||
t.clientV2 = client
|
||||
}
|
||||
|
||||
resp, err := t.clientV2.Translate(ctx, []string{source}, lang, nil)
|
||||
if err != nil {
|
||||
return "", language.English, err
|
||||
}
|
||||
|
||||
return resp[0].Text, resp[0].Source, nil
|
||||
}
|
||||
40
translator/google/v3.go
Normal file
40
translator/google/v3.go
Normal file
@@ -0,0 +1,40 @@
|
||||
package google
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
translateV3 "cloud.google.com/go/translate/apiv3"
|
||||
"cloud.google.com/go/translate/apiv3/translatepb"
|
||||
"google.golang.org/api/option"
|
||||
)
|
||||
|
||||
func (t *Translator) TranslateV3(source, sourceLang, targetLang string) (string, error) {
|
||||
ctx := context.Background()
|
||||
|
||||
if t.clientV3 == nil {
|
||||
client, err := translateV3.NewTranslationClient(ctx, option.WithAPIKey(t.options.apiKey))
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("NewTranslationClient: %w", err)
|
||||
}
|
||||
t.clientV3 = client
|
||||
}
|
||||
|
||||
req := &translatepb.TranslateTextRequest{
|
||||
SourceLanguageCode: sourceLang,
|
||||
TargetLanguageCode: targetLang,
|
||||
MimeType: "text/plain", // Mime types: "text/plain", "text/html"
|
||||
Contents: []string{source},
|
||||
}
|
||||
|
||||
resp, err := t.clientV3.TranslateText(ctx, req)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("TranslateText: %w", err)
|
||||
}
|
||||
|
||||
if len(resp.GetTranslations()) != 1 {
|
||||
return "", fmt.Errorf("TranslateText: %w", err)
|
||||
}
|
||||
|
||||
return resp.GetTranslations()[0].GetTranslatedText(), nil
|
||||
}
|
||||
Reference in New Issue
Block a user