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

Merge branch 'master' into theme

This commit is contained in:
eeeXun 2023-05-12 17:32:00 +08:00
commit 2f7037c146
22 changed files with 622 additions and 269 deletions

View File

@ -3,11 +3,22 @@
Google Translate TUI (Originally) Google Translate TUI (Originally)
Supported Translator: Supported Translator:
[`ApertiumTranslate`](https://www.apertium.org/), [`Apertium`](https://www.apertium.org/),
[`ArgosTranslate`](https://translate.argosopentech.com/)(default), [`Argos`](https://translate.argosopentech.com/),
[`BingTranslate`](https://www.bing.com/translator), [`Bing`](https://www.bing.com/translator),
[`GoogleTranslate`](https://translate.google.com/), [`ChatGPT`](https://chat.openai.com/),
[`ReversoTranslate`](https://www.reverso.net/text-translation) [`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 ## ScreenShot
@ -47,11 +58,12 @@ gtt -src "English" -dst "Chinese (Traditional)"
See available languages on: See available languages on:
- [Apertium Translate](https://www.apertium.org/) for `ApertiumTranslate` - [Apertium Translate](https://www.apertium.org/) for `Apertium`
- [argosopentech/argos-translate](https://github.com/argosopentech/argos-translate#supported-languages) for `ArgosTranslate` - [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 `BingTranslate` - [Bing language-support](https://learn.microsoft.com/en-us/azure/cognitive-services/translator/language-support#translation) for `Bing`
- [Google Language support](https://cloud.google.com/translate/docs/languages) for `GoogleTranslate` - `ChatGPT` is same as `Google`. See [Google Language support](https://cloud.google.com/translate/docs/languages)
- [Reverso Translation](https://www.reverso.net/text-translation) for `ReversoTranslate` - [Google Language support](https://cloud.google.com/translate/docs/languages) for `Google`
- [Reverso Translation](https://www.reverso.net/text-translation) for `Reverso`
## Key Map ## Key Map
@ -80,10 +92,10 @@ Copy all text in source of translation window.
Copy all text in destination of translation window. Copy all text in destination of translation window.
`<C-o>` `<C-o>`
Play sound on source of translation window. Play text to speech on source of translation window.
`<C-p>` `<C-p>`
Play sound on destination of translation window. Play text to speech on destination of translation window.
`<C-x>` `<C-x>`
Stop play sound. 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. [`wl-clipboard`](https://github.com/bugaevc/wl-clipboard) for Linux/Wayland to copy text.
`pbcopy` For macOS to copy text.
## Credit ## Credit
[soimort/translate-shell](https://github.com/soimort/translate-shell), [soimort/translate-shell](https://github.com/soimort/translate-shell),

View File

@ -15,22 +15,24 @@ func configInit() {
defaultConfigPath string defaultConfigPath string
themeConfig = config.New() themeConfig = config.New()
defaultConfig = map[string]interface{}{ defaultConfig = map[string]interface{}{
"hide_below": false, "hide_below": false,
"transparent": false, "transparent": false,
"theme": "Gruvbox", "theme": "gruvbox",
"source.border_color": "red", "source.border_color": "red",
"destination.border_color": "blue", "destination.border_color": "blue",
"source.language.apertiumtranslate": "English", "source.language.apertium": "English",
"destination.language.apertiumtranslate": "English", "destination.language.apertium": "English",
"source.language.argostranslate": "English", "source.language.argos": "English",
"destination.language.argostranslate": "English", "destination.language.argos": "English",
"source.language.bingtranslate": "English", "source.language.bing": "English",
"destination.language.bingtranslate": "English", "destination.language.bing": "English",
"source.language.googletranslate": "English", "source.language.chatgpt": "English",
"destination.language.googletranslate": "English", "destination.language.chatgpt": "English",
"source.language.reversotranslate": "English", "source.language.google": "English",
"destination.language.reversotranslate": "English", "destination.language.google": "English",
"translator": "ArgosTranslate", "source.language.reverso": "English",
"destination.language.reverso": "English",
"translator": "Google",
} }
) )
@ -48,7 +50,7 @@ func configInit() {
config.AddConfigPath("$HOME/.config/gtt") config.AddConfigPath("$HOME/.config/gtt")
themeConfig.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 // Otherwise check if config value is missing
if err := config.ReadInConfig(); err != nil { if err := config.ReadInConfig(); err != nil {
for key, value := range defaultConfig { for key, value := range defaultConfig {
@ -66,6 +68,16 @@ func configInit() {
missing = true 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 { if missing {
config.WriteConfig() config.WriteConfig()
} }
@ -84,7 +96,7 @@ func configInit() {
} }
} }
// setup // Setup
for _, name := range translate.AllTranslator { for _, name := range translate.AllTranslator {
translators[name] = translate.NewTranslator(name) translators[name] = translate.NewTranslator(name)
translators[name].SetSrcLang( translators[name].SetSrcLang(
@ -98,7 +110,11 @@ func configInit() {
uiStyle.Transparent = config.GetBool("transparent") uiStyle.Transparent = config.GetBool("transparent")
uiStyle.SetSrcBorderColor(config.GetString("source.border_color")). uiStyle.SetSrcBorderColor(config.GetString("source.border_color")).
SetDstBorderColor(config.GetString("destination.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 { if len(*srcLangArg) > 0 {
translator.SetSrcLang(*srcLangArg) translator.SetSrcLang(*srcLangArg)
} }

View File

@ -5,10 +5,10 @@ import (
) )
var ( var (
AllTheme = []string{"Gruvbox", "Nord"} AllTheme = []string{"gruvbox", "nord"}
Palette = []string{"red", "green", "yellow", "blue", "purple", "cyan", "orange"} Palette = []string{"red", "green", "yellow", "blue", "purple", "cyan", "orange"}
themes = map[string]map[string]tcell.Color{ themes = map[string]map[string]tcell.Color{
"Gruvbox": { "gruvbox": {
"bg": tcell.NewHexColor(0x282828), "bg": tcell.NewHexColor(0x282828),
"fg": tcell.NewHexColor(0xebdbb2), "fg": tcell.NewHexColor(0xebdbb2),
"gray": tcell.NewHexColor(0x665c54), "gray": tcell.NewHexColor(0x665c54),
@ -20,7 +20,7 @@ var (
"cyan": tcell.NewHexColor(0x8ec07c), "cyan": tcell.NewHexColor(0x8ec07c),
"orange": tcell.NewHexColor(0xfe8019), "orange": tcell.NewHexColor(0xfe8019),
}, },
"Nord": { "nord": {
"bg": tcell.NewHexColor(0x3b4252), "bg": tcell.NewHexColor(0x3b4252),
"fg": tcell.NewHexColor(0xeceff4), "fg": tcell.NewHexColor(0xeceff4),
"gray": tcell.NewHexColor(0x4c566a), "gray": tcell.NewHexColor(0x4c566a),

View File

@ -1,4 +1,4 @@
package apertiumtranslate package apertium
var ( var (
lang = []string{ lang = []string{

View 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")
}

View File

@ -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")
}

View File

@ -1,4 +1,4 @@
package argostranslate package argos
var ( var (
lang = []string{ lang = []string{

View File

@ -1,9 +1,8 @@
package argostranslate package argos
import ( import (
"encoding/json" "encoding/json"
"errors" "errors"
"fmt"
"io/ioutil" "io/ioutil"
"net/http" "net/http"
"net/url" "net/url"
@ -15,25 +14,28 @@ const (
textURL = "https://translate.argosopentech.com/translate" textURL = "https://translate.argosopentech.com/translate"
) )
type ArgosTranslate struct { type Translator struct {
*core.APIKey
*core.Language *core.Language
*core.TTSLock *core.TTSLock
core.EngineName core.EngineName
} }
func NewArgosTranslate() *ArgosTranslate { func NewTranslator() *Translator {
return &ArgosTranslate{ return &Translator{
Language: core.NewLanguage(), APIKey: new(core.APIKey),
Language: new(core.Language),
TTSLock: core.NewTTSLock(), TTSLock: core.NewTTSLock(),
EngineName: core.NewEngineName("ArgosTranslate"), EngineName: core.NewEngineName("Argos"),
} }
} }
func (t *ArgosTranslate) GetAllLang() []string { func (t *Translator) GetAllLang() []string {
return lang 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{} var data map[string]interface{}
res, err := http.PostForm(textURL, res, err := http.PostForm(textURL,
@ -43,26 +45,26 @@ func (t *ArgosTranslate) Translate(message string) (translation, definition, par
"target": {langCode[t.GetDstLang()]}, "target": {langCode[t.GetDstLang()]},
}) })
if err != nil { if err != nil {
return "", "", "", err return nil, err
} }
body, err := ioutil.ReadAll(res.Body) body, err := ioutil.ReadAll(res.Body)
if err != nil { if err != nil {
return "", "", "", err return nil, err
} }
if err = json.Unmarshal(body, &data); err != nil { if err = json.Unmarshal(body, &data); err != nil {
return "", "", "", err return nil, err
} }
if len(data) <= 0 { 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() defer t.ReleaseLock()
return errors.New(t.GetEngineName() + " does not support text to speech") return errors.New(t.GetEngineName() + " does not support text to speech")

View File

@ -1,4 +1,4 @@
package bingtranslate package bing
var ( var (
lang = []string{ lang = []string{

View File

@ -1,4 +1,4 @@
package bingtranslate package bing
import ( import (
"encoding/json" "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>" 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.Language
*core.TTSLock *core.TTSLock
core.EngineName core.EngineName
@ -37,20 +38,21 @@ type setUpData struct {
token string token string
} }
func NewBingTranslate() *BingTranslate { func NewTranslator() *Translator {
return &BingTranslate{ return &Translator{
Language: core.NewLanguage(), APIKey: new(core.APIKey),
Language: new(core.Language),
TTSLock: core.NewTTSLock(), TTSLock: core.NewTTSLock(),
EngineName: core.NewEngineName("BingTranslate"), EngineName: core.NewEngineName("Bing"),
} }
} }
func (t *BingTranslate) GetAllLang() []string { func (t *Translator) GetAllLang() []string {
return lang return lang
} }
func (t *BingTranslate) setUp() (*setUpData, error) { func (t *Translator) setUp() (*setUpData, error) {
var data setUpData data := new(setUpData)
res, err := http.Get(setUpURL) res, err := http.Get(setUpURL)
if err != nil { if err != nil {
@ -79,15 +81,16 @@ func (t *BingTranslate) setUp() (*setUpData, error) {
data.key = paramsStr[0] data.key = paramsStr[0]
data.token = paramsStr[1][1 : len(paramsStr[1])-1] 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{} var data []interface{}
initData, err := t.setUp() initData, err := t.setUp()
if err != nil { if err != nil {
return "", "", "", err return nil, err
} }
userData := url.Values{ userData := url.Values{
"fromLang": {langCode[t.GetSrcLang()]}, "fromLang": {langCode[t.GetSrcLang()]},
@ -104,23 +107,23 @@ func (t *BingTranslate) Translate(message string) (translation, definition, part
req.Header.Add("User-Agent", core.UserAgent) req.Header.Add("User-Agent", core.UserAgent)
res, err := http.DefaultClient.Do(req) res, err := http.DefaultClient.Do(req)
if err != nil { if err != nil {
return "", "", "", err return nil, err
} }
body, err := ioutil.ReadAll(res.Body) body, err := ioutil.ReadAll(res.Body)
if err != nil { if err != nil {
return "", "", "", err return nil, err
} }
if err = json.Unmarshal(body, &data); err != nil { if err = json.Unmarshal(body, &data); err != nil {
return "", "", "", err return nil, err
} }
if len(data) <= 0 { if len(data) <= 0 {
return "", "", "", errors.New("Translation not found") return nil, errors.New("Translation not found")
} }
// translation // translation
translation = fmt.Sprintf("%v", translation.TEXT =
data[0].(map[string]interface{})["translations"].([]interface{})[0].(map[string]interface{})["text"]) data[0].(map[string]interface{})["translations"].([]interface{})[0].(map[string]interface{})["text"].(string)
// request part of speech // request part of speech
userData.Del("fromLang") userData.Del("fromLang")
@ -133,11 +136,11 @@ func (t *BingTranslate) Translate(message string) (translation, definition, part
req.Header.Add("User-Agent", core.UserAgent) req.Header.Add("User-Agent", core.UserAgent)
res, err = http.DefaultClient.Do(req) res, err = http.DefaultClient.Do(req)
if err != nil { if err != nil {
return "", "", "", err return nil, err
} }
body, err = ioutil.ReadAll(res.Body) body, err = ioutil.ReadAll(res.Body)
if err != nil { if err != nil {
return "", "", "", err return nil, err
} }
// Bing will return the request with list when success. // Bing will return the request with list when success.
// Otherwises, it would return map. Then the following err would not be nil. // 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) 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() defer t.ReleaseLock()
name, ok := voiceName[lang] name, ok := voiceName[lang]

View File

@ -1,4 +1,4 @@
package bingtranslate package bing
import ( import (
"fmt" "fmt"

View 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",
}
)

View 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")
}

View 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
}

View File

@ -5,10 +5,6 @@ type Language struct {
dstLang string dstLang string
} }
func NewLanguage() *Language {
return &Language{}
}
func (l *Language) GetSrcLang() string { func (l *Language) GetSrcLang() string {
return l.srcLang return l.srcLang
} }

View File

@ -3,3 +3,14 @@ package core
const ( const (
UserAgent = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36" 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
}

View File

@ -1,4 +1,4 @@
package googletranslate package google
// https://cloud.google.com/translate/docs/languages // https://cloud.google.com/translate/docs/languages
var ( var (
@ -9,10 +9,14 @@ var (
"Arabic", "Arabic",
"Armenian", "Armenian",
"Auto", "Auto",
"Assamese",
"Aymara",
"Azerbaijani", "Azerbaijani",
"Bambara",
"Basque", "Basque",
"Belarusian", "Belarusian",
"Bengali", "Bengali",
"Bhojpuri",
"Bosnian", "Bosnian",
"Bulgarian", "Bulgarian",
"Catalan", "Catalan",
@ -23,10 +27,14 @@ var (
"Croatian", "Croatian",
"Czech", "Czech",
"Danish", "Danish",
"Dhivehi",
"Dogri",
"Dutch", "Dutch",
"English", "English",
"Esperanto", "Esperanto",
"Estonian", "Estonian",
"Ewe",
"Filipino (Tagalog)",
"Finnish", "Finnish",
"French", "French",
"Frisian", "Frisian",
@ -34,6 +42,7 @@ var (
"Georgian", "Georgian",
"German", "German",
"Greek", "Greek",
"Guarani",
"Gujarati", "Gujarati",
"Haitian Creole", "Haitian Creole",
"Hausa", "Hausa",
@ -44,6 +53,7 @@ var (
"Hungarian", "Hungarian",
"Icelandic", "Icelandic",
"Igbo", "Igbo",
"Ilocano",
"Indonesian", "Indonesian",
"Irish", "Irish",
"Italian", "Italian",
@ -53,36 +63,48 @@ var (
"Kazakh", "Kazakh",
"Khmer", "Khmer",
"Kinyarwanda", "Kinyarwanda",
"Konkani",
"Korean", "Korean",
"Krio",
"Kurdish", "Kurdish",
"Kurdish (Sorani)",
"Kyrgyz", "Kyrgyz",
"Lao", "Lao",
"Latin", "Latin",
"Latvian", "Latvian",
"Lingala",
"Lithuanian", "Lithuanian",
"Luganda",
"Luxembourgish", "Luxembourgish",
"Macedonian", "Macedonian",
"Maithili",
"Malagasy", "Malagasy",
"Malay", "Malay",
"Malayalam", "Malayalam",
"Maltese", "Maltese",
"Maori", "Maori",
"Marathi", "Marathi",
"Meiteilon (Manipuri)",
"Mizo",
"Mongolian", "Mongolian",
"Myanmar (Burmese)", "Myanmar (Burmese)",
"Nepali", "Nepali",
"Norwegian", "Norwegian",
"Nyanja (Chichewa)", "Nyanja (Chichewa)",
"Odia (Oriya)", "Odia (Oriya)",
"Oromo",
"Pashto", "Pashto",
"Persian", "Persian",
"Polish", "Polish",
"Portuguese (Portugal, Brazil)", "Portuguese (Portugal, Brazil)",
"Punjabi", "Punjabi",
"Quechua",
"Romanian", "Romanian",
"Russian", "Russian",
"Samoan", "Samoan",
"Sanskrit",
"Scots Gaelic", "Scots Gaelic",
"Sepedi",
"Serbian", "Serbian",
"Sesotho", "Sesotho",
"Shona", "Shona",
@ -101,8 +123,11 @@ var (
"Tatar", "Tatar",
"Telugu", "Telugu",
"Thai", "Thai",
"Tigrinya",
"Tsonga",
"Turkish", "Turkish",
"Turkmen", "Turkmen",
"Twi (Akan)",
"Ukrainian", "Ukrainian",
"Urdu", "Urdu",
"Uyghur", "Uyghur",
@ -121,10 +146,14 @@ var (
"Arabic": "ar", "Arabic": "ar",
"Armenian": "hy", "Armenian": "hy",
"Auto": "auto", "Auto": "auto",
"Assamese": "as",
"Aymara": "ay",
"Azerbaijani": "az", "Azerbaijani": "az",
"Bambara": "bm",
"Basque": "eu", "Basque": "eu",
"Belarusian": "be", "Belarusian": "be",
"Bengali": "bn", "Bengali": "bn",
"Bhojpuri": "bho",
"Bosnian": "bs", "Bosnian": "bs",
"Bulgarian": "bg", "Bulgarian": "bg",
"Catalan": "ca", "Catalan": "ca",
@ -135,10 +164,14 @@ var (
"Croatian": "hr", "Croatian": "hr",
"Czech": "cs", "Czech": "cs",
"Danish": "da", "Danish": "da",
"Dhivehi": "dv",
"Dogri": "doi",
"Dutch": "nl", "Dutch": "nl",
"English": "en", "English": "en",
"Esperanto": "eo", "Esperanto": "eo",
"Estonian": "et", "Estonian": "et",
"Ewe": "ee",
"Filipino (Tagalog)": "fil",
"Finnish": "fi", "Finnish": "fi",
"French": "fr", "French": "fr",
"Frisian": "fy", "Frisian": "fy",
@ -146,6 +179,7 @@ var (
"Georgian": "ka", "Georgian": "ka",
"German": "de", "German": "de",
"Greek": "el", "Greek": "el",
"Guarani": "gn",
"Gujarati": "gu", "Gujarati": "gu",
"Haitian Creole": "ht", "Haitian Creole": "ht",
"Hausa": "ha", "Hausa": "ha",
@ -156,6 +190,7 @@ var (
"Hungarian": "hu", "Hungarian": "hu",
"Icelandic": "is", "Icelandic": "is",
"Igbo": "ig", "Igbo": "ig",
"Ilocano": "ilo",
"Indonesian": "id", "Indonesian": "id",
"Irish": "ga", "Irish": "ga",
"Italian": "it", "Italian": "it",
@ -165,36 +200,48 @@ var (
"Kazakh": "kk", "Kazakh": "kk",
"Khmer": "km", "Khmer": "km",
"Kinyarwanda": "rw", "Kinyarwanda": "rw",
"Konkani": "gom",
"Korean": "ko", "Korean": "ko",
"Krio": "kri",
"Kurdish": "ku", "Kurdish": "ku",
"Kurdish (Sorani)": "ckb",
"Kyrgyz": "ky", "Kyrgyz": "ky",
"Lao": "lo", "Lao": "lo",
"Latin": "la", "Latin": "la",
"Latvian": "lv", "Latvian": "lv",
"Lingala": "ln",
"Lithuanian": "lt", "Lithuanian": "lt",
"Luganda": "lg",
"Luxembourgish": "lb", "Luxembourgish": "lb",
"Macedonian": "mk", "Macedonian": "mk",
"Maithili": "mai",
"Malagasy": "mg", "Malagasy": "mg",
"Malay": "ms", "Malay": "ms",
"Malayalam": "ml", "Malayalam": "ml",
"Maltese": "mt", "Maltese": "mt",
"Maori": "mi", "Maori": "mi",
"Marathi": "mr", "Marathi": "mr",
"Meiteilon (Manipuri)": "mni-Mtei",
"Mizo": "lus",
"Mongolian": "mn", "Mongolian": "mn",
"Myanmar (Burmese)": "my", "Myanmar (Burmese)": "my",
"Nepali": "ne", "Nepali": "ne",
"Norwegian": "no", "Norwegian": "no",
"Nyanja (Chichewa)": "ny", "Nyanja (Chichewa)": "ny",
"Odia (Oriya)": "or", "Odia (Oriya)": "or",
"Oromo": "om",
"Pashto": "ps", "Pashto": "ps",
"Persian": "fa", "Persian": "fa",
"Polish": "pl", "Polish": "pl",
"Portuguese (Portugal, Brazil)": "pt", "Portuguese (Portugal, Brazil)": "pt",
"Punjabi": "pa", "Punjabi": "pa",
"Quechua": "qu",
"Romanian": "ro", "Romanian": "ro",
"Russian": "ru", "Russian": "ru",
"Samoan": "sm", "Samoan": "sm",
"Sanskrit": "sa",
"Scots Gaelic": "gd", "Scots Gaelic": "gd",
"Sepedi": "nso",
"Serbian": "sr", "Serbian": "sr",
"Sesotho": "st", "Sesotho": "st",
"Shona": "sn", "Shona": "sn",
@ -213,8 +260,11 @@ var (
"Tatar": "tt", "Tatar": "tt",
"Telugu": "te", "Telugu": "te",
"Thai": "th", "Thai": "th",
"Tigrinya": "ti",
"Tsonga": "ts",
"Turkish": "tr", "Turkish": "tr",
"Turkmen": "tk", "Turkmen": "tk",
"Twi (Akan)": "ak",
"Ukrainian": "uk", "Ukrainian": "uk",
"Urdu": "ur", "Urdu": "ur",
"Uyghur": "ug", "Uyghur": "ug",

View File

@ -1,4 +1,4 @@
package googletranslate package google
import ( import (
"encoding/json" "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" 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.Language
*core.TTSLock *core.TTSLock
core.EngineName core.EngineName
} }
func NewGoogleTranslate() *GoogleTranslate { func NewTranslator() *Translator {
return &GoogleTranslate{ return &Translator{
Language: core.NewLanguage(), APIKey: new(core.APIKey),
Language: new(core.Language),
TTSLock: core.NewTTSLock(), TTSLock: core.NewTTSLock(),
EngineName: core.NewEngineName("GoogleTranslate"), EngineName: core.NewEngineName("Google"),
} }
} }
func (t *GoogleTranslate) GetAllLang() []string { func (t *Translator) GetAllLang() []string {
return lang 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{} var data []interface{}
urlStr := fmt.Sprintf( urlStr := fmt.Sprintf(
@ -48,24 +51,24 @@ func (t *GoogleTranslate) Translate(message string) (translation, definition, pa
) )
res, err := http.Get(urlStr) res, err := http.Get(urlStr)
if err != nil { if err != nil {
return "", "", "", err return nil, err
} }
body, err := ioutil.ReadAll(res.Body) body, err := ioutil.ReadAll(res.Body)
if err != nil { if err != nil {
return "", "", "", err return nil, err
} }
if err = json.Unmarshal(body, &data); err != nil { if err = json.Unmarshal(body, &data); err != nil {
return "", "", "", err return nil, err
} }
if len(data) <= 0 { if len(data) <= 0 {
return "", "", "", errors.New("Translation not found") return nil, errors.New("Translation not found")
} }
// translation = data[0] // translation = data[0]
for _, line := range data[0].([]interface{}) { for _, line := range data[0].([]interface{}) {
translatedLine := line.([]interface{})[0] translatedLine := line.([]interface{})[0]
translation += fmt.Sprintf("%v", translatedLine) translation.TEXT += translatedLine.(string)
} }
// part of speech = data[1] // part of speech = data[1]
if data[1] != nil { if data[1] != nil {
@ -73,23 +76,23 @@ func (t *GoogleTranslate) Translate(message string) (translation, definition, pa
partOfSpeeches := partOfSpeeches.([]interface{}) partOfSpeeches := partOfSpeeches.([]interface{})
// part of speech // part of speech
pos := partOfSpeeches[0] pos := partOfSpeeches[0]
partOfSpeech += fmt.Sprintf("[%v]\n", pos) translation.POS += fmt.Sprintf("[%v]\n", pos)
for _, words := range partOfSpeeches[2].([]interface{}) { for _, words := range partOfSpeeches[2].([]interface{}) {
words := words.([]interface{}) words := words.([]interface{})
// dst lang // dst lang
dstWord := words[0] dstWord := words[0]
partOfSpeech += fmt.Sprintf("\t%v:", dstWord) translation.POS += fmt.Sprintf("\t%v:", dstWord)
// src lang // src lang
firstWord := true firstWord := true
for _, word := range words[1].([]interface{}) { for _, word := range words[1].([]interface{}) {
if firstWord { if firstWord {
partOfSpeech += fmt.Sprintf(" %v", word) translation.POS += fmt.Sprintf(" %v", word)
firstWord = false firstWord = false
} else { } 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{}) definitions := definitions.([]interface{})
// part of speech // part of speech
pos := definitions[0] pos := definitions[0]
definition += fmt.Sprintf("[%v]\n", pos) translation.DEF += fmt.Sprintf("[%v]\n", pos)
for _, sentences := range definitions[1].([]interface{}) { for _, sentences := range definitions[1].([]interface{}) {
sentences := sentences.([]interface{}) sentences := sentences.([]interface{})
// definition // definition
def := sentences[0] def := sentences[0]
definition += fmt.Sprintf("\t- %v\n", def) translation.DEF += fmt.Sprintf("\t- %v\n", def)
// example sentence // example sentence
if len(sentences) >= 3 && sentences[2] != nil { if len(sentences) >= 3 && sentences[2] != nil {
example := sentences[2] 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() defer t.ReleaseLock()
urlStr := fmt.Sprintf( urlStr := fmt.Sprintf(
@ -129,6 +132,9 @@ func (t *GoogleTranslate) PlayTTS(lang, message string) error {
if err != nil { if err != nil {
return err 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) decoder, err := mp3.NewDecoder(res.Body)
if err != nil { if err != nil {
return err return err

View File

@ -1,4 +1,4 @@
package reversotranslate package reverso
var ( var (
lang = []string{ lang = []string{

View File

@ -1,4 +1,4 @@
package reversotranslate package reverso
import ( import (
"bytes" "bytes"
@ -21,29 +21,32 @@ const (
ttsURL = "https://voice.reverso.net/RestPronunciation.svc/v1/output=json/GetVoiceStream/voiceName=%s?voiceSpeed=80&inputText=%s" 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.Language
*core.TTSLock *core.TTSLock
core.EngineName core.EngineName
} }
func NewReversoTranslate() *ReversoTranslate { func NewTranslator() *Translator {
return &ReversoTranslate{ return &Translator{
Language: core.NewLanguage(), APIKey: new(core.APIKey),
Language: new(core.Language),
TTSLock: core.NewTTSLock(), TTSLock: core.NewTTSLock(),
EngineName: core.NewEngineName("ReversoTranslate"), EngineName: core.NewEngineName("Reverso"),
} }
} }
func (t *ReversoTranslate) GetAllLang() []string { func (t *Translator) GetAllLang() []string {
return lang 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{} var data map[string]interface{}
if t.GetSrcLang() == t.GetDstLang() { 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", fmt.Sprintf("%s doesn't support translation of the same language.\ni.e. %s to %s",
t.GetEngineName(), t.GetSrcLang(), t.GetDstLang())) t.GetEngineName(), t.GetSrcLang(), t.GetDstLang()))
} }
@ -62,28 +65,28 @@ func (t *ReversoTranslate) Translate(message string) (translation, definition, p
}) })
req, _ := http.NewRequest("POST", req, _ := http.NewRequest("POST",
textURL, textURL,
bytes.NewBuffer([]byte(userData))) bytes.NewBuffer(userData))
req.Header.Add("Content-Type", "application/json") req.Header.Add("Content-Type", "application/json")
req.Header.Add("User-Agent", core.UserAgent) req.Header.Add("User-Agent", core.UserAgent)
res, err := http.DefaultClient.Do(req) res, err := http.DefaultClient.Do(req)
if err != nil { if err != nil {
return "", "", "", err return nil, err
} }
body, err := ioutil.ReadAll(res.Body) body, err := ioutil.ReadAll(res.Body)
if err != nil { if err != nil {
return "", "", "", err return nil, err
} }
if err = json.Unmarshal(body, &data); err != nil { if err = json.Unmarshal(body, &data); err != nil {
return "", "", "", err return nil, err
} }
if len(data) <= 0 { if len(data) <= 0 {
return "", "", "", errors.New("Translation not found") return nil, errors.New("Translation not found")
} }
// translation // translation
for _, line := range data["translation"].([]interface{}) { for _, line := range data["translation"].([]interface{}) {
translation += fmt.Sprintf("%v", line) translation.TEXT += line.(string)
} }
// definition and part of speech // definition and part of speech
if data["contextResults"] != nil { if data["contextResults"] != nil {
@ -94,23 +97,23 @@ func (t *ReversoTranslate) Translate(message string) (translation, definition, p
dstExample := results["targetExamples"].([]interface{}) dstExample := results["targetExamples"].([]interface{})
if len(srcExample) > 0 && len(dstExample) > 0 { if len(srcExample) > 0 && len(dstExample) > 0 {
for i := 0; i < len(srcExample) && i < len(dstExample); i++ { 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 // part of speech
if results["partOfSpeech"] == nil { if results["partOfSpeech"] == nil {
partOfSpeech += fmt.Sprintf("%v\n", results["translation"]) translation.POS += fmt.Sprintf("%v\n", results["translation"])
} else { } 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() defer t.ReleaseLock()
name, ok := voiceName[lang] name, ok := voiceName[lang]

View File

@ -1,20 +1,23 @@
package translate package translate
import ( import (
"github.com/eeeXun/gtt/internal/translate/apertiumtranslate" "github.com/eeeXun/gtt/internal/translate/apertium"
"github.com/eeeXun/gtt/internal/translate/argostranslate" "github.com/eeeXun/gtt/internal/translate/argos"
"github.com/eeeXun/gtt/internal/translate/bingtranslate" "github.com/eeeXun/gtt/internal/translate/bing"
"github.com/eeeXun/gtt/internal/translate/googletranslate" "github.com/eeeXun/gtt/internal/translate/chatgpt"
"github.com/eeeXun/gtt/internal/translate/reversotranslate" "github.com/eeeXun/gtt/internal/translate/core"
"github.com/eeeXun/gtt/internal/translate/google"
"github.com/eeeXun/gtt/internal/translate/reverso"
) )
var ( var (
AllTranslator = []string{ AllTranslator = []string{
"ApertiumTranslate", "Apertium",
"BingTranslate", "Argos",
"ArgosTranslate", "Bing",
"GoogleTranslate", "ChatGPT",
"ReversoTranslate", "Google",
"Reverso",
} }
) )
@ -40,6 +43,9 @@ type Translator interface {
// Swap source and destination language of the translator // Swap source and destination language of the translator
SwapLang() SwapLang()
// Set API Key
SetAPIKey(key string)
// Check if lock is available // Check if lock is available
LockAvailable() bool LockAvailable() bool
@ -50,7 +56,7 @@ type Translator interface {
StopTTS() StopTTS()
// Translate from source to destination language // 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 // Play text to speech
PlayTTS(lang, message string) error PlayTTS(lang, message string) error
@ -60,16 +66,18 @@ func NewTranslator(name string) Translator {
var translator Translator var translator Translator
switch name { switch name {
case "ApertiumTranslate": case "Apertium":
translator = apertiumtranslate.NewApertiumTranslate() translator = apertium.NewTranslator()
case "ArgosTranslate": case "Argos":
translator = argostranslate.NewArgosTranslate() translator = argos.NewTranslator()
case "BingTranslate": case "Bing":
translator = bingtranslate.NewBingTranslate() translator = bing.NewTranslator()
case "GoogleTranslate": case "ChatGPT":
translator = googletranslate.NewGoogleTranslate() translator = chatgpt.NewTranslator()
case "ReversoTranslate": case "Google":
translator = reversotranslate.NewReversoTranslate() translator = google.NewTranslator()
case "Reverso":
translator = reverso.NewTranslator()
} }
return translator return translator

74
ui.go
View File

@ -30,9 +30,9 @@ const (
[#%[1]s]<C-r>[-] [#%[1]s]<C-r>[-]
Copy all text in destination of translation window. Copy all text in destination of translation window.
[#%[1]s]<C-o>[-] [#%[1]s]<C-o>[-]
Play sound on source of translation window. Play text to speech on source of translation window.
[#%[1]s]<C-p>[-] [#%[1]s]<C-p>[-]
Play sound on destination of translation window. Play text to speech on destination of translation window.
[#%[1]s]<C-x>[-] [#%[1]s]<C-x>[-]
Stop play sound. Stop play sound.
[#%[1]s]<C-t>[-] [#%[1]s]<C-t>[-]
@ -233,6 +233,26 @@ func attachItems(center bool, direction int, items ...Item) *tview.Flex {
return container 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() { func uiInit() {
// input/output // input/output
srcInput.SetBorder(true) srcInput.SetBorder(true)
@ -296,7 +316,7 @@ func uiInit() {
Item{item: attachItems(true, tview.FlexColumn, Item{item: attachItems(true, tview.FlexColumn,
Item{item: attachItems(false, tview.FlexRow, Item{item: attachItems(false, tview.FlexRow,
Item{item: translatorDropDown, fixedSize: 0, proportion: 1, focus: false}), 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}, fixedSize: 1, proportion: 1, focus: false},
Item{item: attachItems(false, tview.FlexColumn, Item{item: attachItems(false, tview.FlexColumn,
Item{item: srcLangDropDown, fixedSize: 0, proportion: 1, focus: true}, Item{item: srcLangDropDown, fixedSize: 0, proportion: 1, focus: true},
@ -402,23 +422,9 @@ func uiInit() {
mainPage.HidePage("keyMapPopOut") mainPage.HidePage("keyMapPopOut")
} }
}) })
langButton.SetSelectedFunc(func() { langButton.SetSelectedFunc(showLangPopout)
mainPage.HidePage("stylePopOut") styleButton.SetSelectedFunc(showStylePopout)
mainPage.HidePage("keyMapPopOut") keyMapButton.SetSelectedFunc(showKeyMapPopout)
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")
})
} }
func mainPageHandler(event *tcell.EventKey) *tcell.EventKey { func mainPageHandler(event *tcell.EventKey) *tcell.EventKey {
@ -454,13 +460,13 @@ func translateWindowHandler(event *tcell.EventKey) *tcell.EventKey {
message := srcInput.GetText() message := srcInput.GetText()
// Only translate when message exist // Only translate when message exist
if len(message) > 0 { if len(message) > 0 {
translation, definition, partOfSpeech, err := translator.Translate(message) translation, err := translator.Translate(message)
if err != nil { if err != nil {
dstOutput.SetText(err.Error()) dstOutput.SetText(err.Error())
} else { } else {
dstOutput.SetText(translation) dstOutput.SetText(translation.TEXT)
defOutput.SetText(definition, false) defOutput.SetText(translation.DEF, false)
posOutput.SetText(partOfSpeech, false) posOutput.SetText(translation.POS, false)
} }
} }
case tcell.KeyCtrlQ: case tcell.KeyCtrlQ:
@ -494,7 +500,7 @@ func translateWindowHandler(event *tcell.EventKey) *tcell.EventKey {
} }
dstOutput.SetText(srcText) dstOutput.SetText(srcText)
case tcell.KeyCtrlO: case tcell.KeyCtrlO:
// Play source sound // Play text to speech on source of translation window.
if translator.LockAvailable() { if translator.LockAvailable() {
message := srcInput.GetText() message := srcInput.GetText()
// Only play when message exist // Only play when message exist
@ -504,13 +510,14 @@ func translateWindowHandler(event *tcell.EventKey) *tcell.EventKey {
err := translator.PlayTTS(translator.GetSrcLang(), message) err := translator.PlayTTS(translator.GetSrcLang(), message)
if err != nil { if err != nil {
srcInput.SetText(err.Error(), true) srcInput.SetText(err.Error(), true)
app.Draw()
} }
}() }()
} }
} }
case tcell.KeyCtrlP: case tcell.KeyCtrlP:
// Play destination sound // Play text to speech on destination of translation window.
if translator.LockAvailable() { if translator.LockAvailable() {
message := dstOutput.GetText(false) message := dstOutput.GetText(false)
// Only play when message exist // Only play when message exist
@ -520,6 +527,7 @@ func translateWindowHandler(event *tcell.EventKey) *tcell.EventKey {
err := translator.PlayTTS(translator.GetDstLang(), message) err := translator.PlayTTS(translator.GetDstLang(), message)
if err != nil { if err != nil {
dstOutput.SetText(err.Error()) dstOutput.SetText(err.Error())
app.Draw()
} }
}() }()
} }
@ -537,19 +545,11 @@ func popOutHandler(event *tcell.EventKey) *tcell.EventKey {
switch ch { switch ch {
case '1': case '1':
mainPage.HidePage("stylePopOut") showLangPopout()
mainPage.HidePage("keyMapPopOut")
mainPage.ShowPage("langPopOut")
app.SetFocus(langCycle.GetCurrentUI())
case '2': case '2':
mainPage.HidePage("langPopOut") showStylePopout()
mainPage.HidePage("keyMapPopOut")
mainPage.ShowPage("stylePopOut")
app.SetFocus(styleCycle.GetCurrentUI())
case '3': case '3':
mainPage.HidePage("langPopOut") showKeyMapPopout()
mainPage.HidePage("stylePopOut")
mainPage.ShowPage("keyMapPopOut")
} }
return event return event