1 package ut
2
3 import (
4 "encoding/json"
5 "fmt"
6 "os"
7 "path/filepath"
8
9 "io"
10
11 "github.com/go-playground/locales"
12 )
13
14 type translation struct {
15 Locale string `json:"locale"`
16 Key interface{} `json:"key"`
17 Translation string `json:"trans"`
18 PluralType string `json:"type,omitempty"`
19 PluralRule string `json:"rule,omitempty"`
20 OverrideExisting bool `json:"override,omitempty"`
21 }
22
23 const (
24 cardinalType = "Cardinal"
25 ordinalType = "Ordinal"
26 rangeType = "Range"
27 )
28
29
30 type ImportExportFormat uint8
31
32
33 const (
34 FormatJSON ImportExportFormat = iota
35 )
36
37
38
39
40 func (t *UniversalTranslator) Export(format ImportExportFormat, dirname string) error {
41
42 _, err := os.Stat(dirname)
43 if err != nil {
44
45 if !os.IsNotExist(err) {
46 return err
47 }
48
49 if err = os.MkdirAll(dirname, 0744); err != nil {
50 return err
51 }
52 }
53
54
55 var trans []translation
56 var b []byte
57 var ext string
58
59 for _, locale := range t.translators {
60
61 for k, v := range locale.(*translator).translations {
62 trans = append(trans, translation{
63 Locale: locale.Locale(),
64 Key: k,
65 Translation: v.text,
66 })
67 }
68
69 for k, pluralTrans := range locale.(*translator).cardinalTanslations {
70
71 for i, plural := range pluralTrans {
72
73
74
75 if plural == nil {
76 continue
77 }
78
79 trans = append(trans, translation{
80 Locale: locale.Locale(),
81 Key: k.(string),
82 Translation: plural.text,
83 PluralType: cardinalType,
84 PluralRule: locales.PluralRule(i).String(),
85 })
86 }
87 }
88
89 for k, pluralTrans := range locale.(*translator).ordinalTanslations {
90
91 for i, plural := range pluralTrans {
92
93
94
95 if plural == nil {
96 continue
97 }
98
99 trans = append(trans, translation{
100 Locale: locale.Locale(),
101 Key: k.(string),
102 Translation: plural.text,
103 PluralType: ordinalType,
104 PluralRule: locales.PluralRule(i).String(),
105 })
106 }
107 }
108
109 for k, pluralTrans := range locale.(*translator).rangeTanslations {
110
111 for i, plural := range pluralTrans {
112
113
114
115 if plural == nil {
116 continue
117 }
118
119 trans = append(trans, translation{
120 Locale: locale.Locale(),
121 Key: k.(string),
122 Translation: plural.text,
123 PluralType: rangeType,
124 PluralRule: locales.PluralRule(i).String(),
125 })
126 }
127 }
128
129 switch format {
130 case FormatJSON:
131 b, err = json.MarshalIndent(trans, "", " ")
132 ext = ".json"
133 }
134
135 if err != nil {
136 return err
137 }
138
139 err = os.WriteFile(filepath.Join(dirname, fmt.Sprintf("%s%s", locale.Locale(), ext)), b, 0644)
140 if err != nil {
141 return err
142 }
143
144 trans = trans[0:0]
145 }
146
147 return nil
148 }
149
150
151
152
153 func (t *UniversalTranslator) Import(format ImportExportFormat, dirnameOrFilename string) error {
154
155 fi, err := os.Stat(dirnameOrFilename)
156 if err != nil {
157 return err
158 }
159
160 processFn := func(filename string) error {
161
162 f, err := os.Open(filename)
163 if err != nil {
164 return err
165 }
166 defer f.Close()
167
168 return t.ImportByReader(format, f)
169 }
170
171 if !fi.IsDir() {
172 return processFn(dirnameOrFilename)
173 }
174
175
176 walker := func(path string, info os.FileInfo, err error) error {
177
178 if info.IsDir() {
179 return nil
180 }
181
182 switch format {
183 case FormatJSON:
184
185 if filepath.Ext(info.Name()) != ".json" {
186 return nil
187 }
188 }
189
190 return processFn(path)
191 }
192
193 return filepath.Walk(dirnameOrFilename, walker)
194 }
195
196
197
198
199 func (t *UniversalTranslator) ImportByReader(format ImportExportFormat, reader io.Reader) error {
200
201 b, err := io.ReadAll(reader)
202 if err != nil {
203 return err
204 }
205
206 var trans []translation
207
208 switch format {
209 case FormatJSON:
210 err = json.Unmarshal(b, &trans)
211 }
212
213 if err != nil {
214 return err
215 }
216
217 for _, tl := range trans {
218
219 locale, found := t.FindTranslator(tl.Locale)
220 if !found {
221 return &ErrMissingLocale{locale: tl.Locale}
222 }
223
224 pr := stringToPR(tl.PluralRule)
225
226 if pr == locales.PluralRuleUnknown {
227
228 err = locale.Add(tl.Key, tl.Translation, tl.OverrideExisting)
229 if err != nil {
230 return err
231 }
232
233 continue
234 }
235
236 switch tl.PluralType {
237 case cardinalType:
238 err = locale.AddCardinal(tl.Key, tl.Translation, pr, tl.OverrideExisting)
239 case ordinalType:
240 err = locale.AddOrdinal(tl.Key, tl.Translation, pr, tl.OverrideExisting)
241 case rangeType:
242 err = locale.AddRange(tl.Key, tl.Translation, pr, tl.OverrideExisting)
243 default:
244 return &ErrBadPluralDefinition{tl: tl}
245 }
246
247 if err != nil {
248 return err
249 }
250 }
251
252 return nil
253 }
254
255 func stringToPR(s string) locales.PluralRule {
256
257 switch s {
258 case "Zero":
259 return locales.PluralRuleZero
260 case "One":
261 return locales.PluralRuleOne
262 case "Two":
263 return locales.PluralRuleTwo
264 case "Few":
265 return locales.PluralRuleFew
266 case "Many":
267 return locales.PluralRuleMany
268 case "Other":
269 return locales.PluralRuleOther
270 default:
271 return locales.PluralRuleUnknown
272 }
273
274 }
275
View as plain text