1 | package engines
|
---|
2 |
|
---|
3 | import (
|
---|
4 | "bytes"
|
---|
5 | "encoding/json"
|
---|
6 | "fmt"
|
---|
7 | "net/http"
|
---|
8 | )
|
---|
9 |
|
---|
10 | // Reverso is an engine that fetches data from https://reverso.net.
|
---|
11 | type Reverso struct{}
|
---|
12 |
|
---|
13 | func (_ *Reverso) InternalName() string { return "reverso" }
|
---|
14 |
|
---|
15 | func (_ *Reverso) DisplayName() string { return "Reverso" }
|
---|
16 |
|
---|
17 | var reversoLangs = []Language{
|
---|
18 | {Name: "Arabic", Code: "ara"},
|
---|
19 | {Name: "Chinese (Simplified)", Code: "chi"}, // marketed as just "Chinese"
|
---|
20 | {Name: "Czech", Code: "cze"},
|
---|
21 | {Name: "Danish", Code: "dan"},
|
---|
22 | {Name: "Dutch", Code: "dut"},
|
---|
23 | {Name: "English", Code: "eng"},
|
---|
24 | {Name: "French", Code: "fra"},
|
---|
25 | {Name: "German", Code: "ger"},
|
---|
26 | {Name: "Hebrew", Code: "heb"},
|
---|
27 | {Name: "Hindi", Code: "hin"},
|
---|
28 | {Name: "Hungarian", Code: "hun"},
|
---|
29 | {Name: "Italian", Code: "ita"},
|
---|
30 | {Name: "Japanese", Code: "jpn"},
|
---|
31 | {Name: "Korean", Code: "kor"},
|
---|
32 | {Name: "Persian", Code: "per"},
|
---|
33 | {Name: "Polish", Code: "pol"},
|
---|
34 | {Name: "Portuguese", Code: "por"},
|
---|
35 | {Name: "Romanian", Code: "rum"},
|
---|
36 | {Name: "Russian", Code: "rus"},
|
---|
37 | {Name: "Slovak", Code: "slo"},
|
---|
38 | {Name: "Spanish", Code: "spa"},
|
---|
39 | {Name: "Swedish", Code: "swe"},
|
---|
40 | {Name: "Thai", Code: "tha"},
|
---|
41 | {Name: "Turkish", Code: "tur"},
|
---|
42 | {Name: "Ukrainian", Code: "ukr"},
|
---|
43 | }
|
---|
44 |
|
---|
45 | func (_ *Reverso) SourceLanguages() ([]Language, error) { return reversoLangs, nil }
|
---|
46 |
|
---|
47 | func (_ *Reverso) TargetLanguages() ([]Language, error) { return reversoLangs, nil }
|
---|
48 |
|
---|
49 | func (_ *Reverso) SupportsAutodetect() bool { return true }
|
---|
50 |
|
---|
51 | type reversoAPIResponse struct {
|
---|
52 | LanguageDetection struct {
|
---|
53 | DetectedLanguage string `json:"detectedLanguage"`
|
---|
54 | } `json:"languageDetection"`
|
---|
55 | Translation []string `json:"translation"`
|
---|
56 | }
|
---|
57 |
|
---|
58 | func (e *Reverso) callAPI(text string, from, to Language) (reversoAPIResponse, error) {
|
---|
59 | // `contextResults` must be false for language detection
|
---|
60 | formData := map[string]interface{}{
|
---|
61 | "format": "text",
|
---|
62 | "from": from.Code,
|
---|
63 | "to": to.Code,
|
---|
64 | "input": text,
|
---|
65 | "options": map[string]interface{}{
|
---|
66 | "sentenceSplitter": false,
|
---|
67 | "origin": "translation.web",
|
---|
68 | "contextResults": false,
|
---|
69 | "languageDetection": true,
|
---|
70 | },
|
---|
71 | }
|
---|
72 |
|
---|
73 | formDataJSON, err := json.Marshal(formData)
|
---|
74 |
|
---|
75 | if err != nil {
|
---|
76 | return reversoAPIResponse{}, err
|
---|
77 | }
|
---|
78 |
|
---|
79 | request, err := http.NewRequest("POST", "https://api.reverso.net/translate/v1/translation", bytes.NewBuffer(formDataJSON))
|
---|
80 |
|
---|
81 | if err != nil {
|
---|
82 | return reversoAPIResponse{}, err
|
---|
83 | }
|
---|
84 |
|
---|
85 | request.Header.Set("Content-Type", "application/json")
|
---|
86 | // Returns 403 with empty or no user agent.
|
---|
87 | request.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; rv:110.0) Gecko/20100101 Firefox/110.0")
|
---|
88 |
|
---|
89 | client := &http.Client{}
|
---|
90 |
|
---|
91 | response, err := client.Do(request)
|
---|
92 |
|
---|
93 | if err != nil {
|
---|
94 | return reversoAPIResponse{}, err
|
---|
95 | }
|
---|
96 |
|
---|
97 | defer response.Body.Close()
|
---|
98 |
|
---|
99 | if response.StatusCode != 200 {
|
---|
100 | return reversoAPIResponse{}, fmt.Errorf("got status code %d from Reverso API", response.StatusCode)
|
---|
101 | }
|
---|
102 |
|
---|
103 | var result reversoAPIResponse
|
---|
104 |
|
---|
105 | if err := json.NewDecoder(response.Body).Decode(&result); err != nil {
|
---|
106 | return reversoAPIResponse{}, err
|
---|
107 | }
|
---|
108 |
|
---|
109 | return result, nil
|
---|
110 | }
|
---|
111 |
|
---|
112 | func (e *Reverso) DetectLanguage(text string) (Language, error) {
|
---|
113 | // Any language pair works here, does not affect result
|
---|
114 | r, err := e.callAPI(text, reversoLangs[0], reversoLangs[1])
|
---|
115 |
|
---|
116 | if err != nil {
|
---|
117 | return Language{}, err
|
---|
118 | }
|
---|
119 |
|
---|
120 | langCode := r.LanguageDetection.DetectedLanguage
|
---|
121 |
|
---|
122 | for _, lang := range reversoLangs {
|
---|
123 | if lang.Code == langCode {
|
---|
124 | return lang, nil
|
---|
125 | }
|
---|
126 | }
|
---|
127 |
|
---|
128 | return Language{}, fmt.Errorf("language code \"%s\" is not in Reverso's language list", langCode)
|
---|
129 | }
|
---|
130 |
|
---|
131 | func (e *Reverso) Translate(text string, from, to Language) (TranslationResult, error) {
|
---|
132 | if from.Code == "auto" {
|
---|
133 | from_, err := e.DetectLanguage(text)
|
---|
134 |
|
---|
135 | if err != nil {
|
---|
136 | return TranslationResult{}, err
|
---|
137 | }
|
---|
138 |
|
---|
139 | from = from_
|
---|
140 | }
|
---|
141 |
|
---|
142 | var translation string
|
---|
143 |
|
---|
144 | if from == to {
|
---|
145 | translation = text
|
---|
146 | } else {
|
---|
147 | r, err := e.callAPI(text, from, to)
|
---|
148 |
|
---|
149 | if err != nil {
|
---|
150 | return TranslationResult{}, err
|
---|
151 | }
|
---|
152 |
|
---|
153 | translation = r.Translation[0]
|
---|
154 | }
|
---|
155 |
|
---|
156 | return TranslationResult{
|
---|
157 | SourceLanguage: from,
|
---|
158 | TranslatedText: translation,
|
---|
159 | }, nil
|
---|
160 | }
|
---|