diff --git a/README.md b/README.md index 6b9e3bd..ff2717d 100644 --- a/README.md +++ b/README.md @@ -6,9 +6,20 @@ Supported Translator: [`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 a API key on [ChatGPT](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](https://user-images.githubusercontent.com/58657914/213123592-5d8bccfb-ff80-4ad6-aaca-03b31c4c2c59.gif) @@ -50,6 +61,7 @@ See available languages on: - [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` diff --git a/config.go b/config.go index 05ef925..7cddd07 100644 --- a/config.go +++ b/config.go @@ -25,6 +25,8 @@ func configInit() { "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", @@ -76,7 +78,7 @@ func configInit() { } } - // setup + // Setup for _, name := range translate.AllTranslator { translators[name] = translate.NewTranslator(name) translators[name].SetSrcLang( @@ -90,7 +92,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) } diff --git a/internal/translate/apertium/translator.go b/internal/translate/apertium/translator.go index b4331fe..ad2ef56 100644 --- a/internal/translate/apertium/translator.go +++ b/internal/translate/apertium/translator.go @@ -16,6 +16,7 @@ const ( ) type Translator struct { + *core.APIKey *core.Language *core.TTSLock core.EngineName @@ -23,6 +24,7 @@ type Translator struct { func NewTranslator() *Translator { return &Translator{ + APIKey: new(core.APIKey), Language: new(core.Language), TTSLock: core.NewTTSLock(), EngineName: core.NewEngineName("Apertium"), diff --git a/internal/translate/argos/translator.go b/internal/translate/argos/translator.go index e8c07e2..1547647 100644 --- a/internal/translate/argos/translator.go +++ b/internal/translate/argos/translator.go @@ -16,6 +16,7 @@ const ( ) type Translator struct { + *core.APIKey *core.Language *core.TTSLock core.EngineName @@ -23,6 +24,7 @@ type Translator struct { func NewTranslator() *Translator { return &Translator{ + APIKey: new(core.APIKey), Language: new(core.Language), TTSLock: core.NewTTSLock(), EngineName: core.NewEngineName("Argos"), diff --git a/internal/translate/bing/translator.go b/internal/translate/bing/translator.go index 81370e8..4d9e3fd 100644 --- a/internal/translate/bing/translator.go +++ b/internal/translate/bing/translator.go @@ -25,6 +25,7 @@ const ( ) type Translator struct { + *core.APIKey *core.Language *core.TTSLock core.EngineName @@ -39,6 +40,7 @@ type setUpData struct { func NewTranslator() *Translator { return &Translator{ + APIKey: new(core.APIKey), Language: new(core.Language), TTSLock: core.NewTTSLock(), EngineName: core.NewEngineName("Bing"), diff --git a/internal/translate/chatgpt/language.go b/internal/translate/chatgpt/language.go new file mode 100644 index 0000000..2b607a3 --- /dev/null +++ b/internal/translate/chatgpt/language.go @@ -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", + } +) diff --git a/internal/translate/chatgpt/translator.go b/internal/translate/chatgpt/translator.go new file mode 100644 index 0000000..dee3c30 --- /dev/null +++ b/internal/translate/chatgpt/translator.go @@ -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") +} diff --git a/internal/translate/core/apikey.go b/internal/translate/core/apikey.go new file mode 100644 index 0000000..9208a34 --- /dev/null +++ b/internal/translate/core/apikey.go @@ -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 +} diff --git a/internal/translate/google/translator.go b/internal/translate/google/translator.go index d040ed7..dd2a7bc 100644 --- a/internal/translate/google/translator.go +++ b/internal/translate/google/translator.go @@ -20,6 +20,7 @@ const ( ) type Translator struct { + *core.APIKey *core.Language *core.TTSLock core.EngineName @@ -27,6 +28,7 @@ type Translator struct { func NewTranslator() *Translator { return &Translator{ + APIKey: new(core.APIKey), Language: new(core.Language), TTSLock: core.NewTTSLock(), EngineName: core.NewEngineName("Google"), diff --git a/internal/translate/reverso/translator.go b/internal/translate/reverso/translator.go index 044c7ad..a02b234 100644 --- a/internal/translate/reverso/translator.go +++ b/internal/translate/reverso/translator.go @@ -22,6 +22,7 @@ const ( ) type Translator struct { + *core.APIKey *core.Language *core.TTSLock core.EngineName @@ -29,6 +30,7 @@ type Translator struct { func NewTranslator() *Translator { return &Translator{ + APIKey: new(core.APIKey), Language: new(core.Language), TTSLock: core.NewTTSLock(), EngineName: core.NewEngineName("Reverso"), diff --git a/internal/translate/translator.go b/internal/translate/translator.go index 708f2fb..4fa6aa4 100644 --- a/internal/translate/translator.go +++ b/internal/translate/translator.go @@ -4,6 +4,7 @@ import ( "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" @@ -14,6 +15,7 @@ var ( "Apertium", "Argos", "Bing", + "ChatGPT", "Google", "Reverso", } @@ -41,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 @@ -67,6 +72,8 @@ func NewTranslator(name string) Translator { translator = argos.NewTranslator() case "Bing": translator = bing.NewTranslator() + case "ChatGPT": + translator = chatgpt.NewTranslator() case "Google": translator = google.NewTranslator() case "Reverso":