1 package ut
2
3 import (
4 "fmt"
5 "strconv"
6 "strings"
7
8 "github.com/go-playground/locales"
9 )
10
11 const (
12 paramZero = "{0}"
13 paramOne = "{1}"
14 unknownTranslation = ""
15 )
16
17
18
19
20
21 type Translator interface {
22 locales.Translator
23
24
25
26
27 Add(key interface{}, text string, override bool) error
28
29
30
31
32
33
34 AddCardinal(key interface{}, text string, rule locales.PluralRule, override bool) error
35
36
37
38
39
40
41
42 AddOrdinal(key interface{}, text string, rule locales.PluralRule, override bool) error
43
44
45
46
47 AddRange(key interface{}, text string, rule locales.PluralRule, override bool) error
48
49
50 T(key interface{}, params ...string) (string, error)
51
52
53
54 C(key interface{}, num float64, digits uint64, param string) (string, error)
55
56
57
58 O(key interface{}, num float64, digits uint64, param string) (string, error)
59
60
61
62 R(key interface{}, num1 float64, digits1 uint64, num2 float64, digits2 uint64, param1, param2 string) (string, error)
63
64
65
66 VerifyTranslations() error
67 }
68
69 var _ Translator = new(translator)
70 var _ locales.Translator = new(translator)
71
72 type translator struct {
73 locales.Translator
74 translations map[interface{}]*transText
75 cardinalTanslations map[interface{}][]*transText
76 ordinalTanslations map[interface{}][]*transText
77 rangeTanslations map[interface{}][]*transText
78 }
79
80 type transText struct {
81 text string
82 indexes []int
83 }
84
85 func newTranslator(trans locales.Translator) Translator {
86 return &translator{
87 Translator: trans,
88 translations: make(map[interface{}]*transText),
89 cardinalTanslations: make(map[interface{}][]*transText),
90 ordinalTanslations: make(map[interface{}][]*transText),
91 rangeTanslations: make(map[interface{}][]*transText),
92 }
93 }
94
95
96
97
98 func (t *translator) Add(key interface{}, text string, override bool) error {
99
100 if _, ok := t.translations[key]; ok && !override {
101 return &ErrConflictingTranslation{locale: t.Locale(), key: key, text: text}
102 }
103
104 lb := strings.Count(text, "{")
105 rb := strings.Count(text, "}")
106
107 if lb != rb {
108 return &ErrMissingBracket{locale: t.Locale(), key: key, text: text}
109 }
110
111 trans := &transText{
112 text: text,
113 }
114
115 var idx int
116
117 for i := 0; i < lb; i++ {
118 s := "{" + strconv.Itoa(i) + "}"
119 idx = strings.Index(text, s)
120 if idx == -1 {
121 return &ErrBadParamSyntax{locale: t.Locale(), param: s, key: key, text: text}
122 }
123
124 trans.indexes = append(trans.indexes, idx)
125 trans.indexes = append(trans.indexes, idx+len(s))
126 }
127
128 t.translations[key] = trans
129
130 return nil
131 }
132
133
134
135
136
137
138 func (t *translator) AddCardinal(key interface{}, text string, rule locales.PluralRule, override bool) error {
139
140 var verified bool
141
142
143 for _, pr := range t.PluralsCardinal() {
144 if pr == rule {
145 verified = true
146 break
147 }
148 }
149
150 if !verified {
151 return &ErrCardinalTranslation{text: fmt.Sprintf("error: cardinal plural rule '%s' does not exist for locale '%s' key: '%v' text: '%s'", rule, t.Locale(), key, text)}
152 }
153
154 tarr, ok := t.cardinalTanslations[key]
155 if ok {
156
157 if len(tarr) > 0 && tarr[rule] != nil && !override {
158 return &ErrConflictingTranslation{locale: t.Locale(), key: key, rule: rule, text: text}
159 }
160
161 } else {
162 tarr = make([]*transText, 7)
163 t.cardinalTanslations[key] = tarr
164 }
165
166 trans := &transText{
167 text: text,
168 indexes: make([]int, 2),
169 }
170
171 tarr[rule] = trans
172
173 idx := strings.Index(text, paramZero)
174 if idx == -1 {
175 tarr[rule] = nil
176 return &ErrCardinalTranslation{text: fmt.Sprintf("error: parameter '%s' not found, may want to use 'Add' instead of 'AddCardinal'. locale: '%s' key: '%v' text: '%s'", paramZero, t.Locale(), key, text)}
177 }
178
179 trans.indexes[0] = idx
180 trans.indexes[1] = idx + len(paramZero)
181
182 return nil
183 }
184
185
186
187
188
189
190 func (t *translator) AddOrdinal(key interface{}, text string, rule locales.PluralRule, override bool) error {
191
192 var verified bool
193
194
195 for _, pr := range t.PluralsOrdinal() {
196 if pr == rule {
197 verified = true
198 break
199 }
200 }
201
202 if !verified {
203 return &ErrOrdinalTranslation{text: fmt.Sprintf("error: ordinal plural rule '%s' does not exist for locale '%s' key: '%v' text: '%s'", rule, t.Locale(), key, text)}
204 }
205
206 tarr, ok := t.ordinalTanslations[key]
207 if ok {
208
209 if len(tarr) > 0 && tarr[rule] != nil && !override {
210 return &ErrConflictingTranslation{locale: t.Locale(), key: key, rule: rule, text: text}
211 }
212
213 } else {
214 tarr = make([]*transText, 7)
215 t.ordinalTanslations[key] = tarr
216 }
217
218 trans := &transText{
219 text: text,
220 indexes: make([]int, 2),
221 }
222
223 tarr[rule] = trans
224
225 idx := strings.Index(text, paramZero)
226 if idx == -1 {
227 tarr[rule] = nil
228 return &ErrOrdinalTranslation{text: fmt.Sprintf("error: parameter '%s' not found, may want to use 'Add' instead of 'AddOrdinal'. locale: '%s' key: '%v' text: '%s'", paramZero, t.Locale(), key, text)}
229 }
230
231 trans.indexes[0] = idx
232 trans.indexes[1] = idx + len(paramZero)
233
234 return nil
235 }
236
237
238
239
240 func (t *translator) AddRange(key interface{}, text string, rule locales.PluralRule, override bool) error {
241
242 var verified bool
243
244
245 for _, pr := range t.PluralsRange() {
246 if pr == rule {
247 verified = true
248 break
249 }
250 }
251
252 if !verified {
253 return &ErrRangeTranslation{text: fmt.Sprintf("error: range plural rule '%s' does not exist for locale '%s' key: '%v' text: '%s'", rule, t.Locale(), key, text)}
254 }
255
256 tarr, ok := t.rangeTanslations[key]
257 if ok {
258
259 if len(tarr) > 0 && tarr[rule] != nil && !override {
260 return &ErrConflictingTranslation{locale: t.Locale(), key: key, rule: rule, text: text}
261 }
262
263 } else {
264 tarr = make([]*transText, 7)
265 t.rangeTanslations[key] = tarr
266 }
267
268 trans := &transText{
269 text: text,
270 indexes: make([]int, 4),
271 }
272
273 tarr[rule] = trans
274
275 idx := strings.Index(text, paramZero)
276 if idx == -1 {
277 tarr[rule] = nil
278 return &ErrRangeTranslation{text: fmt.Sprintf("error: parameter '%s' not found, are you sure you're adding a Range Translation? locale: '%s' key: '%v' text: '%s'", paramZero, t.Locale(), key, text)}
279 }
280
281 trans.indexes[0] = idx
282 trans.indexes[1] = idx + len(paramZero)
283
284 idx = strings.Index(text, paramOne)
285 if idx == -1 {
286 tarr[rule] = nil
287 return &ErrRangeTranslation{text: fmt.Sprintf("error: parameter '%s' not found, a Range Translation requires two parameters. locale: '%s' key: '%v' text: '%s'", paramOne, t.Locale(), key, text)}
288 }
289
290 trans.indexes[2] = idx
291 trans.indexes[3] = idx + len(paramOne)
292
293 return nil
294 }
295
296
297 func (t *translator) T(key interface{}, params ...string) (string, error) {
298
299 trans, ok := t.translations[key]
300 if !ok {
301 return unknownTranslation, ErrUnknowTranslation
302 }
303
304 b := make([]byte, 0, 64)
305
306 var start, end, count int
307
308 for i := 0; i < len(trans.indexes); i++ {
309 end = trans.indexes[i]
310 b = append(b, trans.text[start:end]...)
311 b = append(b, params[count]...)
312 i++
313 start = trans.indexes[i]
314 count++
315 }
316
317 b = append(b, trans.text[start:]...)
318
319 return string(b), nil
320 }
321
322
323 func (t *translator) C(key interface{}, num float64, digits uint64, param string) (string, error) {
324
325 tarr, ok := t.cardinalTanslations[key]
326 if !ok {
327 return unknownTranslation, ErrUnknowTranslation
328 }
329
330 rule := t.CardinalPluralRule(num, digits)
331
332 trans := tarr[rule]
333
334 b := make([]byte, 0, 64)
335 b = append(b, trans.text[:trans.indexes[0]]...)
336 b = append(b, param...)
337 b = append(b, trans.text[trans.indexes[1]:]...)
338
339 return string(b), nil
340 }
341
342
343 func (t *translator) O(key interface{}, num float64, digits uint64, param string) (string, error) {
344
345 tarr, ok := t.ordinalTanslations[key]
346 if !ok {
347 return unknownTranslation, ErrUnknowTranslation
348 }
349
350 rule := t.OrdinalPluralRule(num, digits)
351
352 trans := tarr[rule]
353
354 b := make([]byte, 0, 64)
355 b = append(b, trans.text[:trans.indexes[0]]...)
356 b = append(b, param...)
357 b = append(b, trans.text[trans.indexes[1]:]...)
358
359 return string(b), nil
360 }
361
362
363
364 func (t *translator) R(key interface{}, num1 float64, digits1 uint64, num2 float64, digits2 uint64, param1, param2 string) (string, error) {
365
366 tarr, ok := t.rangeTanslations[key]
367 if !ok {
368 return unknownTranslation, ErrUnknowTranslation
369 }
370
371 rule := t.RangePluralRule(num1, digits1, num2, digits2)
372
373 trans := tarr[rule]
374
375 b := make([]byte, 0, 64)
376 b = append(b, trans.text[:trans.indexes[0]]...)
377 b = append(b, param1...)
378 b = append(b, trans.text[trans.indexes[1]:trans.indexes[2]]...)
379 b = append(b, param2...)
380 b = append(b, trans.text[trans.indexes[3]:]...)
381
382 return string(b), nil
383 }
384
385
386
387 func (t *translator) VerifyTranslations() error {
388
389 for k, v := range t.cardinalTanslations {
390
391 for _, rule := range t.PluralsCardinal() {
392
393 if v[rule] == nil {
394 return &ErrMissingPluralTranslation{locale: t.Locale(), translationType: "plural", rule: rule, key: k}
395 }
396 }
397 }
398
399 for k, v := range t.ordinalTanslations {
400
401 for _, rule := range t.PluralsOrdinal() {
402
403 if v[rule] == nil {
404 return &ErrMissingPluralTranslation{locale: t.Locale(), translationType: "ordinal", rule: rule, key: k}
405 }
406 }
407 }
408
409 for k, v := range t.rangeTanslations {
410
411 for _, rule := range t.PluralsRange() {
412
413 if v[rule] == nil {
414 return &ErrMissingPluralTranslation{locale: t.Locale(), translationType: "range", rule: rule, key: k}
415 }
416 }
417 }
418
419 return nil
420 }
421
View as plain text