Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CCC-3108 Migrate to version 3 of the Translator Text API before April… #4

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions acceptance_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ func microsoftTranslator(t *testing.T) translator.Translator {
}

func testTranslate(t *testing.T, translator translator.Translator) {
translation, err := translator.Translate("Hello World!", "en", "de")
translation, err := translator.Translate("Hello World!", "en", "de", "3.0")

if err != nil {
t.Errorf("Unexpected error: %s", err.Error())
Expand All @@ -72,7 +72,7 @@ func testTranslate(t *testing.T, translator translator.Translator) {
}

func testDetect(t *testing.T, translator translator.Translator) {
languageCode, err := translator.Detect("¿cómo está?")
languageCode, err := translator.Detect("¿cómo está?", "3.0")

if err != nil {
t.Errorf("Unexpected error: %s", err.Error())
Expand Down
6 changes: 3 additions & 3 deletions example/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ func helloWorld(t translator.Translator) {

translations := make([]<-chan string, len(languages))
for i, language := range languages {
translations[i] = translate(t, "Hello World!", "en", language)
translations[i] = translate(t, "Hello World!", "en", language, "")
}

for n := range mergeChannels(translations) {
Expand All @@ -39,10 +39,10 @@ func helloWorld(t translator.Translator) {

// Starts a go routine to translate text for a particular language. Returns a channel that will be
// used to send either the translation or an error string if something went wrong.
func translate(t translator.Translator, text, from string, to translator.Language) <-chan string {
func translate(t translator.Translator, text, from string, to translator.Language, version string) <-chan string {
out := make(chan string)
go func() {
translation, err := t.Translate(text, from, to.Code)
translation, err := t.Translate(text, from, to.Code, version)
if err != nil {
out <- fmt.Sprintf("Error during translation for %s: %s", to.Name, err.Error())
} else {
Expand Down
4 changes: 2 additions & 2 deletions google/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,10 @@ func (a *api) Languages() ([]translator.Language, error) {
return a.lp.languages()
}

func (a *api) Detect(text string) (string, error) {
func (a *api) Detect(text, version string) (string, error) {
return a.lp.detect(text)
}

func (a *api) Translate(text, from, to string) (string, error) {
func (a *api) Translate(text, from, to, version string) (string, error) {
return a.tp.translate(text, from, to)
}
12 changes: 6 additions & 6 deletions google/api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,21 @@ import "github.com/st3v/translator"

type mockLanguageProvider struct {
languagesFunc func() ([]translator.Language, error)
detectFunc func(text string) (string, error)
detectFunc func(text, version string) (string, error)
}

func (m *mockLanguageProvider) languages() ([]translator.Language, error) {
return m.languagesFunc()
}

func (m *mockLanguageProvider) detect(text string) (string, error) {
return m.detectFunc(text)
func (m *mockLanguageProvider) detect(text, version string) (string, error) {
return m.detectFunc(text, version)
}

type mockTranslationProvider struct {
translateFunc func(text, from, to string) (string, error)
translateFunc func(text, from, to, version string) (string, error)
}

func (m *mockTranslationProvider) translate(text, from, to string) (string, error) {
return m.translateFunc(text, from, to)
func (m *mockTranslationProvider) translate(text, from, to, version string) (string, error) {
return m.translateFunc(text, from, to, version)
}
8 changes: 4 additions & 4 deletions microsoft/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,14 @@ func NewTranslator(subscriptionKey string) translator.Translator {
}
}

func (a *api) Translate(text, from, to string) (string, error) {
return a.translationProvider.Translate(text, from, to)
func (a *api) Translate(text, from, to, version string) (string, error) {
return a.translationProvider.Translate(text, from, to, version)
}

func (a *api) Languages() ([]translator.Language, error) {
return a.languageCatalog.Languages()
}

func (a *api) Detect(text string) (string, error) {
return a.translationProvider.Detect(text)
func (a *api) Detect(text, version string) (string, error) {
return a.translationProvider.Detect(text, version)
}
6 changes: 4 additions & 2 deletions microsoft/api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,13 @@ func TestAPITranslate(t *testing.T) {
expectedTranslation := "My English is under all pig."
from := "de"
to := "en"
version := "3.0"

api := &api{
translationProvider: newMockTranslationProvider(original, from, to, expectedTranslation, t),
}

actualTranslation, err := api.Translate(original, from, to)
actualTranslation, err := api.Translate(original, from, to, version)
if err != nil {
t.Errorf("Unexpected error: %s", err.Error())
}
Expand Down Expand Up @@ -72,12 +73,13 @@ func TestAPIDetect(t *testing.T) {
text := "Mein Englisch ist unter aller Sau."
expectedLanguage := "de"
from := "de"
version := "3.0"

api := &api{
translationProvider: newMockTranslationProvider(text, from, "", "", t),
}

actualLanguage, err := api.Detect(text)
actualLanguage, err := api.Detect(text, version)
if err != nil {
t.Errorf("Unexpected error: %s", err.Error())
}
Expand Down
12 changes: 9 additions & 3 deletions microsoft/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@ package microsoft

const (
authURL = "https://api.cognitive.microsoft.com/sts/v1.0/issueToken"
serviceURL = "https://api.microsofttranslator.com/v2/Http.svc/"
serviceURL = "https://api.cognitive.microsofttranslator.com/"
translationURL = serviceURL + "Translate"
detectURL = serviceURL + "Detect"
languageNamesURL = serviceURL + "GetLanguageNames"
languageCodesURL = serviceURL + "GetLanguagesForTranslate"
languageNamesURL = serviceURL + "Languages"
languageCodesURL = serviceURL + "Languages"
apiVersion = "3.0"
)

// The Router provides necessary URLs to communicate with
Expand All @@ -17,6 +18,7 @@ type Router interface {
DetectURL() string
LanguageNamesURL() string
LanguageCodesURL() string
ApiVersion() string
}

type router struct{}
Expand Down Expand Up @@ -44,3 +46,7 @@ func (r *router) LanguageNamesURL() string {
func (r *router) LanguageCodesURL() string {
return languageCodesURL
}

func (r *router) ApiVersion() string {
return apiVersion
}
11 changes: 8 additions & 3 deletions microsoft/router_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ func TestRouterAuthURL(t *testing.T) {
func TestRouterTranslationURL(t *testing.T) {
router := newRouter()

expectedURL := "https://api.microsofttranslator.com/v2/Http.svc/Translate"
expectedURL := "https://api.cognitive.microsofttranslator.com/Translate"

actualURL := router.TranslationURL()

Expand All @@ -29,7 +29,7 @@ func TestRouterTranslationURL(t *testing.T) {
func TestRouterLanguageNamesURL(t *testing.T) {
router := newRouter()

expectedURL := "https://api.microsofttranslator.com/v2/Http.svc/GetLanguageNames"
expectedURL := "https://api.cognitive.microsofttranslator.com/Languages"

actualURL := router.LanguageNamesURL()

Expand All @@ -41,7 +41,7 @@ func TestRouterLanguageNamesURL(t *testing.T) {
func TestRouterLanguageCodesURL(t *testing.T) {
router := newRouter()

expectedURL := "https://api.microsofttranslator.com/v2/Http.svc/GetLanguagesForTranslate"
expectedURL := "https://api.cognitive.microsofttranslator.com/Languages"

actualURL := router.LanguageCodesURL()

Expand All @@ -66,6 +66,7 @@ type mockRouter struct {
languageNamesURL string
languageCodesURL string
detectURL string
apiVersion string
}

func (m *mockRouter) AuthURL() string {
Expand All @@ -87,3 +88,7 @@ func (m *mockRouter) LanguageCodesURL() string {
func (m *mockRouter) DetectURL() string {
return m.detectURL
}

func (m *mockRouter) ApiVersion() string {
return m.apiVersion
}
103 changes: 82 additions & 21 deletions microsoft/translation_provider.go
Original file line number Diff line number Diff line change
@@ -1,84 +1,145 @@
package microsoft

import (
"encoding/xml"
"bytes"
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"net/url"

"github.com/st3v/tracerr"
"github.com/st3v/translator/http"
"io/ioutil"
"net/url"
)

// The TranslationProvider communicates with Microsoft's
// API to provide a translation for a given text.
type TranslationProvider interface {
Translate(text, from, to string) (string, error)
Detect(text string) (string, error)
Translate(text, from, to, version string) (string, error)
Detect(text, version string) (string, error)
}

type translationProvider struct {
router Router
httpClient http.Client
}

type Request []struct {
Text string `json:"Text"`
}

type Translate []struct {
Translations []Translations `json:"translations"`
}
type Translations struct {
Text string `json:"text"`
To string `json:"to"`
}

type Detect []struct {
Language string `json:"language"`
Score float64 `json:"score"`
IsTranslationSupported bool `json:"isTranslationSupported"`
IsTransliterationSupported bool `json:"isTransliterationSupported"`
Alternatives []Alternatives `json:"alternatives"`
}

type Alternatives struct {
Language string `json:"language"`
Score float64 `json:"score"`
IsTranslationSupported bool `json:"isTranslationSupported"`
IsTransliterationSupported bool `json:"isTransliterationSupported"`
}

type Errors struct {
Error Error `json:"error"`
}
type Error struct {
Code int `json:"code"`
Message string `json:"message"`
}

func newTranslationProvider(authenticator http.Authenticator, router Router) TranslationProvider {
return &translationProvider{
router: router,
httpClient: http.NewClient(authenticator),
}
}

func (p *translationProvider) Translate(text, from, to string) (string, error) {
func (p *translationProvider) Translate(text, from, to, version string) (string, error) {
apiVer := version

if apiVer == "" {
apiVer = p.router.ApiVersion()
}
request := make(Request, 1)
request[0].Text = text
b, _ := json.Marshal(&request)
uri := fmt.Sprintf(
"%s?text=%s&from=%s&to=%s",
"%s?api-version=%s&from=%s&to=%s&textType=html",
p.router.TranslationURL(),
url.QueryEscape(text),
apiVer,
url.QueryEscape(from),
url.QueryEscape(to))

response, err := p.httpClient.SendRequest("GET", uri, nil, "text/plain")
response, err := p.httpClient.SendRequest("POST", uri, bytes.NewBuffer(b), "application/json")
if err != nil {
return "", tracerr.Wrap(err)
}

body, err := ioutil.ReadAll(response.Body)

defer response.Body.Close()
if err != nil {
return "", tracerr.Wrap(err)
}

translation := &xmlString{}
err = xml.Unmarshal(body, &translation)
errMsg := Errors{}
err = json.Unmarshal(body, &errMsg)
translation := Translate{}

if err != nil {
return "", tracerr.Wrap(err)
err = json.Unmarshal(body, &translation)
if err != nil {
return "", tracerr.Wrap(err)
}
} else {
return "", errors.New(errMsg.Error.Message)
}

return translation.Value, nil
return translation[0].Translations[0].Text, nil
}

func (p *translationProvider) Detect(text string) (string, error) {
func (p *translationProvider) Detect(text, version string) (string, error) {
apiVer := version

if apiVer == "" {
apiVer = p.router.ApiVersion()
}
request := make(Request, 1)
request[0].Text = text
b, _ := json.Marshal(&request)
uri := fmt.Sprintf(
"%s?text=%s",
"%s?api-version=%s",
p.router.DetectURL(),
url.QueryEscape(text))
apiVer)

response, err := p.httpClient.SendRequest("GET", uri, nil, "text/plain")
response, err := p.httpClient.SendRequest("POST", uri, bytes.NewBuffer(b), "application/json")
if err != nil {
return "", tracerr.Wrap(err)
}

body, err := ioutil.ReadAll(response.Body)

defer response.Body.Close()
if err != nil {
return "", tracerr.Wrap(err)
}

detect := &xmlString{}
err = xml.Unmarshal(body, &detect)
detect := Detect{}
err = json.Unmarshal(body, &detect)
if err != nil {
return "", tracerr.Wrap(err)
}

return detect.Value, nil
return detect[0].Language, nil
}
Loading