├── go.mod ├── libretranslate_test.go ├── LICENSE ├── README.md └── libretranslate.go /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/snakesel/libretranslate 2 | 3 | go 1.16 4 | -------------------------------------------------------------------------------- /libretranslate_test.go: -------------------------------------------------------------------------------- 1 | package libretranslate 2 | 3 | import ( 4 | "os" 5 | "testing" 6 | ) 7 | 8 | func TestTranslate(t *testing.T) { 9 | translate := New(Config{ 10 | //Url: "https://libretranslate.com", 11 | Key: "XXX", 12 | Debug: os.Stdout, 13 | }) 14 | 15 | text, err := translate.Translate("Hello, World!", "en", "ru") 16 | 17 | if err == nil { 18 | if text != "Привет, Мир!" { 19 | t.Error("[TestTranslate] failed: Translate not valid.") 20 | } 21 | } else { 22 | t.Errorf("[TestTranslate] failed: %s", err.Error()) 23 | } 24 | 25 | } 26 | 27 | func TestDetect(t *testing.T) { 28 | translate := New(Config{ 29 | Url: "https://libretranslate.com/", 30 | Key: "X", 31 | Debug: os.Stdout, 32 | }) 33 | 34 | _, lang, err := translate.Detect("Nächster Stil") 35 | if err == nil { 36 | if lang != "de" { 37 | t.Error("[TestDetect] failed: language not valid.") 38 | } 39 | 40 | } else { 41 | t.Errorf("[TestDetect] failed: %s", err.Error()) 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 SnakeSel 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # LibreTranslate in golang 2 | [LibreTranslate](https://libretranslate.com) is an Open Source Machine Translation 3 | [API Docs](https://libretranslate.com/docs) | [Self-Hosted](https://github.com/uav4geo/LibreTranslate) 4 | 5 | ### Install: 6 | ``` 7 | go get -u github.com/snakesel/libretranslate 8 | ``` 9 | 10 | ### Example usage: 11 | 12 | ```go 13 | package main 14 | 15 | import ( 16 | "fmt" 17 | tr "github.com/snakesel/libretranslate" 18 | ) 19 | 20 | func main() { 21 | translate := tr.New(tr.Config{ 22 | Url: "https://libretranslate.com", 23 | Key: "XXX", 24 | }) 25 | 26 | // you can use "auto" for source language 27 | // so, translator will detect language 28 | trtext, err := translate.Translate("Hello, World!", "auto", "ru") 29 | if err == nil { 30 | fmt.Println(trtext) 31 | } else { 32 | fmt.Println(err.Error()) 33 | } 34 | 35 | // Detect the language of the text 36 | conf, lang, err := translate.Detect("Nächster Stil") 37 | if err == nil { 38 | fmt.Printf("%s (%f)\n", lang, conf) 39 | } else { 40 | fmt.Println(err.Error()) 41 | } 42 | } 43 | ``` 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /libretranslate.go: -------------------------------------------------------------------------------- 1 | package libretranslate 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | 7 | "errors" 8 | "fmt" 9 | "io" 10 | "io/ioutil" 11 | "log" 12 | "net/http" 13 | "net/url" 14 | "path" 15 | ) 16 | 17 | // Config is the configuration struct you should pass to New(). 18 | type Config struct { 19 | Url string 20 | Key string 21 | // Debug is an optional writer which will be used for debug output. 22 | Debug io.Writer 23 | } 24 | type Translation struct { 25 | log *log.Logger 26 | Config 27 | } 28 | 29 | // New returns a new Translation. 30 | func New(conf Config) *Translation { 31 | 32 | tr := new(Translation) 33 | 34 | if conf.Url != "" { 35 | tr.Url = conf.Url 36 | } else { 37 | tr.Url = "https://libretranslate.com" 38 | } 39 | 40 | tr.Key = conf.Key 41 | 42 | if conf.Debug == nil { 43 | conf.Debug = ioutil.Discard 44 | } 45 | 46 | tr.log = log.New(conf.Debug, "[LibreTr]\t", log.LstdFlags) 47 | 48 | return tr 49 | } 50 | 51 | // Translate text from a language to another 52 | func (tr *Translation) Translate(source, sourceLang, targetLang string) (string, error) { 53 | params := url.Values{} 54 | params.Set("q", source) 55 | params.Add("source", sourceLang) 56 | params.Add("target", targetLang) 57 | if len(tr.Key) > 0 { 58 | tr.log.Println("add api key to param") 59 | params.Add("api_key", tr.Key) 60 | } 61 | uri, err := url.Parse(tr.Url) 62 | if err != nil { 63 | tr.log.Println("Error parse url") 64 | return "", err 65 | } 66 | 67 | uri.Path = path.Join(uri.Path, "/translate") 68 | tr.log.Println(uri.String()) 69 | 70 | res, err := http.Post(uri.String(), "application/x-www-form-urlencoded", bytes.NewBufferString(params.Encode())) 71 | if err != nil { 72 | fmt.Println("Post error") 73 | return "", err 74 | } 75 | defer res.Body.Close() 76 | tr.log.Printf("Response code: %d", res.StatusCode) 77 | 78 | // Decode the JSON response 79 | var result interface{} 80 | if err := json.NewDecoder(res.Body).Decode(&result); err != nil { 81 | return "", err 82 | } 83 | 84 | m := result.(map[string]interface{}) 85 | if val, ok := m["translatedText"]; ok { 86 | return fmt.Sprintf("%v", val), nil 87 | } 88 | 89 | if val, ok := m["error"]; ok { 90 | return "", fmt.Errorf("%v", val) 91 | 92 | } 93 | 94 | return "", errors.New("unknown answer") 95 | 96 | } 97 | 98 | // Detect the language of the text 99 | // Return: confidence, language, error 100 | func (tr *Translation) Detect(text string) (float32, string, error) { 101 | params := url.Values{} 102 | params.Set("q", text) 103 | if len(tr.Key) > 0 { 104 | tr.log.Println("add api key to param") 105 | params.Add("api_key", tr.Key) 106 | } 107 | uri, err := url.Parse(tr.Url) 108 | if err != nil { 109 | tr.log.Println("Error parse url") 110 | return -1, "", err 111 | } 112 | 113 | uri.Path = path.Join(uri.Path, "/detect") 114 | tr.log.Println(uri.String()) 115 | 116 | res, err := http.Post(uri.String(), "application/x-www-form-urlencoded", bytes.NewBufferString(params.Encode())) 117 | if err != nil { 118 | return -1, "", err 119 | } 120 | defer res.Body.Close() 121 | tr.log.Printf("Response code: %d", res.StatusCode) 122 | 123 | switch res.StatusCode { 124 | case http.StatusOK: //200 125 | // Decode the JSON response 126 | type Detection struct { 127 | Confidence float32 128 | Language string 129 | } 130 | 131 | var result []Detection 132 | if err := json.NewDecoder(res.Body).Decode(&result); err == nil { 133 | if len(result) == 1 { 134 | return result[0].Confidence, result[0].Language, nil 135 | } else { 136 | return -1, "", fmt.Errorf("unknown number of responses ") 137 | } 138 | } 139 | //case StatusBadRequest: //400 Invalid request 140 | //case StatusTooManyRequests: //429 Slow down 141 | //case StatusInternalServerError: //500 Detection error 142 | default: 143 | var result2 interface{} 144 | if err := json.NewDecoder(res.Body).Decode(&result2); err != nil { 145 | return -1, "", err 146 | } 147 | m := result2.(map[string]interface{}) 148 | if val, ok := m["error"]; ok { 149 | return -1, "", fmt.Errorf("%v", val) 150 | } 151 | } 152 | 153 | return -1, "", errors.New("unknown answer") 154 | } 155 | --------------------------------------------------------------------------------