transifexdl.go 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. // Copyright (C) 2014 The Syncthing Authors.
  2. //
  3. // This Source Code Form is subject to the terms of the Mozilla Public
  4. // License, v. 2.0. If a copy of the MPL was not distributed with this file,
  5. // You can obtain one at https://mozilla.org/MPL/2.0/.
  6. //go:build ignore
  7. // +build ignore
  8. package main
  9. import (
  10. "encoding/json"
  11. "fmt"
  12. "io"
  13. "log"
  14. "net/http"
  15. "os"
  16. "regexp"
  17. "sort"
  18. "strings"
  19. )
  20. type stat struct {
  21. Translated int `json:"translated_entities"`
  22. Untranslated int `json:"untranslated_entities"`
  23. }
  24. type translation struct {
  25. Content string
  26. }
  27. func main() {
  28. log.SetFlags(log.Lshortfile)
  29. if u, p := userPass(); u == "" || p == "" {
  30. log.Fatal("Need environment variables TRANSIFEX_USER and TRANSIFEX_PASS")
  31. }
  32. curValidLangs := map[string]bool{}
  33. for _, lang := range loadValidLangs() {
  34. curValidLangs[lang] = true
  35. }
  36. log.Println(curValidLangs)
  37. resp := req("https://www.transifex.com/api/2/project/syncthing/resource/gui/stats")
  38. var stats map[string]stat
  39. err := json.NewDecoder(resp.Body).Decode(&stats)
  40. if err != nil {
  41. log.Fatal(err)
  42. }
  43. resp.Body.Close()
  44. names := make(map[string]string)
  45. var langs []string
  46. for code, stat := range stats {
  47. code = strings.Replace(code, "_", "-", 1)
  48. pct := 100 * stat.Translated / (stat.Translated + stat.Untranslated)
  49. if pct < 75 || !curValidLangs[code] && pct < 95 {
  50. log.Printf("Skipping language %q (too low completion ratio %d%%)", code, pct)
  51. os.Remove("lang-" + code + ".json")
  52. continue
  53. }
  54. langs = append(langs, code)
  55. names[code] = languageName(code)
  56. if code == "en" {
  57. continue
  58. }
  59. log.Printf("Updating language %q", code)
  60. resp := req("https://www.transifex.com/api/2/project/syncthing/resource/gui/translation/" + code)
  61. var t translation
  62. err := json.NewDecoder(resp.Body).Decode(&t)
  63. if err != nil {
  64. log.Fatal(err)
  65. }
  66. resp.Body.Close()
  67. fd, err := os.Create("lang-" + code + ".json")
  68. if err != nil {
  69. log.Fatal(err)
  70. }
  71. fd.WriteString(t.Content)
  72. fd.Close()
  73. }
  74. saveValidLangs(langs)
  75. saveLanguageNames(names)
  76. }
  77. func saveValidLangs(langs []string) {
  78. sort.Strings(langs)
  79. fd, err := os.Create("valid-langs.js")
  80. if err != nil {
  81. log.Fatal(err)
  82. }
  83. fmt.Fprint(fd, "var validLangs = ")
  84. json.NewEncoder(fd).Encode(langs)
  85. fd.Close()
  86. }
  87. func saveLanguageNames(names map[string]string) {
  88. fd, err := os.Create("prettyprint.js")
  89. if err != nil {
  90. log.Fatal(err)
  91. }
  92. fmt.Fprint(fd, "var langPrettyprint = ")
  93. json.NewEncoder(fd).Encode(names)
  94. fd.Close()
  95. }
  96. func userPass() (string, string) {
  97. user := os.Getenv("TRANSIFEX_USER")
  98. pass := os.Getenv("TRANSIFEX_PASS")
  99. return user, pass
  100. }
  101. func req(url string) *http.Response {
  102. user, pass := userPass()
  103. req, err := http.NewRequest("GET", url, nil)
  104. if err != nil {
  105. log.Fatal(err)
  106. }
  107. req.SetBasicAuth(user, pass)
  108. resp, err := http.DefaultClient.Do(req)
  109. if err != nil {
  110. log.Fatal(err)
  111. }
  112. return resp
  113. }
  114. func loadValidLangs() []string {
  115. fd, err := os.Open("valid-langs.js")
  116. if err != nil {
  117. log.Fatal(err)
  118. }
  119. defer fd.Close()
  120. bs, err := io.ReadAll(fd)
  121. if err != nil {
  122. log.Fatal(err)
  123. }
  124. var langs []string
  125. exp := regexp.MustCompile(`\[([a-zA-Z@",-]+)\]`)
  126. if matches := exp.FindSubmatch(bs); len(matches) == 2 {
  127. langs = strings.Split(string(matches[1]), ",")
  128. for i := range langs {
  129. // Remove quotes
  130. langs[i] = langs[i][1 : len(langs[i])-1]
  131. }
  132. }
  133. return langs
  134. }
  135. type languageResponse struct {
  136. Code string
  137. Name string
  138. }
  139. func languageName(code string) string {
  140. var lang languageResponse
  141. resp := req("https://www.transifex.com/api/2/language/" + code)
  142. defer resp.Body.Close()
  143. json.NewDecoder(resp.Body).Decode(&lang)
  144. if lang.Name == "" {
  145. return code
  146. }
  147. return lang.Name
  148. }