mirror of
https://github.com/eeeXun/GTT.git
synced 2025-05-17 00:00:16 -07:00
feat(translator): add ReversoTranslate (#14)
This commit is contained in:
parent
9060a3999b
commit
ea1e23172c
18
README.md
18
README.md
@ -6,6 +6,7 @@ Support:
|
|||||||
[`ApertiumTranslate`](https://www.apertium.org/),
|
[`ApertiumTranslate`](https://www.apertium.org/),
|
||||||
[`ArgosTranslate`](https://translate.argosopentech.com/),
|
[`ArgosTranslate`](https://translate.argosopentech.com/),
|
||||||
[`GoogleTranslate`](https://translate.google.com/)
|
[`GoogleTranslate`](https://translate.google.com/)
|
||||||
|
[`ReversoTranslate`](https://www.reverso.net/text-translation)
|
||||||
|
|
||||||
## ScreenShot
|
## 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)"
|
gtt -src "English" -dst "Chinese (Traditional)"
|
||||||
```
|
```
|
||||||
|
|
||||||
See language on
|
See language on:
|
||||||
[Apertium Translate](https://www.apertium.org/) for `ApertiumTranslate`,
|
|
||||||
[argosopentech/argos-translate](https://github.com/argosopentech/argos-translate#supported-languages) for `ArgosTranslate`,
|
- [Apertium Translate](https://www.apertium.org/) for `ApertiumTranslate`
|
||||||
[Google Language support](https://cloud.google.com/translate/docs/languages) for `GoogleTranslate`.
|
- [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
|
## Key Map
|
||||||
|
|
||||||
@ -87,7 +90,7 @@ Stop play sound.
|
|||||||
Toggle transparent.
|
Toggle transparent.
|
||||||
|
|
||||||
`<C-\>`
|
`<C-\>`
|
||||||
Toggle Definition & Part of speech
|
Toggle Definition/Example & Part of speech.
|
||||||
|
|
||||||
`<Tab>`, `<S-Tab>`
|
`<Tab>`, `<S-Tab>`
|
||||||
Cycle through the pop out widget.
|
Cycle through the pop out widget.
|
||||||
@ -105,7 +108,10 @@ Switch pop out window.
|
|||||||
|
|
||||||
## Credit
|
## 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.
|
[snsd0805/GoogleTranslate-TUI](https://github.com/snsd0805/GoogleTranslate-TUI) For inspiration.
|
||||||
|
|
||||||
|
@ -24,6 +24,8 @@ func configInit() {
|
|||||||
"destination.language.argostranslate": "English",
|
"destination.language.argostranslate": "English",
|
||||||
"source.language.googletranslate": "English",
|
"source.language.googletranslate": "English",
|
||||||
"destination.language.googletranslate": "English",
|
"destination.language.googletranslate": "English",
|
||||||
|
"source.language.reversotranslate": "English",
|
||||||
|
"destination.language.reversotranslate": "English",
|
||||||
"translator": "ArgosTranslate",
|
"translator": "ArgosTranslate",
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
82
internal/translate/reversotranslate/language.go
Normal file
82
internal/translate/reversotranslate/language.go
Normal 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",
|
||||||
|
}
|
||||||
|
)
|
116
internal/translate/reversotranslate/translator.go
Normal file
116
internal/translate/reversotranslate/translator.go
Normal 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
|
||||||
|
}
|
75
internal/translate/reversotranslate/tts.go
Normal file
75
internal/translate/reversotranslate/tts.go
Normal 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
|
||||||
|
}
|
@ -5,10 +5,11 @@ import (
|
|||||||
"github.com/eeeXun/gtt/internal/translate/apertiumtranslate"
|
"github.com/eeeXun/gtt/internal/translate/apertiumtranslate"
|
||||||
"github.com/eeeXun/gtt/internal/translate/argostranslate"
|
"github.com/eeeXun/gtt/internal/translate/argostranslate"
|
||||||
"github.com/eeeXun/gtt/internal/translate/googletranslate"
|
"github.com/eeeXun/gtt/internal/translate/googletranslate"
|
||||||
|
"github.com/eeeXun/gtt/internal/translate/reversotranslate"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
AllTranslator = []string{"ApertiumTranslate", "ArgosTranslate", "GoogleTranslate"}
|
AllTranslator = []string{"ApertiumTranslate", "ArgosTranslate", "GoogleTranslate", "ReversoTranslate"}
|
||||||
)
|
)
|
||||||
|
|
||||||
type Translator interface {
|
type Translator interface {
|
||||||
@ -48,6 +49,11 @@ func NewTranslator(name string) Translator {
|
|||||||
EngineName: "GoogleTranslate",
|
EngineName: "GoogleTranslate",
|
||||||
SoundLock: lock.NewLock(),
|
SoundLock: lock.NewLock(),
|
||||||
}
|
}
|
||||||
|
case "ReversoTranslate":
|
||||||
|
translator = &reversotranslate.ReversoTranslate{
|
||||||
|
EngineName: "ReversoTranslate",
|
||||||
|
SoundLock: lock.NewLock(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return translator
|
return translator
|
||||||
|
4
ui.go
4
ui.go
@ -38,7 +38,7 @@ const (
|
|||||||
[#%[1]s]<C-t>[-]
|
[#%[1]s]<C-t>[-]
|
||||||
Toggle transparent.
|
Toggle transparent.
|
||||||
[#%[1]s]<C-\>[-]
|
[#%[1]s]<C-\>[-]
|
||||||
Toggle Definition & Part of speech
|
Toggle Definition/Example & Part of speech.
|
||||||
[#%[1]s]<Tab>, <S-Tab>[-]
|
[#%[1]s]<Tab>, <S-Tab>[-]
|
||||||
Cycle through the pop out widget.
|
Cycle through the pop out widget.
|
||||||
[#%[1]s]<1>, <2>, <3>[-]
|
[#%[1]s]<1>, <2>, <3>[-]
|
||||||
@ -237,7 +237,7 @@ func uiInit() {
|
|||||||
// input/output
|
// input/output
|
||||||
srcInput.SetBorder(true)
|
srcInput.SetBorder(true)
|
||||||
dstOutput.SetBorder(true)
|
dstOutput.SetBorder(true)
|
||||||
defOutput.SetBorder(true).SetTitle("Definition")
|
defOutput.SetBorder(true).SetTitle("Definition/Example")
|
||||||
posOutput.SetBorder(true).SetTitle("Part of speech")
|
posOutput.SetBorder(true).SetTitle("Part of speech")
|
||||||
|
|
||||||
// dropdown
|
// dropdown
|
||||||
|
Loading…
x
Reference in New Issue
Block a user