mirror of
https://github.com/eeeXun/GTT.git
synced 2025-05-18 08:40:35 -07:00
Merge branch 'master' into theme
This commit is contained in:
commit
2f7037c146
38
README.md
38
README.md
@ -3,11 +3,22 @@
|
||||
Google Translate TUI (Originally)
|
||||
|
||||
Supported Translator:
|
||||
[`ApertiumTranslate`](https://www.apertium.org/),
|
||||
[`ArgosTranslate`](https://translate.argosopentech.com/)(default),
|
||||
[`BingTranslate`](https://www.bing.com/translator),
|
||||
[`GoogleTranslate`](https://translate.google.com/),
|
||||
[`ReversoTranslate`](https://www.reverso.net/text-translation)
|
||||
[`Apertium`](https://www.apertium.org/),
|
||||
[`Argos`](https://translate.argosopentech.com/),
|
||||
[`Bing`](https://www.bing.com/translator),
|
||||
[`ChatGPT`](https://chat.openai.com/),
|
||||
[`Google`](https://translate.google.com/)(default),
|
||||
[`Reverso`](https://www.reverso.net/text-translation)
|
||||
|
||||
## ⚠️ Note for ChatGPT
|
||||
|
||||
You need to apply an API key on [OpenAI API keys](https://platform.openai.com/account/api-keys).
|
||||
And write it to `$XDG_CONFIG_HOME/gtt/gtt.yaml` or `$HOME/.config/gtt/gtt.yaml`.
|
||||
|
||||
```yaml
|
||||
api_key:
|
||||
chatgpt: YOUR_API_KEY # <- Replace with your API Key
|
||||
```
|
||||
|
||||
## ScreenShot
|
||||
|
||||
@ -47,11 +58,12 @@ gtt -src "English" -dst "Chinese (Traditional)"
|
||||
|
||||
See available languages on:
|
||||
|
||||
- [Apertium Translate](https://www.apertium.org/) for `ApertiumTranslate`
|
||||
- [argosopentech/argos-translate](https://github.com/argosopentech/argos-translate#supported-languages) for `ArgosTranslate`
|
||||
- [Bing language-support](https://learn.microsoft.com/en-us/azure/cognitive-services/translator/language-support#translation) for `BingTranslate`
|
||||
- [Google Language support](https://cloud.google.com/translate/docs/languages) for `GoogleTranslate`
|
||||
- [Reverso Translation](https://www.reverso.net/text-translation) for `ReversoTranslate`
|
||||
- [Apertium Translate](https://www.apertium.org/) for `Apertium`
|
||||
- [argosopentech/argos-translate](https://github.com/argosopentech/argos-translate#supported-languages) for `Argos`
|
||||
- [Bing language-support](https://learn.microsoft.com/en-us/azure/cognitive-services/translator/language-support#translation) for `Bing`
|
||||
- `ChatGPT` is same as `Google`. See [Google Language support](https://cloud.google.com/translate/docs/languages)
|
||||
- [Google Language support](https://cloud.google.com/translate/docs/languages) for `Google`
|
||||
- [Reverso Translation](https://www.reverso.net/text-translation) for `Reverso`
|
||||
|
||||
## Key Map
|
||||
|
||||
@ -80,10 +92,10 @@ Copy all text in source of translation window.
|
||||
Copy all text in destination of translation window.
|
||||
|
||||
`<C-o>`
|
||||
Play sound on source of translation window.
|
||||
Play text to speech on source of translation window.
|
||||
|
||||
`<C-p>`
|
||||
Play sound on destination of translation window.
|
||||
Play text to speech on destination of translation window.
|
||||
|
||||
`<C-x>`
|
||||
Stop play sound.
|
||||
@ -106,8 +118,6 @@ Switch pop out window.
|
||||
|
||||
[`wl-clipboard`](https://github.com/bugaevc/wl-clipboard) for Linux/Wayland to copy text.
|
||||
|
||||
`pbcopy` For macOS to copy text.
|
||||
|
||||
## Credit
|
||||
|
||||
[soimort/translate-shell](https://github.com/soimort/translate-shell),
|
||||
|
54
config.go
54
config.go
@ -15,22 +15,24 @@ func configInit() {
|
||||
defaultConfigPath string
|
||||
themeConfig = config.New()
|
||||
defaultConfig = map[string]interface{}{
|
||||
"hide_below": false,
|
||||
"transparent": false,
|
||||
"theme": "Gruvbox",
|
||||
"source.border_color": "red",
|
||||
"destination.border_color": "blue",
|
||||
"source.language.apertiumtranslate": "English",
|
||||
"destination.language.apertiumtranslate": "English",
|
||||
"source.language.argostranslate": "English",
|
||||
"destination.language.argostranslate": "English",
|
||||
"source.language.bingtranslate": "English",
|
||||
"destination.language.bingtranslate": "English",
|
||||
"source.language.googletranslate": "English",
|
||||
"destination.language.googletranslate": "English",
|
||||
"source.language.reversotranslate": "English",
|
||||
"destination.language.reversotranslate": "English",
|
||||
"translator": "ArgosTranslate",
|
||||
"hide_below": false,
|
||||
"transparent": false,
|
||||
"theme": "gruvbox",
|
||||
"source.border_color": "red",
|
||||
"destination.border_color": "blue",
|
||||
"source.language.apertium": "English",
|
||||
"destination.language.apertium": "English",
|
||||
"source.language.argos": "English",
|
||||
"destination.language.argos": "English",
|
||||
"source.language.bing": "English",
|
||||
"destination.language.bing": "English",
|
||||
"source.language.chatgpt": "English",
|
||||
"destination.language.chatgpt": "English",
|
||||
"source.language.google": "English",
|
||||
"destination.language.google": "English",
|
||||
"source.language.reverso": "English",
|
||||
"destination.language.reverso": "English",
|
||||
"translator": "Google",
|
||||
}
|
||||
)
|
||||
|
||||
@ -48,7 +50,7 @@ func configInit() {
|
||||
config.AddConfigPath("$HOME/.config/gtt")
|
||||
themeConfig.AddConfigPath("$HOME/.config/gtt")
|
||||
|
||||
// Create config file if not exists
|
||||
// Create config file if it does not exist
|
||||
// Otherwise check if config value is missing
|
||||
if err := config.ReadInConfig(); err != nil {
|
||||
for key, value := range defaultConfig {
|
||||
@ -66,6 +68,16 @@ func configInit() {
|
||||
missing = true
|
||||
}
|
||||
}
|
||||
// Set to default theme if theme in config does not exist
|
||||
if IndexOf(config.GetString("theme"), style.AllTheme) < 0 {
|
||||
config.Set("theme", defaultConfig["theme"])
|
||||
missing = true
|
||||
}
|
||||
// Set to default translator if translator in config does not exist
|
||||
if IndexOf(config.GetString("translator"), translate.AllTranslator) < 0 {
|
||||
config.Set("translator", defaultConfig["translator"])
|
||||
missing = true
|
||||
}
|
||||
if missing {
|
||||
config.WriteConfig()
|
||||
}
|
||||
@ -84,7 +96,7 @@ func configInit() {
|
||||
}
|
||||
}
|
||||
|
||||
// setup
|
||||
// Setup
|
||||
for _, name := range translate.AllTranslator {
|
||||
translators[name] = translate.NewTranslator(name)
|
||||
translators[name].SetSrcLang(
|
||||
@ -98,7 +110,11 @@ func configInit() {
|
||||
uiStyle.Transparent = config.GetBool("transparent")
|
||||
uiStyle.SetSrcBorderColor(config.GetString("source.border_color")).
|
||||
SetDstBorderColor(config.GetString("destination.border_color"))
|
||||
// set argument language
|
||||
// Set API Key
|
||||
if config.Get("api_key.chatgpt") != nil {
|
||||
translators["ChatGPT"].SetAPIKey(config.GetString("api_key.chatgpt"))
|
||||
}
|
||||
// Set argument language
|
||||
if len(*srcLangArg) > 0 {
|
||||
translator.SetSrcLang(*srcLangArg)
|
||||
}
|
||||
|
@ -5,10 +5,10 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
AllTheme = []string{"Gruvbox", "Nord"}
|
||||
AllTheme = []string{"gruvbox", "nord"}
|
||||
Palette = []string{"red", "green", "yellow", "blue", "purple", "cyan", "orange"}
|
||||
themes = map[string]map[string]tcell.Color{
|
||||
"Gruvbox": {
|
||||
"gruvbox": {
|
||||
"bg": tcell.NewHexColor(0x282828),
|
||||
"fg": tcell.NewHexColor(0xebdbb2),
|
||||
"gray": tcell.NewHexColor(0x665c54),
|
||||
@ -20,7 +20,7 @@ var (
|
||||
"cyan": tcell.NewHexColor(0x8ec07c),
|
||||
"orange": tcell.NewHexColor(0xfe8019),
|
||||
},
|
||||
"Nord": {
|
||||
"nord": {
|
||||
"bg": tcell.NewHexColor(0x3b4252),
|
||||
"fg": tcell.NewHexColor(0xeceff4),
|
||||
"gray": tcell.NewHexColor(0x4c566a),
|
||||
|
@ -1,4 +1,4 @@
|
||||
package apertiumtranslate
|
||||
package apertium
|
||||
|
||||
var (
|
||||
lang = []string{
|
81
internal/translate/apertium/translator.go
Normal file
81
internal/translate/apertium/translator.go
Normal file
@ -0,0 +1,81 @@
|
||||
package apertium
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
||||
"github.com/eeeXun/gtt/internal/translate/core"
|
||||
)
|
||||
|
||||
const (
|
||||
textURL = "https://www.apertium.org/apy/translate?langpair=%s|%s&q=%s"
|
||||
)
|
||||
|
||||
type Translator struct {
|
||||
*core.APIKey
|
||||
*core.Language
|
||||
*core.TTSLock
|
||||
core.EngineName
|
||||
}
|
||||
|
||||
func NewTranslator() *Translator {
|
||||
return &Translator{
|
||||
APIKey: new(core.APIKey),
|
||||
Language: new(core.Language),
|
||||
TTSLock: core.NewTTSLock(),
|
||||
EngineName: core.NewEngineName("Apertium"),
|
||||
}
|
||||
}
|
||||
|
||||
func (t *Translator) GetAllLang() []string {
|
||||
return lang
|
||||
}
|
||||
|
||||
func (t *Translator) Translate(message string) (translation *core.Translation, err error) {
|
||||
translation = new(core.Translation)
|
||||
var data map[string]interface{}
|
||||
|
||||
urlStr := fmt.Sprintf(
|
||||
textURL,
|
||||
langCode[t.GetSrcLang()],
|
||||
langCode[t.GetDstLang()],
|
||||
url.QueryEscape(message),
|
||||
)
|
||||
res, err := http.Get(urlStr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
body, err := ioutil.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err = json.Unmarshal(body, &data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(data) <= 0 {
|
||||
return nil, errors.New("Translation not found")
|
||||
}
|
||||
// If responseData is nil, then suppose the translation pair is not available
|
||||
if data["responseData"] == nil {
|
||||
return nil, errors.New(fmt.Sprintf("%s does not support translate from %s to %s.",
|
||||
t.GetEngineName(),
|
||||
t.GetSrcLang(),
|
||||
t.GetDstLang(),
|
||||
))
|
||||
}
|
||||
|
||||
translation.TEXT = data["responseData"].(map[string]interface{})["translatedText"].(string)
|
||||
|
||||
return translation, nil
|
||||
}
|
||||
|
||||
func (t *Translator) PlayTTS(lang, message string) error {
|
||||
defer t.ReleaseLock()
|
||||
|
||||
return errors.New(t.GetEngineName() + " does not support text to speech")
|
||||
}
|
@ -1,82 +0,0 @@
|
||||
package apertiumtranslate
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
||||
"github.com/eeeXun/gtt/internal/translate/core"
|
||||
)
|
||||
|
||||
const (
|
||||
textURL = "https://www.apertium.org/apy/translate?langpair=%s|%s&q=%s"
|
||||
)
|
||||
|
||||
type ApertiumTranslate struct {
|
||||
*core.Language
|
||||
*core.TTSLock
|
||||
core.EngineName
|
||||
}
|
||||
|
||||
func NewApertiumTranslate() *ApertiumTranslate {
|
||||
return &ApertiumTranslate{
|
||||
Language: core.NewLanguage(),
|
||||
TTSLock: core.NewTTSLock(),
|
||||
EngineName: core.NewEngineName("ApertiumTranslate"),
|
||||
}
|
||||
}
|
||||
|
||||
func (t *ApertiumTranslate) GetAllLang() []string {
|
||||
return lang
|
||||
}
|
||||
|
||||
func (t *ApertiumTranslate) Translate(message string) (translation, definition, partOfSpeech string, err error) {
|
||||
var data map[string]interface{}
|
||||
|
||||
urlStr := fmt.Sprintf(
|
||||
textURL,
|
||||
langCode[t.GetSrcLang()],
|
||||
langCode[t.GetDstLang()],
|
||||
url.QueryEscape(message),
|
||||
)
|
||||
res, err := http.Get(urlStr)
|
||||
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")
|
||||
}
|
||||
|
||||
switch res.StatusCode {
|
||||
case 200:
|
||||
translation = fmt.Sprintf("%v",
|
||||
data["responseData"].(map[string]interface{})["translatedText"])
|
||||
default:
|
||||
return "", "", "", errors.New(
|
||||
fmt.Sprintf("%s does not support translate from %s to %s.\nSee available pair on %s",
|
||||
t.GetEngineName(),
|
||||
t.GetSrcLang(),
|
||||
t.GetDstLang(),
|
||||
"https://www.apertium.org/",
|
||||
))
|
||||
}
|
||||
|
||||
return translation, definition, partOfSpeech, nil
|
||||
}
|
||||
|
||||
func (t *ApertiumTranslate) PlayTTS(lang, message string) error {
|
||||
defer t.ReleaseLock()
|
||||
|
||||
return errors.New(t.GetEngineName() + " does not support text to speech")
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package argostranslate
|
||||
package argos
|
||||
|
||||
var (
|
||||
lang = []string{
|
@ -1,9 +1,8 @@
|
||||
package argostranslate
|
||||
package argos
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
@ -15,25 +14,28 @@ const (
|
||||
textURL = "https://translate.argosopentech.com/translate"
|
||||
)
|
||||
|
||||
type ArgosTranslate struct {
|
||||
type Translator struct {
|
||||
*core.APIKey
|
||||
*core.Language
|
||||
*core.TTSLock
|
||||
core.EngineName
|
||||
}
|
||||
|
||||
func NewArgosTranslate() *ArgosTranslate {
|
||||
return &ArgosTranslate{
|
||||
Language: core.NewLanguage(),
|
||||
func NewTranslator() *Translator {
|
||||
return &Translator{
|
||||
APIKey: new(core.APIKey),
|
||||
Language: new(core.Language),
|
||||
TTSLock: core.NewTTSLock(),
|
||||
EngineName: core.NewEngineName("ArgosTranslate"),
|
||||
EngineName: core.NewEngineName("Argos"),
|
||||
}
|
||||
}
|
||||
|
||||
func (t *ArgosTranslate) GetAllLang() []string {
|
||||
func (t *Translator) GetAllLang() []string {
|
||||
return lang
|
||||
}
|
||||
|
||||
func (t *ArgosTranslate) Translate(message string) (translation, definition, partOfSpeech string, err error) {
|
||||
func (t *Translator) Translate(message string) (translation *core.Translation, err error) {
|
||||
translation = new(core.Translation)
|
||||
var data map[string]interface{}
|
||||
|
||||
res, err := http.PostForm(textURL,
|
||||
@ -43,26 +45,26 @@ func (t *ArgosTranslate) Translate(message string) (translation, definition, par
|
||||
"target": {langCode[t.GetDstLang()]},
|
||||
})
|
||||
if err != nil {
|
||||
return "", "", "", err
|
||||
return nil, err
|
||||
}
|
||||
body, err := ioutil.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
return "", "", "", err
|
||||
return nil, err
|
||||
}
|
||||
if err = json.Unmarshal(body, &data); err != nil {
|
||||
return "", "", "", err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(data) <= 0 {
|
||||
return "", "", "", errors.New("Translation not found")
|
||||
return nil, errors.New("Translation not found")
|
||||
}
|
||||
|
||||
translation = fmt.Sprintf("%v", data["translatedText"])
|
||||
translation.TEXT = data["translatedText"].(string)
|
||||
|
||||
return translation, definition, partOfSpeech, nil
|
||||
return translation, nil
|
||||
}
|
||||
|
||||
func (t *ArgosTranslate) PlayTTS(lang, message string) error {
|
||||
func (t *Translator) PlayTTS(lang, message string) error {
|
||||
defer t.ReleaseLock()
|
||||
|
||||
return errors.New(t.GetEngineName() + " does not support text to speech")
|
@ -1,4 +1,4 @@
|
||||
package bingtranslate
|
||||
package bing
|
||||
|
||||
var (
|
||||
lang = []string{
|
@ -1,4 +1,4 @@
|
||||
package bingtranslate
|
||||
package bing
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
@ -24,7 +24,8 @@ const (
|
||||
ttsSSML = "<speak version='1.0' xml:lang='%[1]s'><voice xml:lang='%[1]s' xml:gender='Female' name='%s'><prosody rate='-20.00%%'>%s</prosody></voice></speak>"
|
||||
)
|
||||
|
||||
type BingTranslate struct {
|
||||
type Translator struct {
|
||||
*core.APIKey
|
||||
*core.Language
|
||||
*core.TTSLock
|
||||
core.EngineName
|
||||
@ -37,20 +38,21 @@ type setUpData struct {
|
||||
token string
|
||||
}
|
||||
|
||||
func NewBingTranslate() *BingTranslate {
|
||||
return &BingTranslate{
|
||||
Language: core.NewLanguage(),
|
||||
func NewTranslator() *Translator {
|
||||
return &Translator{
|
||||
APIKey: new(core.APIKey),
|
||||
Language: new(core.Language),
|
||||
TTSLock: core.NewTTSLock(),
|
||||
EngineName: core.NewEngineName("BingTranslate"),
|
||||
EngineName: core.NewEngineName("Bing"),
|
||||
}
|
||||
}
|
||||
|
||||
func (t *BingTranslate) GetAllLang() []string {
|
||||
func (t *Translator) GetAllLang() []string {
|
||||
return lang
|
||||
}
|
||||
|
||||
func (t *BingTranslate) setUp() (*setUpData, error) {
|
||||
var data setUpData
|
||||
func (t *Translator) setUp() (*setUpData, error) {
|
||||
data := new(setUpData)
|
||||
|
||||
res, err := http.Get(setUpURL)
|
||||
if err != nil {
|
||||
@ -79,15 +81,16 @@ func (t *BingTranslate) setUp() (*setUpData, error) {
|
||||
data.key = paramsStr[0]
|
||||
data.token = paramsStr[1][1 : len(paramsStr[1])-1]
|
||||
|
||||
return &data, nil
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func (t *BingTranslate) Translate(message string) (translation, definition, partOfSpeech string, err error) {
|
||||
func (t *Translator) Translate(message string) (translation *core.Translation, err error) {
|
||||
translation = new(core.Translation)
|
||||
var data []interface{}
|
||||
|
||||
initData, err := t.setUp()
|
||||
if err != nil {
|
||||
return "", "", "", err
|
||||
return nil, err
|
||||
}
|
||||
userData := url.Values{
|
||||
"fromLang": {langCode[t.GetSrcLang()]},
|
||||
@ -104,23 +107,23 @@ func (t *BingTranslate) Translate(message string) (translation, definition, part
|
||||
req.Header.Add("User-Agent", core.UserAgent)
|
||||
res, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return "", "", "", err
|
||||
return nil, err
|
||||
}
|
||||
body, err := ioutil.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
return "", "", "", err
|
||||
return nil, err
|
||||
}
|
||||
if err = json.Unmarshal(body, &data); err != nil {
|
||||
return "", "", "", err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(data) <= 0 {
|
||||
return "", "", "", errors.New("Translation not found")
|
||||
return nil, errors.New("Translation not found")
|
||||
}
|
||||
|
||||
// translation
|
||||
translation = fmt.Sprintf("%v",
|
||||
data[0].(map[string]interface{})["translations"].([]interface{})[0].(map[string]interface{})["text"])
|
||||
translation.TEXT =
|
||||
data[0].(map[string]interface{})["translations"].([]interface{})[0].(map[string]interface{})["text"].(string)
|
||||
|
||||
// request part of speech
|
||||
userData.Del("fromLang")
|
||||
@ -133,11 +136,11 @@ func (t *BingTranslate) Translate(message string) (translation, definition, part
|
||||
req.Header.Add("User-Agent", core.UserAgent)
|
||||
res, err = http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return "", "", "", err
|
||||
return nil, err
|
||||
}
|
||||
body, err = ioutil.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
return "", "", "", err
|
||||
return nil, err
|
||||
}
|
||||
// Bing will return the request with list when success.
|
||||
// Otherwises, it would return map. Then the following err would not be nil.
|
||||
@ -154,13 +157,13 @@ func (t *BingTranslate) Translate(message string) (translation, definition, part
|
||||
}
|
||||
poses.add(pos["posTag"].(string), words)
|
||||
}
|
||||
partOfSpeech = poses.format()
|
||||
translation.POS = poses.format()
|
||||
}
|
||||
|
||||
return translation, definition, partOfSpeech, nil
|
||||
return translation, nil
|
||||
}
|
||||
|
||||
func (t *BingTranslate) PlayTTS(lang, message string) error {
|
||||
func (t *Translator) PlayTTS(lang, message string) error {
|
||||
defer t.ReleaseLock()
|
||||
|
||||
name, ok := voiceName[lang]
|
@ -1,4 +1,4 @@
|
||||
package bingtranslate
|
||||
package bing
|
||||
|
||||
import (
|
||||
"fmt"
|
142
internal/translate/chatgpt/language.go
Normal file
142
internal/translate/chatgpt/language.go
Normal file
@ -0,0 +1,142 @@
|
||||
package chatgpt
|
||||
|
||||
var (
|
||||
// Generated from Google
|
||||
lang = []string{
|
||||
"Afrikaans",
|
||||
"Albanian",
|
||||
"Amharic",
|
||||
"Arabic",
|
||||
"Armenian",
|
||||
"Auto",
|
||||
"Assamese",
|
||||
"Aymara",
|
||||
"Azerbaijani",
|
||||
"Bambara",
|
||||
"Basque",
|
||||
"Belarusian",
|
||||
"Bengali",
|
||||
"Bhojpuri",
|
||||
"Bosnian",
|
||||
"Bulgarian",
|
||||
"Catalan",
|
||||
"Cebuano",
|
||||
"Chinese (Simplified)",
|
||||
"Chinese (Traditional)",
|
||||
"Corsican",
|
||||
"Croatian",
|
||||
"Czech",
|
||||
"Danish",
|
||||
"Dhivehi",
|
||||
"Dogri",
|
||||
"Dutch",
|
||||
"English",
|
||||
"Esperanto",
|
||||
"Estonian",
|
||||
"Ewe",
|
||||
"Filipino (Tagalog)",
|
||||
"Finnish",
|
||||
"French",
|
||||
"Frisian",
|
||||
"Galician",
|
||||
"Georgian",
|
||||
"German",
|
||||
"Greek",
|
||||
"Guarani",
|
||||
"Gujarati",
|
||||
"Haitian Creole",
|
||||
"Hausa",
|
||||
"Hawaiian",
|
||||
"Hebrew",
|
||||
"Hindi",
|
||||
"Hmong",
|
||||
"Hungarian",
|
||||
"Icelandic",
|
||||
"Igbo",
|
||||
"Ilocano",
|
||||
"Indonesian",
|
||||
"Irish",
|
||||
"Italian",
|
||||
"Japanese",
|
||||
"Javanese",
|
||||
"Kannada",
|
||||
"Kazakh",
|
||||
"Khmer",
|
||||
"Kinyarwanda",
|
||||
"Konkani",
|
||||
"Korean",
|
||||
"Krio",
|
||||
"Kurdish",
|
||||
"Kurdish (Sorani)",
|
||||
"Kyrgyz",
|
||||
"Lao",
|
||||
"Latin",
|
||||
"Latvian",
|
||||
"Lingala",
|
||||
"Lithuanian",
|
||||
"Luganda",
|
||||
"Luxembourgish",
|
||||
"Macedonian",
|
||||
"Maithili",
|
||||
"Malagasy",
|
||||
"Malay",
|
||||
"Malayalam",
|
||||
"Maltese",
|
||||
"Maori",
|
||||
"Marathi",
|
||||
"Meiteilon (Manipuri)",
|
||||
"Mizo",
|
||||
"Mongolian",
|
||||
"Myanmar (Burmese)",
|
||||
"Nepali",
|
||||
"Norwegian",
|
||||
"Nyanja (Chichewa)",
|
||||
"Odia (Oriya)",
|
||||
"Oromo",
|
||||
"Pashto",
|
||||
"Persian",
|
||||
"Polish",
|
||||
"Portuguese (Portugal, Brazil)",
|
||||
"Punjabi",
|
||||
"Quechua",
|
||||
"Romanian",
|
||||
"Russian",
|
||||
"Samoan",
|
||||
"Sanskrit",
|
||||
"Scots Gaelic",
|
||||
"Sepedi",
|
||||
"Serbian",
|
||||
"Sesotho",
|
||||
"Shona",
|
||||
"Sindhi",
|
||||
"Sinhala (Sinhalese)",
|
||||
"Slovak",
|
||||
"Slovenian",
|
||||
"Somali",
|
||||
"Spanish",
|
||||
"Sundanese",
|
||||
"Swahili",
|
||||
"Swedish",
|
||||
"Tagalog (Filipino)",
|
||||
"Tajik",
|
||||
"Tamil",
|
||||
"Tatar",
|
||||
"Telugu",
|
||||
"Thai",
|
||||
"Tigrinya",
|
||||
"Tsonga",
|
||||
"Turkish",
|
||||
"Turkmen",
|
||||
"Twi (Akan)",
|
||||
"Ukrainian",
|
||||
"Urdu",
|
||||
"Uyghur",
|
||||
"Uzbek",
|
||||
"Vietnamese",
|
||||
"Welsh",
|
||||
"Xhosa",
|
||||
"Yiddish",
|
||||
"Yoruba",
|
||||
"Zulu",
|
||||
}
|
||||
)
|
94
internal/translate/chatgpt/translator.go
Normal file
94
internal/translate/chatgpt/translator.go
Normal file
@ -0,0 +1,94 @@
|
||||
package chatgpt
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
|
||||
"github.com/eeeXun/gtt/internal/translate/core"
|
||||
)
|
||||
|
||||
const (
|
||||
textURL = "https://api.openai.com/v1/chat/completions"
|
||||
)
|
||||
|
||||
type Translator struct {
|
||||
*core.APIKey
|
||||
*core.Language
|
||||
*core.TTSLock
|
||||
core.EngineName
|
||||
}
|
||||
|
||||
func NewTranslator() *Translator {
|
||||
return &Translator{
|
||||
APIKey: new(core.APIKey),
|
||||
Language: new(core.Language),
|
||||
TTSLock: core.NewTTSLock(),
|
||||
EngineName: core.NewEngineName("ChatGPT"),
|
||||
}
|
||||
}
|
||||
|
||||
func (t *Translator) GetAllLang() []string {
|
||||
return lang
|
||||
}
|
||||
|
||||
func (t *Translator) Translate(message string) (translation *core.Translation, err error) {
|
||||
translation = new(core.Translation)
|
||||
var data map[string]interface{}
|
||||
|
||||
if len(t.GetAPIKey()) <= 0 {
|
||||
return nil, errors.New("Please write your API Key in config file for " + t.GetEngineName())
|
||||
}
|
||||
|
||||
userData, _ := json.Marshal(map[string]interface{}{
|
||||
"model": "gpt-3.5-turbo",
|
||||
"messages": []map[string]string{{
|
||||
"role": "user",
|
||||
"content": fmt.Sprintf(
|
||||
"Translate following text from %s to %s\n%s",
|
||||
t.GetSrcLang(),
|
||||
t.GetDstLang(),
|
||||
message,
|
||||
),
|
||||
}},
|
||||
"temperature": 0.7,
|
||||
})
|
||||
req, _ := http.NewRequest("POST",
|
||||
textURL,
|
||||
bytes.NewBuffer(userData),
|
||||
)
|
||||
req.Header.Add("Content-Type", "application/json")
|
||||
req.Header.Add("Authorization", "Bearer "+t.GetAPIKey())
|
||||
res, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
body, err := ioutil.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err = json.Unmarshal(body, &data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(data) <= 0 {
|
||||
return nil, errors.New("Translation not found")
|
||||
}
|
||||
if data["error"] != nil {
|
||||
return nil, errors.New(data["error"].(map[string]interface{})["message"].(string))
|
||||
}
|
||||
|
||||
translation.TEXT =
|
||||
data["choices"].([]interface{})[0].(map[string]interface{})["message"].(map[string]interface{})["content"].(string)
|
||||
|
||||
return translation, nil
|
||||
}
|
||||
|
||||
func (t *Translator) PlayTTS(lang, message string) error {
|
||||
defer t.ReleaseLock()
|
||||
|
||||
return errors.New(t.GetEngineName() + " does not support text to speech")
|
||||
}
|
13
internal/translate/core/apikey.go
Normal file
13
internal/translate/core/apikey.go
Normal file
@ -0,0 +1,13 @@
|
||||
package core
|
||||
|
||||
type APIKey struct {
|
||||
key string
|
||||
}
|
||||
|
||||
func (k *APIKey) SetAPIKey(key string) {
|
||||
k.key = key
|
||||
}
|
||||
|
||||
func (k *APIKey) GetAPIKey() string {
|
||||
return k.key
|
||||
}
|
@ -5,10 +5,6 @@ type Language struct {
|
||||
dstLang string
|
||||
}
|
||||
|
||||
func NewLanguage() *Language {
|
||||
return &Language{}
|
||||
}
|
||||
|
||||
func (l *Language) GetSrcLang() string {
|
||||
return l.srcLang
|
||||
}
|
||||
|
@ -3,3 +3,14 @@ package core
|
||||
const (
|
||||
UserAgent = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36"
|
||||
)
|
||||
|
||||
type Translation struct {
|
||||
// translation text
|
||||
TEXT string
|
||||
|
||||
// translation definition or example
|
||||
DEF string
|
||||
|
||||
// translation part of speech
|
||||
POS string
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
package googletranslate
|
||||
package google
|
||||
|
||||
// https://cloud.google.com/translate/docs/languages
|
||||
var (
|
||||
@ -9,10 +9,14 @@ var (
|
||||
"Arabic",
|
||||
"Armenian",
|
||||
"Auto",
|
||||
"Assamese",
|
||||
"Aymara",
|
||||
"Azerbaijani",
|
||||
"Bambara",
|
||||
"Basque",
|
||||
"Belarusian",
|
||||
"Bengali",
|
||||
"Bhojpuri",
|
||||
"Bosnian",
|
||||
"Bulgarian",
|
||||
"Catalan",
|
||||
@ -23,10 +27,14 @@ var (
|
||||
"Croatian",
|
||||
"Czech",
|
||||
"Danish",
|
||||
"Dhivehi",
|
||||
"Dogri",
|
||||
"Dutch",
|
||||
"English",
|
||||
"Esperanto",
|
||||
"Estonian",
|
||||
"Ewe",
|
||||
"Filipino (Tagalog)",
|
||||
"Finnish",
|
||||
"French",
|
||||
"Frisian",
|
||||
@ -34,6 +42,7 @@ var (
|
||||
"Georgian",
|
||||
"German",
|
||||
"Greek",
|
||||
"Guarani",
|
||||
"Gujarati",
|
||||
"Haitian Creole",
|
||||
"Hausa",
|
||||
@ -44,6 +53,7 @@ var (
|
||||
"Hungarian",
|
||||
"Icelandic",
|
||||
"Igbo",
|
||||
"Ilocano",
|
||||
"Indonesian",
|
||||
"Irish",
|
||||
"Italian",
|
||||
@ -53,36 +63,48 @@ var (
|
||||
"Kazakh",
|
||||
"Khmer",
|
||||
"Kinyarwanda",
|
||||
"Konkani",
|
||||
"Korean",
|
||||
"Krio",
|
||||
"Kurdish",
|
||||
"Kurdish (Sorani)",
|
||||
"Kyrgyz",
|
||||
"Lao",
|
||||
"Latin",
|
||||
"Latvian",
|
||||
"Lingala",
|
||||
"Lithuanian",
|
||||
"Luganda",
|
||||
"Luxembourgish",
|
||||
"Macedonian",
|
||||
"Maithili",
|
||||
"Malagasy",
|
||||
"Malay",
|
||||
"Malayalam",
|
||||
"Maltese",
|
||||
"Maori",
|
||||
"Marathi",
|
||||
"Meiteilon (Manipuri)",
|
||||
"Mizo",
|
||||
"Mongolian",
|
||||
"Myanmar (Burmese)",
|
||||
"Nepali",
|
||||
"Norwegian",
|
||||
"Nyanja (Chichewa)",
|
||||
"Odia (Oriya)",
|
||||
"Oromo",
|
||||
"Pashto",
|
||||
"Persian",
|
||||
"Polish",
|
||||
"Portuguese (Portugal, Brazil)",
|
||||
"Punjabi",
|
||||
"Quechua",
|
||||
"Romanian",
|
||||
"Russian",
|
||||
"Samoan",
|
||||
"Sanskrit",
|
||||
"Scots Gaelic",
|
||||
"Sepedi",
|
||||
"Serbian",
|
||||
"Sesotho",
|
||||
"Shona",
|
||||
@ -101,8 +123,11 @@ var (
|
||||
"Tatar",
|
||||
"Telugu",
|
||||
"Thai",
|
||||
"Tigrinya",
|
||||
"Tsonga",
|
||||
"Turkish",
|
||||
"Turkmen",
|
||||
"Twi (Akan)",
|
||||
"Ukrainian",
|
||||
"Urdu",
|
||||
"Uyghur",
|
||||
@ -121,10 +146,14 @@ var (
|
||||
"Arabic": "ar",
|
||||
"Armenian": "hy",
|
||||
"Auto": "auto",
|
||||
"Assamese": "as",
|
||||
"Aymara": "ay",
|
||||
"Azerbaijani": "az",
|
||||
"Bambara": "bm",
|
||||
"Basque": "eu",
|
||||
"Belarusian": "be",
|
||||
"Bengali": "bn",
|
||||
"Bhojpuri": "bho",
|
||||
"Bosnian": "bs",
|
||||
"Bulgarian": "bg",
|
||||
"Catalan": "ca",
|
||||
@ -135,10 +164,14 @@ var (
|
||||
"Croatian": "hr",
|
||||
"Czech": "cs",
|
||||
"Danish": "da",
|
||||
"Dhivehi": "dv",
|
||||
"Dogri": "doi",
|
||||
"Dutch": "nl",
|
||||
"English": "en",
|
||||
"Esperanto": "eo",
|
||||
"Estonian": "et",
|
||||
"Ewe": "ee",
|
||||
"Filipino (Tagalog)": "fil",
|
||||
"Finnish": "fi",
|
||||
"French": "fr",
|
||||
"Frisian": "fy",
|
||||
@ -146,6 +179,7 @@ var (
|
||||
"Georgian": "ka",
|
||||
"German": "de",
|
||||
"Greek": "el",
|
||||
"Guarani": "gn",
|
||||
"Gujarati": "gu",
|
||||
"Haitian Creole": "ht",
|
||||
"Hausa": "ha",
|
||||
@ -156,6 +190,7 @@ var (
|
||||
"Hungarian": "hu",
|
||||
"Icelandic": "is",
|
||||
"Igbo": "ig",
|
||||
"Ilocano": "ilo",
|
||||
"Indonesian": "id",
|
||||
"Irish": "ga",
|
||||
"Italian": "it",
|
||||
@ -165,36 +200,48 @@ var (
|
||||
"Kazakh": "kk",
|
||||
"Khmer": "km",
|
||||
"Kinyarwanda": "rw",
|
||||
"Konkani": "gom",
|
||||
"Korean": "ko",
|
||||
"Krio": "kri",
|
||||
"Kurdish": "ku",
|
||||
"Kurdish (Sorani)": "ckb",
|
||||
"Kyrgyz": "ky",
|
||||
"Lao": "lo",
|
||||
"Latin": "la",
|
||||
"Latvian": "lv",
|
||||
"Lingala": "ln",
|
||||
"Lithuanian": "lt",
|
||||
"Luganda": "lg",
|
||||
"Luxembourgish": "lb",
|
||||
"Macedonian": "mk",
|
||||
"Maithili": "mai",
|
||||
"Malagasy": "mg",
|
||||
"Malay": "ms",
|
||||
"Malayalam": "ml",
|
||||
"Maltese": "mt",
|
||||
"Maori": "mi",
|
||||
"Marathi": "mr",
|
||||
"Meiteilon (Manipuri)": "mni-Mtei",
|
||||
"Mizo": "lus",
|
||||
"Mongolian": "mn",
|
||||
"Myanmar (Burmese)": "my",
|
||||
"Nepali": "ne",
|
||||
"Norwegian": "no",
|
||||
"Nyanja (Chichewa)": "ny",
|
||||
"Odia (Oriya)": "or",
|
||||
"Oromo": "om",
|
||||
"Pashto": "ps",
|
||||
"Persian": "fa",
|
||||
"Polish": "pl",
|
||||
"Portuguese (Portugal, Brazil)": "pt",
|
||||
"Punjabi": "pa",
|
||||
"Quechua": "qu",
|
||||
"Romanian": "ro",
|
||||
"Russian": "ru",
|
||||
"Samoan": "sm",
|
||||
"Sanskrit": "sa",
|
||||
"Scots Gaelic": "gd",
|
||||
"Sepedi": "nso",
|
||||
"Serbian": "sr",
|
||||
"Sesotho": "st",
|
||||
"Shona": "sn",
|
||||
@ -213,8 +260,11 @@ var (
|
||||
"Tatar": "tt",
|
||||
"Telugu": "te",
|
||||
"Thai": "th",
|
||||
"Tigrinya": "ti",
|
||||
"Tsonga": "ts",
|
||||
"Turkish": "tr",
|
||||
"Turkmen": "tk",
|
||||
"Twi (Akan)": "ak",
|
||||
"Ukrainian": "uk",
|
||||
"Urdu": "ur",
|
||||
"Uyghur": "ug",
|
@ -1,4 +1,4 @@
|
||||
package googletranslate
|
||||
package google
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
@ -19,25 +19,28 @@ const (
|
||||
ttsURL = "https://translate.google.com.vn/translate_tts?ie=UTF-8&q=%s&tl=%s&client=tw-ob"
|
||||
)
|
||||
|
||||
type GoogleTranslate struct {
|
||||
type Translator struct {
|
||||
*core.APIKey
|
||||
*core.Language
|
||||
*core.TTSLock
|
||||
core.EngineName
|
||||
}
|
||||
|
||||
func NewGoogleTranslate() *GoogleTranslate {
|
||||
return &GoogleTranslate{
|
||||
Language: core.NewLanguage(),
|
||||
func NewTranslator() *Translator {
|
||||
return &Translator{
|
||||
APIKey: new(core.APIKey),
|
||||
Language: new(core.Language),
|
||||
TTSLock: core.NewTTSLock(),
|
||||
EngineName: core.NewEngineName("GoogleTranslate"),
|
||||
EngineName: core.NewEngineName("Google"),
|
||||
}
|
||||
}
|
||||
|
||||
func (t *GoogleTranslate) GetAllLang() []string {
|
||||
func (t *Translator) GetAllLang() []string {
|
||||
return lang
|
||||
}
|
||||
|
||||
func (t *GoogleTranslate) Translate(message string) (translation, definition, partOfSpeech string, err error) {
|
||||
func (t *Translator) Translate(message string) (translation *core.Translation, err error) {
|
||||
translation = new(core.Translation)
|
||||
var data []interface{}
|
||||
|
||||
urlStr := fmt.Sprintf(
|
||||
@ -48,24 +51,24 @@ func (t *GoogleTranslate) Translate(message string) (translation, definition, pa
|
||||
)
|
||||
res, err := http.Get(urlStr)
|
||||
if err != nil {
|
||||
return "", "", "", err
|
||||
return nil, err
|
||||
}
|
||||
body, err := ioutil.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
return "", "", "", err
|
||||
return nil, err
|
||||
}
|
||||
if err = json.Unmarshal(body, &data); err != nil {
|
||||
return "", "", "", err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(data) <= 0 {
|
||||
return "", "", "", errors.New("Translation not found")
|
||||
return nil, errors.New("Translation not found")
|
||||
}
|
||||
|
||||
// translation = data[0]
|
||||
for _, line := range data[0].([]interface{}) {
|
||||
translatedLine := line.([]interface{})[0]
|
||||
translation += fmt.Sprintf("%v", translatedLine)
|
||||
translation.TEXT += translatedLine.(string)
|
||||
}
|
||||
// part of speech = data[1]
|
||||
if data[1] != nil {
|
||||
@ -73,23 +76,23 @@ func (t *GoogleTranslate) Translate(message string) (translation, definition, pa
|
||||
partOfSpeeches := partOfSpeeches.([]interface{})
|
||||
// part of speech
|
||||
pos := partOfSpeeches[0]
|
||||
partOfSpeech += fmt.Sprintf("[%v]\n", pos)
|
||||
translation.POS += fmt.Sprintf("[%v]\n", pos)
|
||||
for _, words := range partOfSpeeches[2].([]interface{}) {
|
||||
words := words.([]interface{})
|
||||
// dst lang
|
||||
dstWord := words[0]
|
||||
partOfSpeech += fmt.Sprintf("\t%v:", dstWord)
|
||||
translation.POS += fmt.Sprintf("\t%v:", dstWord)
|
||||
// src lang
|
||||
firstWord := true
|
||||
for _, word := range words[1].([]interface{}) {
|
||||
if firstWord {
|
||||
partOfSpeech += fmt.Sprintf(" %v", word)
|
||||
translation.POS += fmt.Sprintf(" %v", word)
|
||||
firstWord = false
|
||||
} else {
|
||||
partOfSpeech += fmt.Sprintf(", %v", word)
|
||||
translation.POS += fmt.Sprintf(", %v", word)
|
||||
}
|
||||
}
|
||||
partOfSpeech += "\n"
|
||||
translation.POS += "\n"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -99,25 +102,25 @@ func (t *GoogleTranslate) Translate(message string) (translation, definition, pa
|
||||
definitions := definitions.([]interface{})
|
||||
// part of speech
|
||||
pos := definitions[0]
|
||||
definition += fmt.Sprintf("[%v]\n", pos)
|
||||
translation.DEF += fmt.Sprintf("[%v]\n", pos)
|
||||
for _, sentences := range definitions[1].([]interface{}) {
|
||||
sentences := sentences.([]interface{})
|
||||
// definition
|
||||
def := sentences[0]
|
||||
definition += fmt.Sprintf("\t- %v\n", def)
|
||||
translation.DEF += fmt.Sprintf("\t- %v\n", def)
|
||||
// example sentence
|
||||
if len(sentences) >= 3 && sentences[2] != nil {
|
||||
example := sentences[2]
|
||||
definition += fmt.Sprintf("\t\t\"%v\"\n", example)
|
||||
translation.DEF += fmt.Sprintf("\t\t\"%v\"\n", example)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return translation, definition, partOfSpeech, nil
|
||||
return translation, nil
|
||||
}
|
||||
|
||||
func (t *GoogleTranslate) PlayTTS(lang, message string) error {
|
||||
func (t *Translator) PlayTTS(lang, message string) error {
|
||||
defer t.ReleaseLock()
|
||||
|
||||
urlStr := fmt.Sprintf(
|
||||
@ -129,6 +132,9 @@ func (t *GoogleTranslate) PlayTTS(lang, message string) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if res.StatusCode == 400 {
|
||||
return errors.New(t.GetEngineName() + " does not support text to speech of " + lang)
|
||||
}
|
||||
decoder, err := mp3.NewDecoder(res.Body)
|
||||
if err != nil {
|
||||
return err
|
@ -1,4 +1,4 @@
|
||||
package reversotranslate
|
||||
package reverso
|
||||
|
||||
var (
|
||||
lang = []string{
|
@ -1,4 +1,4 @@
|
||||
package reversotranslate
|
||||
package reverso
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
@ -21,29 +21,32 @@ const (
|
||||
ttsURL = "https://voice.reverso.net/RestPronunciation.svc/v1/output=json/GetVoiceStream/voiceName=%s?voiceSpeed=80&inputText=%s"
|
||||
)
|
||||
|
||||
type ReversoTranslate struct {
|
||||
type Translator struct {
|
||||
*core.APIKey
|
||||
*core.Language
|
||||
*core.TTSLock
|
||||
core.EngineName
|
||||
}
|
||||
|
||||
func NewReversoTranslate() *ReversoTranslate {
|
||||
return &ReversoTranslate{
|
||||
Language: core.NewLanguage(),
|
||||
func NewTranslator() *Translator {
|
||||
return &Translator{
|
||||
APIKey: new(core.APIKey),
|
||||
Language: new(core.Language),
|
||||
TTSLock: core.NewTTSLock(),
|
||||
EngineName: core.NewEngineName("ReversoTranslate"),
|
||||
EngineName: core.NewEngineName("Reverso"),
|
||||
}
|
||||
}
|
||||
|
||||
func (t *ReversoTranslate) GetAllLang() []string {
|
||||
func (t *Translator) GetAllLang() []string {
|
||||
return lang
|
||||
}
|
||||
|
||||
func (t *ReversoTranslate) Translate(message string) (translation, definition, partOfSpeech string, err error) {
|
||||
func (t *Translator) Translate(message string) (translation *core.Translation, err error) {
|
||||
translation = new(core.Translation)
|
||||
var data map[string]interface{}
|
||||
|
||||
if t.GetSrcLang() == t.GetDstLang() {
|
||||
return "", "", "", errors.New(
|
||||
return nil, errors.New(
|
||||
fmt.Sprintf("%s doesn't support translation of the same language.\ni.e. %s to %s",
|
||||
t.GetEngineName(), t.GetSrcLang(), t.GetDstLang()))
|
||||
}
|
||||
@ -62,28 +65,28 @@ func (t *ReversoTranslate) Translate(message string) (translation, definition, p
|
||||
})
|
||||
req, _ := http.NewRequest("POST",
|
||||
textURL,
|
||||
bytes.NewBuffer([]byte(userData)))
|
||||
bytes.NewBuffer(userData))
|
||||
req.Header.Add("Content-Type", "application/json")
|
||||
req.Header.Add("User-Agent", core.UserAgent)
|
||||
res, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return "", "", "", err
|
||||
return nil, err
|
||||
}
|
||||
body, err := ioutil.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
return "", "", "", err
|
||||
return nil, err
|
||||
}
|
||||
if err = json.Unmarshal(body, &data); err != nil {
|
||||
return "", "", "", err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(data) <= 0 {
|
||||
return "", "", "", errors.New("Translation not found")
|
||||
return nil, errors.New("Translation not found")
|
||||
}
|
||||
|
||||
// translation
|
||||
for _, line := range data["translation"].([]interface{}) {
|
||||
translation += fmt.Sprintf("%v", line)
|
||||
translation.TEXT += line.(string)
|
||||
}
|
||||
// definition and part of speech
|
||||
if data["contextResults"] != nil {
|
||||
@ -94,23 +97,23 @@ func (t *ReversoTranslate) Translate(message string) (translation, definition, p
|
||||
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])
|
||||
translation.DEF += 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"])
|
||||
translation.POS += fmt.Sprintf("%v\n", results["translation"])
|
||||
} else {
|
||||
partOfSpeech += fmt.Sprintf("%v [%v]\n", results["translation"], results["partOfSpeech"])
|
||||
translation.POS += fmt.Sprintf("%v [%v]\n", results["translation"], results["partOfSpeech"])
|
||||
}
|
||||
}
|
||||
definition = regexp.MustCompile("<(|/)em>").ReplaceAllString(definition, "")
|
||||
translation.DEF = regexp.MustCompile("<(|/)em>").ReplaceAllString(translation.DEF, "")
|
||||
}
|
||||
|
||||
return translation, definition, partOfSpeech, nil
|
||||
return translation, nil
|
||||
}
|
||||
|
||||
func (t *ReversoTranslate) PlayTTS(lang, message string) error {
|
||||
func (t *Translator) PlayTTS(lang, message string) error {
|
||||
defer t.ReleaseLock()
|
||||
|
||||
name, ok := voiceName[lang]
|
@ -1,20 +1,23 @@
|
||||
package translate
|
||||
|
||||
import (
|
||||
"github.com/eeeXun/gtt/internal/translate/apertiumtranslate"
|
||||
"github.com/eeeXun/gtt/internal/translate/argostranslate"
|
||||
"github.com/eeeXun/gtt/internal/translate/bingtranslate"
|
||||
"github.com/eeeXun/gtt/internal/translate/googletranslate"
|
||||
"github.com/eeeXun/gtt/internal/translate/reversotranslate"
|
||||
"github.com/eeeXun/gtt/internal/translate/apertium"
|
||||
"github.com/eeeXun/gtt/internal/translate/argos"
|
||||
"github.com/eeeXun/gtt/internal/translate/bing"
|
||||
"github.com/eeeXun/gtt/internal/translate/chatgpt"
|
||||
"github.com/eeeXun/gtt/internal/translate/core"
|
||||
"github.com/eeeXun/gtt/internal/translate/google"
|
||||
"github.com/eeeXun/gtt/internal/translate/reverso"
|
||||
)
|
||||
|
||||
var (
|
||||
AllTranslator = []string{
|
||||
"ApertiumTranslate",
|
||||
"BingTranslate",
|
||||
"ArgosTranslate",
|
||||
"GoogleTranslate",
|
||||
"ReversoTranslate",
|
||||
"Apertium",
|
||||
"Argos",
|
||||
"Bing",
|
||||
"ChatGPT",
|
||||
"Google",
|
||||
"Reverso",
|
||||
}
|
||||
)
|
||||
|
||||
@ -40,6 +43,9 @@ type Translator interface {
|
||||
// Swap source and destination language of the translator
|
||||
SwapLang()
|
||||
|
||||
// Set API Key
|
||||
SetAPIKey(key string)
|
||||
|
||||
// Check if lock is available
|
||||
LockAvailable() bool
|
||||
|
||||
@ -50,7 +56,7 @@ type Translator interface {
|
||||
StopTTS()
|
||||
|
||||
// Translate from source to destination language
|
||||
Translate(message string) (translation, definition, partOfSpeech string, err error)
|
||||
Translate(message string) (translation *core.Translation, err error)
|
||||
|
||||
// Play text to speech
|
||||
PlayTTS(lang, message string) error
|
||||
@ -60,16 +66,18 @@ func NewTranslator(name string) Translator {
|
||||
var translator Translator
|
||||
|
||||
switch name {
|
||||
case "ApertiumTranslate":
|
||||
translator = apertiumtranslate.NewApertiumTranslate()
|
||||
case "ArgosTranslate":
|
||||
translator = argostranslate.NewArgosTranslate()
|
||||
case "BingTranslate":
|
||||
translator = bingtranslate.NewBingTranslate()
|
||||
case "GoogleTranslate":
|
||||
translator = googletranslate.NewGoogleTranslate()
|
||||
case "ReversoTranslate":
|
||||
translator = reversotranslate.NewReversoTranslate()
|
||||
case "Apertium":
|
||||
translator = apertium.NewTranslator()
|
||||
case "Argos":
|
||||
translator = argos.NewTranslator()
|
||||
case "Bing":
|
||||
translator = bing.NewTranslator()
|
||||
case "ChatGPT":
|
||||
translator = chatgpt.NewTranslator()
|
||||
case "Google":
|
||||
translator = google.NewTranslator()
|
||||
case "Reverso":
|
||||
translator = reverso.NewTranslator()
|
||||
}
|
||||
|
||||
return translator
|
||||
|
74
ui.go
74
ui.go
@ -30,9 +30,9 @@ const (
|
||||
[#%[1]s]<C-r>[-]
|
||||
Copy all text in destination of translation window.
|
||||
[#%[1]s]<C-o>[-]
|
||||
Play sound on source of translation window.
|
||||
Play text to speech on source of translation window.
|
||||
[#%[1]s]<C-p>[-]
|
||||
Play sound on destination of translation window.
|
||||
Play text to speech on destination of translation window.
|
||||
[#%[1]s]<C-x>[-]
|
||||
Stop play sound.
|
||||
[#%[1]s]<C-t>[-]
|
||||
@ -233,6 +233,26 @@ func attachItems(center bool, direction int, items ...Item) *tview.Flex {
|
||||
return container
|
||||
}
|
||||
|
||||
func showLangPopout() {
|
||||
mainPage.HidePage("stylePopOut")
|
||||
mainPage.HidePage("keyMapPopOut")
|
||||
mainPage.ShowPage("langPopOut")
|
||||
app.SetFocus(langCycle.GetCurrentUI())
|
||||
}
|
||||
|
||||
func showStylePopout() {
|
||||
mainPage.HidePage("langPopOut")
|
||||
mainPage.HidePage("keyMapPopOut")
|
||||
mainPage.ShowPage("stylePopOut")
|
||||
app.SetFocus(styleCycle.GetCurrentUI())
|
||||
}
|
||||
|
||||
func showKeyMapPopout() {
|
||||
mainPage.HidePage("langPopOut")
|
||||
mainPage.HidePage("stylePopOut")
|
||||
mainPage.ShowPage("keyMapPopOut")
|
||||
}
|
||||
|
||||
func uiInit() {
|
||||
// input/output
|
||||
srcInput.SetBorder(true)
|
||||
@ -296,7 +316,7 @@ func uiInit() {
|
||||
Item{item: attachItems(true, tview.FlexColumn,
|
||||
Item{item: attachItems(false, tview.FlexRow,
|
||||
Item{item: translatorDropDown, fixedSize: 0, proportion: 1, focus: false}),
|
||||
fixedSize: 0, proportion: 2, focus: false}),
|
||||
fixedSize: 0, proportion: 1, focus: false}),
|
||||
fixedSize: 1, proportion: 1, focus: false},
|
||||
Item{item: attachItems(false, tview.FlexColumn,
|
||||
Item{item: srcLangDropDown, fixedSize: 0, proportion: 1, focus: true},
|
||||
@ -402,23 +422,9 @@ func uiInit() {
|
||||
mainPage.HidePage("keyMapPopOut")
|
||||
}
|
||||
})
|
||||
langButton.SetSelectedFunc(func() {
|
||||
mainPage.HidePage("stylePopOut")
|
||||
mainPage.HidePage("keyMapPopOut")
|
||||
mainPage.ShowPage("langPopOut")
|
||||
app.SetFocus(langCycle.GetCurrentUI())
|
||||
})
|
||||
styleButton.SetSelectedFunc(func() {
|
||||
mainPage.HidePage("langPopOut")
|
||||
mainPage.HidePage("keyMapPopOut")
|
||||
mainPage.ShowPage("stylePopOut")
|
||||
app.SetFocus(styleCycle.GetCurrentUI())
|
||||
})
|
||||
keyMapButton.SetSelectedFunc(func() {
|
||||
mainPage.HidePage("langPopOut")
|
||||
mainPage.HidePage("stylePopOut")
|
||||
mainPage.ShowPage("keyMapPopOut")
|
||||
})
|
||||
langButton.SetSelectedFunc(showLangPopout)
|
||||
styleButton.SetSelectedFunc(showStylePopout)
|
||||
keyMapButton.SetSelectedFunc(showKeyMapPopout)
|
||||
}
|
||||
|
||||
func mainPageHandler(event *tcell.EventKey) *tcell.EventKey {
|
||||
@ -454,13 +460,13 @@ func translateWindowHandler(event *tcell.EventKey) *tcell.EventKey {
|
||||
message := srcInput.GetText()
|
||||
// Only translate when message exist
|
||||
if len(message) > 0 {
|
||||
translation, definition, partOfSpeech, err := translator.Translate(message)
|
||||
translation, err := translator.Translate(message)
|
||||
if err != nil {
|
||||
dstOutput.SetText(err.Error())
|
||||
} else {
|
||||
dstOutput.SetText(translation)
|
||||
defOutput.SetText(definition, false)
|
||||
posOutput.SetText(partOfSpeech, false)
|
||||
dstOutput.SetText(translation.TEXT)
|
||||
defOutput.SetText(translation.DEF, false)
|
||||
posOutput.SetText(translation.POS, false)
|
||||
}
|
||||
}
|
||||
case tcell.KeyCtrlQ:
|
||||
@ -494,7 +500,7 @@ func translateWindowHandler(event *tcell.EventKey) *tcell.EventKey {
|
||||
}
|
||||
dstOutput.SetText(srcText)
|
||||
case tcell.KeyCtrlO:
|
||||
// Play source sound
|
||||
// Play text to speech on source of translation window.
|
||||
if translator.LockAvailable() {
|
||||
message := srcInput.GetText()
|
||||
// Only play when message exist
|
||||
@ -504,13 +510,14 @@ func translateWindowHandler(event *tcell.EventKey) *tcell.EventKey {
|
||||
err := translator.PlayTTS(translator.GetSrcLang(), message)
|
||||
if err != nil {
|
||||
srcInput.SetText(err.Error(), true)
|
||||
app.Draw()
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
}
|
||||
case tcell.KeyCtrlP:
|
||||
// Play destination sound
|
||||
// Play text to speech on destination of translation window.
|
||||
if translator.LockAvailable() {
|
||||
message := dstOutput.GetText(false)
|
||||
// Only play when message exist
|
||||
@ -520,6 +527,7 @@ func translateWindowHandler(event *tcell.EventKey) *tcell.EventKey {
|
||||
err := translator.PlayTTS(translator.GetDstLang(), message)
|
||||
if err != nil {
|
||||
dstOutput.SetText(err.Error())
|
||||
app.Draw()
|
||||
}
|
||||
}()
|
||||
}
|
||||
@ -537,19 +545,11 @@ func popOutHandler(event *tcell.EventKey) *tcell.EventKey {
|
||||
|
||||
switch ch {
|
||||
case '1':
|
||||
mainPage.HidePage("stylePopOut")
|
||||
mainPage.HidePage("keyMapPopOut")
|
||||
mainPage.ShowPage("langPopOut")
|
||||
app.SetFocus(langCycle.GetCurrentUI())
|
||||
showLangPopout()
|
||||
case '2':
|
||||
mainPage.HidePage("langPopOut")
|
||||
mainPage.HidePage("keyMapPopOut")
|
||||
mainPage.ShowPage("stylePopOut")
|
||||
app.SetFocus(styleCycle.GetCurrentUI())
|
||||
showStylePopout()
|
||||
case '3':
|
||||
mainPage.HidePage("langPopOut")
|
||||
mainPage.HidePage("stylePopOut")
|
||||
mainPage.ShowPage("keyMapPopOut")
|
||||
showKeyMapPopout()
|
||||
}
|
||||
|
||||
return event
|
||||
|
Loading…
x
Reference in New Issue
Block a user