diff --git a/libretranslate.go b/libretranslate.go new file mode 100644 index 0000000..4d26fce --- /dev/null +++ b/libretranslate.go @@ -0,0 +1,169 @@ +package simplytranslate_engines + +import ( + "bytes" + "encoding/json" + "fmt" + "net/http" +) + +// LibreTranslateEngine is an engine that interfaces with any +// [LibreTranslate](https://github.com/LibreTranslate/LibreTranslate) instance. +type LibreTranslateEngine struct { + // InstanceURL is the URL to a LibreTranslate instance, for instance + // "https://libretranslate.com". + InstanceURL string + // APIKey is the API key for the given instance. If empty, then no API + // key will be sent along with requests to the instance. + // + // Some instances issue API keys to users so that they can have a + // higher rate limit. See + // https://github.com/LibreTranslate/LibreTranslate#manage-api-keys for + // more information. + APIKey string +} + +func (_ *LibreTranslateEngine) InternalName() string { return "libre" } + +func (_ *LibreTranslateEngine) DisplayName() string { return "LibreTranslate" } + +type libreLanguagesResponse []struct { + Name string `json:"name"` + Code string `json:"code"` +} + +func (e *LibreTranslateEngine) getLangs() ([]Language, error) { + response, err := http.Get(e.InstanceURL + "/languages") + + if err != nil { + return nil, err + } + + defer response.Body.Close() + + if response.StatusCode != 200 { + return nil, fmt.Errorf("got status code %d from LibreTranslate API", response.StatusCode) + } + + var langsResponse libreLanguagesResponse + + if err := json.NewDecoder(response.Body).Decode(&langsResponse); err != nil { + return nil, err + } + + langs := make([]Language, len(langsResponse)) + + for i, lang := range langsResponse { + langs[i] = Language{Name: lang.Name, Code: lang.Code} + } + + return langs, nil + +} + +func (e *LibreTranslateEngine) SourceLanguages() ([]Language, error) { return e.getLangs() } + +func (e *LibreTranslateEngine) TargetLanguages() ([]Language, error) { return e.getLangs() } + +func (_ *LibreTranslateEngine) SupportsAutodetect() bool { return true } + +type libreDetectResponse []struct { + Confidence float64 `json:"confidence"` + LanguageCode string `json:"language"` +} + +func (e *LibreTranslateEngine) DetectLanguage(text string) (Language, error) { + formData := map[string]string{"q": text} + + if e.APIKey != "" { + formData["api_key"] = e.APIKey + } + + formDataJSON, err := json.Marshal(formData) + + if err != nil { + return Language{}, err + } + + response, err := http.Post(e.InstanceURL+"/detect", "application/json", bytes.NewBuffer(formDataJSON)) + + if err != nil { + return Language{}, err + } + + defer response.Body.Close() + + if response.StatusCode != 200 { + return Language{}, fmt.Errorf("got status code %d from LibreTranslate API", response.StatusCode) + } + + var langs libreDetectResponse + + if err := json.NewDecoder(response.Body).Decode(&langs); err != nil { + return Language{}, err + } + + maxConfidenceLang := langs[0] + + for _, lang := range langs[1:] { + if lang.Confidence > maxConfidenceLang.Confidence { + maxConfidenceLang = lang + } + } + + engineLangs, err := e.getLangs() + + if err != nil { + return Language{}, err + } + + for _, lang := range engineLangs { + if lang.Code == maxConfidenceLang.LanguageCode { + return lang, nil + } + } + + return Language{}, fmt.Errorf("language code \"%s\" is not in the instance's language list", maxConfidenceLang.LanguageCode) +} + +type libreTranslateResponse struct { + TranslatedText string `json:"translatedText"` +} + +func (e *LibreTranslateEngine) Translate(text string, from Language, to Language) (TranslationResult, error) { + formData := map[string]string{ + "q": text, + "source": from.Code, + "target": to.Code, + } + + if e.APIKey != "" { + formData["api_key"] = e.APIKey + } + + formDataJSON, err := json.Marshal(formData) + + if err != nil { + return TranslationResult{}, err + } + + response, err := http.Post(e.InstanceURL+"/translate", "application/json", bytes.NewBuffer(formDataJSON)) + + if err != nil { + return TranslationResult{}, err + } + + defer response.Body.Close() + + if response.StatusCode != 200 { + return TranslationResult{}, fmt.Errorf("got status code %d from LibreTranslate API", response.StatusCode) + } + + var responseJSON libreTranslateResponse + + if err := json.NewDecoder(response.Body).Decode(&responseJSON); err != nil { + return TranslationResult{}, err + } + + return TranslationResult{SourceLanguage: from, TranslatedText: responseJSON.TranslatedText}, nil +}