package utils import ( "errors" "io" "net/http" "os" "regexp" "strings" "github.com/OwO-Network/gdeeplx" "github.com/gocolly/colly" "github.com/google/go-querystring/query" "github.com/google/uuid" ) var ddgVqd string func TranslateGoogle(to string, from string, text string) (LangOut, error) { ToOrig := to FromOrig := from // For some reason google uses no for norwegian instead of nb like the rest of the translators. This is for the All function primarily if to == "nb" { to = "no" } else if from == "nb" { to = "no" } var ToValid bool var FromValid bool for _, v := range LangListGoogle("sl") { if v.Id == to { ToValid = true } if v.Id == from { FromValid = true } if FromValid == true && ToValid == true { break } } if ToValid != true { return LangOut{}, errors.New("Target Language Code invalid") } if FromValid != true { return LangOut{}, errors.New("Source language code invalid") } UserAgent, ok := os.LookupEnv("MOZHI_USER_AGENT") if !ok { UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36" } sc := colly.NewCollector(colly.AllowedDomains("translate.google.com"), colly.UserAgent(UserAgent)) var answer string sc.OnHTML("div.result-container", func(e *colly.HTMLElement) { answer = e.Text }) type Options struct { To string `url:"tl"` UI string `url:"hl"` From string `url:"sl"` Text string `url:"q"` } opt := Options{to, to, from, text} v, _ := query.Values(opt) url := "https://translate.google.com/m?" + v.Encode() sc.Visit(url) var langout LangOut langout.OutputText = answer langout.Engine = "google" langout.SourceLang = FromOrig langout.TargetLang = ToOrig return langout, nil } func TranslateReverso(to string, from string, query string) (LangOut, error) { ToOrig := to FromOrig := from var ToValid bool var FromValid bool for _, v := range LangListReverso("sl") { if v.Id == to { ToValid = true } if v.Id == from { FromValid = true } if FromValid == true && ToValid == true { break } } if ToValid != true { return LangOut{}, errors.New("Target language code invalid") } if FromValid != true { return LangOut{}, errors.New("Source language code invalid") } json := []byte(`{ "format": "text", "from": "` + from + `", "to": "` + to + `", "input":"` + query + `", "options": {"sentenceSplitter": false, "origin":"translation.web", contextResults: false, languageDetection: true} }`) reversoOut := PostRequest("https://api.reverso.net/translate/v1/translation", json) gjsonArr := reversoOut.Get("translation").Array() var langout LangOut langout.OutputText = gjsonArr[0].String() langout.Engine = "reverso" langout.SourceLang = FromOrig langout.TargetLang = ToOrig return langout, nil } func TranslateLibreTranslate(to string, from string, query string) (LangOut, error) { ToOrig := to FromOrig := from var ToValid bool var FromValid bool for _, v := range LangListLibreTranslate("sl") { if v.Id == to { ToValid = true } if v.Id == from { FromValid = true } if FromValid == true && ToValid == true { break } } if ToValid != true { return LangOut{}, errors.New("Target language code invalid") } if FromValid != true { return LangOut{}, errors.New("Source language code invalid") } json := []byte(`{"q":"` + query + `","source":"` + from + `","target":"` + to + `"}`) // TODO: Make it configurable libreTranslateOut := PostRequest(os.Getenv("MOZHI_LIBRETRANSLATE_URL")+"/translate", json) gjsonArr := libreTranslateOut.Get("translatedText").Array() var langout LangOut langout.OutputText = gjsonArr[0].String() langout.Engine = "libretranslate" langout.SourceLang = FromOrig langout.TargetLang = ToOrig if from == "auto" { langout.AutoDetect, _ = AutoDetectLibreTranslate(query) } return langout, nil } func TranslateWatson(to string, from string, query string) (LangOut, error) { FromOrig := from ToOrig := to var ToValid bool var FromValid bool for _, v := range LangListWatson("sl") { if v.Id == to { ToValid = true } if v.Id == from { FromValid = true } if FromValid == true && ToValid == true { break } } if ToValid != true { return LangOut{}, errors.New("Target language code invalid") } if FromValid != true { return LangOut{}, errors.New("Source language code invalid") } var langout LangOut if from == "auto" { langout.AutoDetect, _ = AutoDetectWatson(query) from = langout.AutoDetect } json := []byte(`{"text":"` + query + `","source":"` + from + `","target":"` + to + `"}`) watsonOut := PostRequest("https://www.ibm.com/demos/live/watson-language-translator/api/translate/text", json) gjsonArr := watsonOut.Get("payload.translations.0.translation").Array() langout.OutputText = gjsonArr[0].String() langout.Engine = "watson" langout.SourceLang = FromOrig langout.TargetLang = ToOrig return langout, nil } func TranslateMyMemory(to string, from string, text string) (LangOut, error) { FromOrig := from ToOrig := to var ToValid bool var FromValid bool for _, v := range LangListMyMemory("sl") { if v.Id == to { ToValid = true } if v.Id == from { FromValid = true } if FromValid == true && ToValid == true { break } } if ToValid != true { return LangOut{}, errors.New("Target language code invalid") } if FromValid != true { return LangOut{}, errors.New("Source language code invalid") } type Options struct { Translate string `url:"langpair"` Text string `url:"q"` } opt := Options{from + "|" + to, text} v, _ := query.Values(opt) myMemoryOut := GetRequest("https://api.mymemory.translated.net/get?" + v.Encode()) gjsonArr := myMemoryOut.Get("responseData.translatedText").Array() var langout LangOut langout.OutputText = gjsonArr[0].String() langout.Engine = "mymemory" langout.SourceLang = FromOrig langout.TargetLang = ToOrig return langout, nil } func TranslateYandex(to string, from string, text string) (LangOut, error) { FromOrig := from ToOrig := to var ToValid bool var FromValid bool for _, v := range LangListYandex("sl") { if v.Id == to { ToValid = true } if v.Id == from { FromValid = true } if FromValid == true && ToValid == true { break } } if ToValid != true { return LangOut{}, errors.New("Target language code invalid") } if FromValid != true { return LangOut{}, errors.New("Source language code invalid") } type Options struct { Translate string `url:"lang"` Text string `url:"text"` Srv string `url:"srv"` Id string `url:"sid"` } uuidWithHyphen := uuid.New() uuid := strings.Replace(uuidWithHyphen.String(), "-", "", -1) opt := Options{from + "-" + to, text, "android", uuid + "-0-0"} v, _ := query.Values(opt) yandexOut := PostRequest("https://translate.yandex.net/api/v1/tr.json/translate?"+v.Encode(), []byte("")) gjsonArr := yandexOut.Get("text.0").Array() var langout LangOut langout.OutputText = gjsonArr[0].String() langout.Engine = "yandex" langout.SourceLang = FromOrig langout.TargetLang = ToOrig return langout, nil } func TranslateDeepl(to string, from string, text string) (LangOut, error) { FromOrig := from ToOrig := to var ToValid bool var FromValid bool for _, v := range LangListDeepl("sl") { if v.Id == to { ToValid = true } if v.Id == from { FromValid = true } if FromValid == true && ToValid == true { break } } if ToValid != true { return LangOut{}, errors.New("Target language code invalid") } if FromValid != true { return LangOut{}, errors.New("Source language code invalid") } answer, err := gdeeplx.Translate(text, from, to, 0) if err != nil { return LangOut{}, errors.New("failed") } answer1 := answer.(map[string]interface{}) ans := answer1["data"].(string) var langout LangOut langout.OutputText = ans langout.Engine = "deepl" if from == "auto" { langout.AutoDetect = strings.ToLower(answer1["detected_lang"].(string)) } langout.SourceLang = FromOrig langout.TargetLang = ToOrig return langout, nil } func ddgVqdUpdate() { r, err := http.NewRequest("GET", "https://duckduckgo.com/?q=translate", nil) if err != nil { panic(err) } UserAgent, ok := os.LookupEnv("MOZHI_USER_AGENT") if !ok { UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36" } r.Header.Set("User-Agent", UserAgent) client := &http.Client{} res, err := client.Do(r) if err != nil { panic(err) } defer res.Body.Close() body, err := io.ReadAll(res.Body) if err != nil { panic(err) } re := regexp.MustCompile(`vqd="([^"]*)"`) match := re.FindStringSubmatch(string(body)) ddgVqd = match[1] } func TranslateDuckDuckGo(to string, from string, query string) (LangOut, error) { FromOrig := from ToOrig := to var ToValid bool var FromValid bool for _, v := range LangListDuckDuckGo("sl") { if v.Id == to { ToValid = true } if v.Id == from { FromValid = true } if FromValid == true && ToValid == true { break } } if ToValid != true { return LangOut{}, errors.New("Target language code invalid") } if FromValid != true { return LangOut{}, errors.New("Source language code invalid") } var url string var langout LangOut ddgVqdUpdate() if from == "auto" { url = "https://duckduckgo.com/translation.js?vqd=" + ddgVqd + "&query=translate&to=" + to } else { url = "https://duckduckgo.com/translation.js?vqd=" + ddgVqd + "&query=translate&to=" + to + "&from=" + from } duckDuckGoOut := PostRequest(url, []byte(query)) gjsonArr := duckDuckGoOut.Get("translated").Array() langout.OutputText = gjsonArr[0].String() langout.Engine = "duckduckgo" langout.SourceLang = FromOrig langout.TargetLang = ToOrig if from == "auto" { langout.AutoDetect = duckDuckGoOut.Get("detected_language").String() } return langout, nil } func TranslateAll(to string, from string, query string) []LangOut { engines := []string{"reverso", "google", "libre", "watson", "mymemory", "yandex", "deepl", "duckduckgo"} langout := []LangOut{} for i := 0; i < len(engines); i++ { data, err := Translate(engines[i], to, from, query) if err == nil { langout = append(langout, data) } } return langout }