v/GTT
1
0
mirror of https://github.com/eeeXun/GTT.git synced 2025-05-16 07:40:44 -07:00

feat(translator): add ReversoTranslate (#14)

This commit is contained in:
Xun 2023-03-10 20:51:38 +08:00 committed by GitHub
parent 9060a3999b
commit ea1e23172c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 296 additions and 9 deletions

View File

@ -6,6 +6,7 @@ Support:
[`ApertiumTranslate`](https://www.apertium.org/),
[`ArgosTranslate`](https://translate.argosopentech.com/),
[`GoogleTranslate`](https://translate.google.com/)
[`ReversoTranslate`](https://www.reverso.net/text-translation)
## ScreenShot
@ -43,10 +44,12 @@ You can pass `-src` and `-dst` in argument to set source and destination languag
gtt -src "English" -dst "Chinese (Traditional)"
```
See language on
[Apertium Translate](https://www.apertium.org/) for `ApertiumTranslate`,
[argosopentech/argos-translate](https://github.com/argosopentech/argos-translate#supported-languages) for `ArgosTranslate`,
[Google Language support](https://cloud.google.com/translate/docs/languages) for `GoogleTranslate`.
See language on:
- [Apertium Translate](https://www.apertium.org/) for `ApertiumTranslate`
- [argosopentech/argos-translate](https://github.com/argosopentech/argos-translate#supported-languages) for `ArgosTranslate`
- [Google Language support](https://cloud.google.com/translate/docs/languages) for `GoogleTranslate`
- [Reverso Translation](https://www.reverso.net/text-translation) for `ReversoTranslate`
## Key Map
@ -87,7 +90,7 @@ Stop play sound.
Toggle transparent.
`<C-\>`
Toggle Definition & Part of speech
Toggle Definition/Example & Part of speech.
`<Tab>`, `<S-Tab>`
Cycle through the pop out widget.
@ -105,7 +108,10 @@ Switch pop out window.
## Credit
[soimort/translate-shell](https://github.com/soimort/translate-shell) For translation URL.
[soimort/translate-shell](https://github.com/soimort/translate-shell),
[SimplyTranslate-Engines](https://codeberg.org/SimpleWeb/SimplyTranslate-Engines),
[s0ftik3/reverso-api](https://github.com/s0ftik3/reverso-api)
For translation URL.
[snsd0805/GoogleTranslate-TUI](https://github.com/snsd0805/GoogleTranslate-TUI) For inspiration.

View File

@ -24,6 +24,8 @@ func configInit() {
"destination.language.argostranslate": "English",
"source.language.googletranslate": "English",
"destination.language.googletranslate": "English",
"source.language.reversotranslate": "English",
"destination.language.reversotranslate": "English",
"translator": "ArgosTranslate",
}
)

View File

@ -0,0 +1,82 @@
package reversotranslate
var (
lang = []string{
"Arabic",
"Chinese (Simplified)",
"Czech",
"Danish",
"Dutch",
"English",
"French",
"German",
"Greek",
"Hebrew",
"Hindi",
"Hungarian",
"Italian",
"Japanese",
"Korean",
"Persian",
"Polish",
"Portuguese",
"Romanian",
"Russian",
"Slovak",
"Spanish",
"Swedish",
"Thai",
"Turkish",
"Ukrainian",
}
langCode = map[string]string{
"Arabic": "ara",
"Chinese (Simplified)": "chi",
"Czech": "cze",
"Danish": "dan",
"Dutch": "dut",
"English": "eng",
"French": "fra",
"German": "ger",
"Greek": "gre",
"Hebrew": "heb",
"Hindi": "hin",
"Hungarian": "hun",
"Italian": "ita",
"Japanese": "jpn",
"Korean": "kor",
"Persian": "per",
"Polish": "pol",
"Portuguese": "por",
"Romanian": "rum",
"Russian": "rus",
"Slovak": "slo",
"Spanish": "spa",
"Swedish": "swe",
"Thai": "tha",
"Turkish": "tur",
"Ukrainian": "ukr",
}
voiceName = map[string]string{
"Arabic": "Mehdi22k",
"Chinese (Simplified)": "Lulu22k",
"Czech": "Eliska22k",
"Danish": "Mette22k",
"Dutch": "Sofie22k",
"English": "Heather22k",
"French": "Alice22k",
"German": "Andreas22k",
"Greek": "Dimitris22k",
"Hebrew": "he-IL-Asaf",
"Italian": "Fabiana22k",
"Japanese": "Sakura22k",
"Korean": "Minji22k",
"Polish": "Monika22k",
"Portuguese": "Celia22k",
"Romanian": "ro-RO-Andrei",
"Russian": "Alyona22k",
"Spanish": "Antonio22k",
"Swedish": "Emma22k",
"Turkish": "Ipek22k",
}
)

View File

@ -0,0 +1,116 @@
package reversotranslate
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"net/http"
"regexp"
"github.com/eeeXun/gtt/internal/lock"
)
const (
textURL = "https://api.reverso.net/translate/v1/translation"
userAgent = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36"
)
type ReversoTranslate struct {
srcLang string
dstLang string
EngineName string
SoundLock *lock.Lock
}
func (t *ReversoTranslate) GetEngineName() string {
return t.EngineName
}
func (t *ReversoTranslate) GetAllLang() []string {
return lang
}
func (t *ReversoTranslate) GetSrcLang() string {
return t.srcLang
}
func (t *ReversoTranslate) GetDstLang() string {
return t.dstLang
}
func (t *ReversoTranslate) SetSrcLang(srcLang string) {
t.srcLang = srcLang
}
func (t *ReversoTranslate) SetDstLang(dstLang string) {
t.dstLang = dstLang
}
func (t *ReversoTranslate) SwapLang() {
t.srcLang, t.dstLang = t.dstLang, t.srcLang
}
func (t *ReversoTranslate) Translate(message string) (translation, definition, partOfSpeech string, err error) {
var data map[string]interface{}
userData, _ := json.Marshal(map[string]interface{}{
"format": "text",
"from": langCode[t.srcLang],
"to": langCode[t.dstLang],
"input": message,
"options": map[string]string{
"sentenceSplitter": "true",
"origin": "translation.web",
"contextResults": "true",
"languageDetection": "false",
},
})
req, _ := http.NewRequest("POST",
textURL,
bytes.NewBuffer([]byte(userData)))
req.Header.Add("Content-Type", "application/json")
req.Header.Add("User-Agent", userAgent)
res, err := http.DefaultClient.Do(req)
if err != nil {
return "", "", "", err
}
body, err := ioutil.ReadAll(res.Body)
if err != nil {
return "", "", "", err
}
if err = json.Unmarshal(body, &data); err != nil {
return "", "", "", err
}
if len(data) <= 0 {
return "", "", "", errors.New("Translation not found")
}
// translation
translation += fmt.Sprintf("%v", data["translation"].([]interface{})[0])
// definition and part of speech
if data["contextResults"] != nil {
for _, results := range data["contextResults"].(map[string]interface{})["results"].([]interface{}) {
results := results.(map[string]interface{})
// definition
srcExample := results["sourceExamples"].([]interface{})
dstExample := results["targetExamples"].([]interface{})
if len(srcExample) > 0 && len(dstExample) > 0 {
for i := 0; i < len(srcExample) && i < len(dstExample); i++ {
definition += fmt.Sprintf("- %v\n\t\"%v\"\n", srcExample[i], dstExample[i])
}
}
// part of speech
if results["partOfSpeech"] == nil {
partOfSpeech += fmt.Sprintf("%v\n", results["translation"])
} else {
partOfSpeech += fmt.Sprintf("%v [%v]\n", results["translation"], results["partOfSpeech"])
}
}
definition = regexp.MustCompile("<(|/)em>").ReplaceAllString(definition, "")
}
return translation, definition, partOfSpeech, nil
}

View File

@ -0,0 +1,75 @@
package reversotranslate
import (
"encoding/base64"
"errors"
"fmt"
"net/http"
"time"
"github.com/hajimehoshi/go-mp3"
"github.com/hajimehoshi/oto/v2"
)
const (
ttsURL = "https://voice.reverso.net/RestPronunciation.svc/v1/output=json/GetVoiceStream/voiceName=%s?voiceSpeed=80&inputText=%s"
)
func (t *ReversoTranslate) LockAvailable() bool {
return t.SoundLock.Available()
}
func (t *ReversoTranslate) LockAcquire() {
t.SoundLock.Acquire()
}
func (t *ReversoTranslate) StopTTS() {
t.SoundLock.Stop = true
}
func (t *ReversoTranslate) PlayTTS(lang, message string) error {
name, ok := voiceName[lang]
if !ok {
return errors.New(t.EngineName + " does not support text to speech of " + lang)
}
urlStr := fmt.Sprintf(
ttsURL,
name,
base64.StdEncoding.EncodeToString([]byte(message)),
)
req, _ := http.NewRequest("GET", urlStr, nil)
req.Header.Add("User-Agent", userAgent)
res, err := http.DefaultClient.Do(req)
if err != nil {
t.SoundLock.Release()
return err
}
decoder, err := mp3.NewDecoder(res.Body)
if err != nil {
t.SoundLock.Release()
return err
}
otoCtx, readyChan, err := oto.NewContext(decoder.SampleRate(), 2, 2)
if err != nil {
t.SoundLock.Release()
return err
}
<-readyChan
player := otoCtx.NewPlayer(decoder)
player.Play()
for player.IsPlaying() {
if t.SoundLock.Stop {
t.SoundLock.Release()
return nil
} else {
time.Sleep(time.Millisecond)
}
}
if err = player.Close(); err != nil {
t.SoundLock.Release()
return err
}
t.SoundLock.Release()
return nil
}

View File

@ -5,10 +5,11 @@ import (
"github.com/eeeXun/gtt/internal/translate/apertiumtranslate"
"github.com/eeeXun/gtt/internal/translate/argostranslate"
"github.com/eeeXun/gtt/internal/translate/googletranslate"
"github.com/eeeXun/gtt/internal/translate/reversotranslate"
)
var (
AllTranslator = []string{"ApertiumTranslate", "ArgosTranslate", "GoogleTranslate"}
AllTranslator = []string{"ApertiumTranslate", "ArgosTranslate", "GoogleTranslate", "ReversoTranslate"}
)
type Translator interface {
@ -48,6 +49,11 @@ func NewTranslator(name string) Translator {
EngineName: "GoogleTranslate",
SoundLock: lock.NewLock(),
}
case "ReversoTranslate":
translator = &reversotranslate.ReversoTranslate{
EngineName: "ReversoTranslate",
SoundLock: lock.NewLock(),
}
}
return translator

4
ui.go
View File

@ -38,7 +38,7 @@ const (
[#%[1]s]<C-t>[-]
Toggle transparent.
[#%[1]s]<C-\>[-]
Toggle Definition & Part of speech
Toggle Definition/Example & Part of speech.
[#%[1]s]<Tab>, <S-Tab>[-]
Cycle through the pop out widget.
[#%[1]s]<1>, <2>, <3>[-]
@ -237,7 +237,7 @@ func uiInit() {
// input/output
srcInput.SetBorder(true)
dstOutput.SetBorder(true)
defOutput.SetBorder(true).SetTitle("Definition")
defOutput.SetBorder(true).SetTitle("Definition/Example")
posOutput.SetBorder(true).SetTitle("Part of speech")
// dropdown