import_export.go 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274
  1. package ut
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "io/ioutil"
  6. "os"
  7. "path/filepath"
  8. "io"
  9. "github.com/go-playground/locales"
  10. )
  11. type translation struct {
  12. Locale string `json:"locale"`
  13. Key interface{} `json:"key"` // either string or integer
  14. Translation string `json:"trans"`
  15. PluralType string `json:"type,omitempty"`
  16. PluralRule string `json:"rule,omitempty"`
  17. OverrideExisting bool `json:"override,omitempty"`
  18. }
  19. const (
  20. cardinalType = "Cardinal"
  21. ordinalType = "Ordinal"
  22. rangeType = "Range"
  23. )
  24. // ImportExportFormat is the format of the file import or export
  25. type ImportExportFormat uint8
  26. // supported Export Formats
  27. const (
  28. FormatJSON ImportExportFormat = iota
  29. )
  30. // Export writes the translations out to a file on disk.
  31. //
  32. // NOTE: this currently only works with string or int translations keys.
  33. func (t *UniversalTranslator) Export(format ImportExportFormat, dirname string) error {
  34. _, err := os.Stat(dirname)
  35. fmt.Println(dirname, err, os.IsNotExist(err))
  36. if err != nil {
  37. if !os.IsNotExist(err) {
  38. return err
  39. }
  40. if err = os.MkdirAll(dirname, 0744); err != nil {
  41. return err
  42. }
  43. }
  44. // build up translations
  45. var trans []translation
  46. var b []byte
  47. var ext string
  48. for _, locale := range t.translators {
  49. for k, v := range locale.(*translator).translations {
  50. trans = append(trans, translation{
  51. Locale: locale.Locale(),
  52. Key: k,
  53. Translation: v.text,
  54. })
  55. }
  56. for k, pluralTrans := range locale.(*translator).cardinalTanslations {
  57. for i, plural := range pluralTrans {
  58. // leave enough for all plural rules
  59. // but not all are set for all languages.
  60. if plural == nil {
  61. continue
  62. }
  63. trans = append(trans, translation{
  64. Locale: locale.Locale(),
  65. Key: k.(string),
  66. Translation: plural.text,
  67. PluralType: cardinalType,
  68. PluralRule: locales.PluralRule(i).String(),
  69. })
  70. }
  71. }
  72. for k, pluralTrans := range locale.(*translator).ordinalTanslations {
  73. for i, plural := range pluralTrans {
  74. // leave enough for all plural rules
  75. // but not all are set for all languages.
  76. if plural == nil {
  77. continue
  78. }
  79. trans = append(trans, translation{
  80. Locale: locale.Locale(),
  81. Key: k.(string),
  82. Translation: plural.text,
  83. PluralType: ordinalType,
  84. PluralRule: locales.PluralRule(i).String(),
  85. })
  86. }
  87. }
  88. for k, pluralTrans := range locale.(*translator).rangeTanslations {
  89. for i, plural := range pluralTrans {
  90. // leave enough for all plural rules
  91. // but not all are set for all languages.
  92. if plural == nil {
  93. continue
  94. }
  95. trans = append(trans, translation{
  96. Locale: locale.Locale(),
  97. Key: k.(string),
  98. Translation: plural.text,
  99. PluralType: rangeType,
  100. PluralRule: locales.PluralRule(i).String(),
  101. })
  102. }
  103. }
  104. switch format {
  105. case FormatJSON:
  106. b, err = json.MarshalIndent(trans, "", " ")
  107. ext = ".json"
  108. }
  109. if err != nil {
  110. return err
  111. }
  112. err = ioutil.WriteFile(filepath.Join(dirname, fmt.Sprintf("%s%s", locale.Locale(), ext)), b, 0644)
  113. if err != nil {
  114. return err
  115. }
  116. trans = trans[0:0]
  117. }
  118. return nil
  119. }
  120. // Import reads the translations out of a file or directory on disk.
  121. //
  122. // NOTE: this currently only works with string or int translations keys.
  123. func (t *UniversalTranslator) Import(format ImportExportFormat, dirnameOrFilename string) error {
  124. fi, err := os.Stat(dirnameOrFilename)
  125. if err != nil {
  126. return err
  127. }
  128. processFn := func(filename string) error {
  129. f, err := os.Open(filename)
  130. if err != nil {
  131. return err
  132. }
  133. defer f.Close()
  134. return t.ImportByReader(format, f)
  135. }
  136. if !fi.IsDir() {
  137. return processFn(dirnameOrFilename)
  138. }
  139. // recursively go through directory
  140. walker := func(path string, info os.FileInfo, err error) error {
  141. if info.IsDir() {
  142. return nil
  143. }
  144. switch format {
  145. case FormatJSON:
  146. // skip non JSON files
  147. if filepath.Ext(info.Name()) != ".json" {
  148. return nil
  149. }
  150. }
  151. return processFn(path)
  152. }
  153. return filepath.Walk(dirnameOrFilename, walker)
  154. }
  155. // ImportByReader imports the the translations found within the contents read from the supplied reader.
  156. //
  157. // NOTE: generally used when assets have been embedded into the binary and are already in memory.
  158. func (t *UniversalTranslator) ImportByReader(format ImportExportFormat, reader io.Reader) error {
  159. b, err := ioutil.ReadAll(reader)
  160. if err != nil {
  161. return err
  162. }
  163. var trans []translation
  164. switch format {
  165. case FormatJSON:
  166. err = json.Unmarshal(b, &trans)
  167. }
  168. if err != nil {
  169. return err
  170. }
  171. for _, tl := range trans {
  172. locale, found := t.FindTranslator(tl.Locale)
  173. if !found {
  174. return &ErrMissingLocale{locale: tl.Locale}
  175. }
  176. pr := stringToPR(tl.PluralRule)
  177. if pr == locales.PluralRuleUnknown {
  178. err = locale.Add(tl.Key, tl.Translation, tl.OverrideExisting)
  179. if err != nil {
  180. return err
  181. }
  182. continue
  183. }
  184. switch tl.PluralType {
  185. case cardinalType:
  186. err = locale.AddCardinal(tl.Key, tl.Translation, pr, tl.OverrideExisting)
  187. case ordinalType:
  188. err = locale.AddOrdinal(tl.Key, tl.Translation, pr, tl.OverrideExisting)
  189. case rangeType:
  190. err = locale.AddRange(tl.Key, tl.Translation, pr, tl.OverrideExisting)
  191. default:
  192. return &ErrBadPluralDefinition{tl: tl}
  193. }
  194. if err != nil {
  195. return err
  196. }
  197. }
  198. return nil
  199. }
  200. func stringToPR(s string) locales.PluralRule {
  201. switch s {
  202. case "One":
  203. return locales.PluralRuleOne
  204. case "Two":
  205. return locales.PluralRuleTwo
  206. case "Few":
  207. return locales.PluralRuleFew
  208. case "Many":
  209. return locales.PluralRuleMany
  210. case "Other":
  211. return locales.PluralRuleOther
  212. default:
  213. return locales.PluralRuleUnknown
  214. }
  215. }