// Copyright (C) 2014 The Syncthing Authors. // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this file, // You can obtain one at https://mozilla.org/MPL/2.0/. //go:build ignore // +build ignore package main import ( "encoding/json" "fmt" "io" "log" "net/http" "os" "regexp" "sort" "strings" ) type stat struct { Translated int `json:"translated_entities"` Untranslated int `json:"untranslated_entities"` } type translation struct { Content string } func main() { log.SetFlags(log.Lshortfile) if u, p := userPass(); u == "" || p == "" { log.Fatal("Need environment variables TRANSIFEX_USER and TRANSIFEX_PASS") } curValidLangs := map[string]bool{} for _, lang := range loadValidLangs() { curValidLangs[lang] = true } log.Println(curValidLangs) resp := req("https://www.transifex.com/api/2/project/syncthing/resource/gui/stats") var stats map[string]stat err := json.NewDecoder(resp.Body).Decode(&stats) if err != nil { log.Fatal(err) } resp.Body.Close() names := make(map[string]string) var langs []string for code, stat := range stats { code = strings.Replace(code, "_", "-", 1) pct := 100 * stat.Translated / (stat.Translated + stat.Untranslated) if pct < 75 || !curValidLangs[code] && pct < 95 { log.Printf("Skipping language %q (too low completion ratio %d%%)", code, pct) os.Remove("lang-" + code + ".json") continue } langs = append(langs, code) names[code] = languageName(code) if code == "en" { continue } log.Printf("Updating language %q", code) resp := req("https://www.transifex.com/api/2/project/syncthing/resource/gui/translation/" + code) var t translation err := json.NewDecoder(resp.Body).Decode(&t) if err != nil { log.Fatal(err) } resp.Body.Close() fd, err := os.Create("lang-" + code + ".json") if err != nil { log.Fatal(err) } fd.WriteString(t.Content) fd.Close() } saveValidLangs(langs) saveLanguageNames(names) } func saveValidLangs(langs []string) { sort.Strings(langs) fd, err := os.Create("valid-langs.js") if err != nil { log.Fatal(err) } fmt.Fprint(fd, "var validLangs = ") json.NewEncoder(fd).Encode(langs) fd.Close() } func saveLanguageNames(names map[string]string) { fd, err := os.Create("prettyprint.js") if err != nil { log.Fatal(err) } fmt.Fprint(fd, "var langPrettyprint = ") json.NewEncoder(fd).Encode(names) fd.Close() } func userPass() (string, string) { user := os.Getenv("TRANSIFEX_USER") pass := os.Getenv("TRANSIFEX_PASS") return user, pass } func req(url string) *http.Response { user, pass := userPass() req, err := http.NewRequest("GET", url, nil) if err != nil { log.Fatal(err) } req.SetBasicAuth(user, pass) resp, err := http.DefaultClient.Do(req) if err != nil { log.Fatal(err) } return resp } func loadValidLangs() []string { fd, err := os.Open("valid-langs.js") if err != nil { log.Fatal(err) } defer fd.Close() bs, err := io.ReadAll(fd) if err != nil { log.Fatal(err) } var langs []string exp := regexp.MustCompile(`\[([a-zA-Z@",-]+)\]`) if matches := exp.FindSubmatch(bs); len(matches) == 2 { langs = strings.Split(string(matches[1]), ",") for i := range langs { // Remove quotes langs[i] = langs[i][1 : len(langs[i])-1] } } return langs } type languageResponse struct { Code string Name string } func languageName(code string) string { var lang languageResponse resp := req("https://www.transifex.com/api/2/language/" + code) defer resp.Body.Close() json.NewDecoder(resp.Body).Decode(&lang) if lang.Name == "" { return code } return lang.Name }