transifexdl.go 3.6 KB

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