OSDN Git Service

delete miner
[bytom/vapor.git] / vendor / github.com / go-playground / locales / cmd / generate_resources.go
1 package main
2
3 import (
4         "fmt"
5         "log"
6         "os"
7         "os/exec"
8         "regexp"
9         "sort"
10         "strconv"
11         "strings"
12
13         "github.com/go-playground/locales"
14
15         "golang.org/x/text/unicode/cldr"
16
17         "text/template"
18 )
19
20 const (
21         locDir      = "../%s"
22         locFilename = locDir + "/%s.go"
23 )
24
25 var (
26         tfuncs = template.FuncMap{
27                 "is_multibyte": func(s string) bool {
28                         return len([]byte(s)) > 1
29                 },
30                 "reverse_bytes": func(s string) string {
31                         b := make([]byte, 0, 8)
32
33                         for j := len(s) - 1; j >= 0; j-- {
34                                 b = append(b, s[j])
35                         }
36
37                         return fmt.Sprintf("%#v", b)
38                 },
39                 "byte_count": func(s ...string) string {
40                         var count int
41
42                         for i := 0; i < len(s); i++ {
43                                 count += len([]byte(s[i]))
44                         }
45
46                         return strconv.Itoa(count)
47                 },
48         }
49         prVarFuncs = map[string]string{
50                 "n": "n := math.Abs(num)\n",
51                 "i": "i := int64(n)\n",
52                 // "v": "v := ...", // inherently available as argument
53                 "w": "w := locales.W(n, v)\n",
54                 "f": "f := locales.F(n, v)\n",
55                 "t": "t := locales.T(n, v)\n",
56         }
57
58         translators            = make(map[string]*translator)
59         baseTranslators        = make(map[string]*translator)
60         globalCurrenciesMap    = make(map[string]struct{}) // ["USD"] = "$" currency code, just all currencies for mapping to enum
61         globCurrencyIdxMap     = make(map[string]int)      // ["USD"] = 0
62         globalCurrencies       = make([]string, 0, 100)    // array of currency codes index maps to enum
63         tmpl                   *template.Template
64         nModRegex              = regexp.MustCompile("(n%[0-9]+)")
65         iModRegex              = regexp.MustCompile("(i%[0-9]+)")
66         wModRegex              = regexp.MustCompile("(w%[0-9]+)")
67         fModRegex              = regexp.MustCompile("(f%[0-9]+)")
68         tModRegex              = regexp.MustCompile("(t%[0-9]+)")
69         groupLenRegex          = regexp.MustCompile(",([0-9#]+)\\.")
70         groupLenPercentRegex   = regexp.MustCompile(",([0-9#]+)$")
71         secondaryGroupLenRegex = regexp.MustCompile(",([0-9#]+),")
72         requiredNumRegex       = regexp.MustCompile("([0-9]+)\\.")
73         requiredDecimalRegex   = regexp.MustCompile("\\.([0-9]+)")
74
75         enInheritance = map[string]string{
76                 "en_AG": "en_001", "en_AI": "en_001", "en_AS": "en", "en_AU": "en_GB", "en_BB": "en_001", "en_BE": "en_GB", "en_BM": "en_001", "en_BS": "en_001", "en_BW": "en_001", "en_BZ": "en_001", "en_CA": "en_001", "en_CC": "en_001", "en_CK": "en_001", "en_CM": "en_001", "en_CX": "en_001", "en_DG": "en_GB", "en_DM": "en_001", "en_ER": "en_001", "en_FJ": "en_001", "en_FK": "en_GB", "en_FM": "en_001", "en_GB": "en_001", "en_GD": "en_001", "en_GG": "en_GB", "en_GH": "en_001", "en_GI": "en_GB", "en_GM": "en_001", "en_GU": "en", "en_GY": "en_001", "en_HK": "en_GB", "en_IE": "en_GB", "en_IM": "en_GB", "en_IN": "en_GB", "en_IO": "en_GB", "en_JE": "en_GB", "en_JM": "en_001", "en_KE": "en_001", "en_KI": "en_001", "en_KN": "en_001", "en_KY": "en_001", "en_LC": "en_001", "en_LR": "en_001", "en_LS": "en_001", "en_MG": "en_001", "en_MH": "en", "en_MO": "en_GB", "en_MP": "en", "en_MS": "en_001", "en_MT": "en_GB", "en_MU": "en_001", "en_MW": "en_001", "en_MY": "en_001", "en_NA": "en_001", "en_NF": "en_001", "en_NG": "en_001", "en_NR": "en_001", "en_NU": "en_001", "en_NZ": "en_GB", "en_PG": "en_001", "en_PH": "en_001", "en_PK": "en_GB", "en_PN": "en_001", "en_PR": "en", "en_PW": "en_001", "en_RW": "en_001", "en_SB": "en_001", "en_SC": "en_001", "en_SD": "en_001", "en_SG": "en_GB", "en_SH": "en_GB", "en_SL": "en_001", "en_SS": "en_001", "en_SX": "en_001", "en_SZ": "en_001", "en_TC": "en_001", "en_TK": "en_001", "en_TO": "en_001", "en_TT": "en_001", "en_TV": "en_001", "en_TZ": "en_001", "en_UG": "en_001", "en_UM": "en", "en_US": "en", "en_VC": "en_001", "en_VG": "en_GB", "en_VI": "en", "en_VU": "en_001", "en_WS": "en_001", "en_ZA": "en_001", "en_ZM": "en_001", "en_ZW": "en_001",
77         }
78 )
79
80 type translator struct {
81         Locale     string
82         BaseLocale string
83         // InheritedLocale string
84         Plurals        string
85         CardinalFunc   string
86         PluralsOrdinal string
87         OrdinalFunc    string
88         PluralsRange   string
89         RangeFunc      string
90         Decimal        string
91         Group          string
92         Minus          string
93         Percent        string
94         PerMille       string
95         TimeSeparator  string
96         Infinity       string
97         Currencies     string
98
99         // FmtNumber vars
100         FmtNumberExists            bool
101         FmtNumberGroupLen          int
102         FmtNumberSecondaryGroupLen int
103         FmtNumberMinDecimalLen     int
104
105         // FmtPercent vars
106         FmtPercentExists            bool
107         FmtPercentGroupLen          int
108         FmtPercentSecondaryGroupLen int
109         FmtPercentMinDecimalLen     int
110         FmtPercentPrefix            string
111         FmtPercentSuffix            string
112         FmtPercentInPrefix          bool
113         FmtPercentLeft              bool
114
115         // FmtCurrency vars
116         FmtCurrencyExists            bool
117         FmtCurrencyGroupLen          int
118         FmtCurrencySecondaryGroupLen int
119         FmtCurrencyMinDecimalLen     int
120         FmtCurrencyPrefix            string
121         FmtCurrencySuffix            string
122         FmtCurrencyInPrefix          bool
123         FmtCurrencyLeft              bool
124         FmtCurrencyNegativeExists    bool
125         FmtCurrencyNegativePrefix    string
126         FmtCurrencyNegativeSuffix    string
127         FmtCurrencyNegativeInPrefix  bool
128         FmtCurrencyNegativeLeft      bool
129
130         // Date & Time
131         FmtCalendarExists bool
132
133         FmtMonthsAbbreviated string
134         FmtMonthsNarrow      string
135         FmtMonthsWide        string
136
137         FmtDaysAbbreviated string
138         FmtDaysNarrow      string
139         FmtDaysShort       string
140         FmtDaysWide        string
141
142         FmtPeriodsAbbreviated string
143         FmtPeriodsNarrow      string
144         FmtPeriodsShort       string
145         FmtPeriodsWide        string
146
147         FmtErasAbbreviated string
148         FmtErasNarrow      string
149         FmtErasWide        string
150
151         FmtTimezones string
152
153         // calculation only fields below this point...
154         DecimalNumberFormat          string
155         PercentNumberFormat          string
156         CurrencyNumberFormat         string
157         NegativeCurrencyNumberFormat string
158
159         // Dates
160         FmtDateFull   string
161         FmtDateLong   string
162         FmtDateMedium string
163         FmtDateShort  string
164
165         // Times
166         FmtTimeFull   string
167         FmtTimeLong   string
168         FmtTimeMedium string
169         FmtTimeShort  string
170
171         // timezones per locale by type
172         timezones map[string]*zoneAbbrev // key = type eg. America_Eastern zone Abbrev will be long form eg. Eastern Standard Time, Pacific Standard Time.....
173 }
174
175 type zoneAbbrev struct {
176         standard string
177         daylight string
178 }
179
180 var timezones = map[string]*zoneAbbrev{} // key = type eg. America_Eastern zone Abbrev eg. EST & EDT
181
182 func main() {
183
184         var err error
185
186         // load template
187         tmpl, err = template.New("all").Funcs(tfuncs).ParseGlob("*.tmpl")
188         if err != nil {
189                 log.Fatal(err)
190         }
191
192         // load CLDR recourses
193         var decoder cldr.Decoder
194
195         cldr, err := decoder.DecodePath("data/core")
196         if err != nil {
197                 panic("failed decode CLDR data; " + err.Error())
198         }
199
200         preProcess(cldr)
201         postProcess(cldr)
202
203         var currencies string
204
205         for i, curr := range globalCurrencies {
206
207                 if i == 0 {
208                         currencies = curr + " Type = iota\n"
209                         continue
210                 }
211
212                 currencies += curr + "\n"
213         }
214
215         if err = os.MkdirAll(fmt.Sprintf(locDir, "currency"), 0777); err != nil {
216                 log.Fatal(err)
217         }
218
219         filename := fmt.Sprintf(locFilename, "currency", "currency")
220
221         output, err := os.Create(filename)
222         if err != nil {
223                 log.Fatal(err)
224         }
225         defer output.Close()
226
227         if err := tmpl.ExecuteTemplate(output, "currencies", currencies); err != nil {
228                 log.Fatal(err)
229         }
230
231         output.Close()
232
233         // after file written run gofmt on file to ensure best formatting
234         cmd := exec.Command("goimports", "-w", filename)
235         if err = cmd.Run(); err != nil {
236                 log.Panic("failed execute \"goimports\" for file ", filename, ": ", err)
237         }
238
239         cmd = exec.Command("gofmt", "-s", "-w", filename)
240         if err = cmd.Run(); err != nil {
241                 log.Panic("failed execute \"gofmt\" for file ", filename, ": ", err)
242         }
243
244         for _, trans := range translators {
245                 fmt.Println("Writing Data:", trans.Locale)
246
247                 if err = os.MkdirAll(fmt.Sprintf(locDir, trans.Locale), 0777); err != nil {
248                         log.Fatal(err)
249                 }
250
251                 filename = fmt.Sprintf(locFilename, trans.Locale, trans.Locale)
252
253                 output, err := os.Create(filename)
254                 if err != nil {
255                         log.Fatal(err)
256                 }
257                 defer output.Close()
258
259                 if err := tmpl.ExecuteTemplate(output, "translator", trans); err != nil {
260                         log.Fatal(err)
261                 }
262
263                 output.Close()
264
265                 // after file written run gofmt on file to ensure best formatting
266                 cmd := exec.Command("goimports", "-w", filename)
267                 if err = cmd.Run(); err != nil {
268                         log.Panic("failed execute \"goimports\" for file ", filename, ": ", err)
269                 }
270
271                 // this simplifies some syntax that I can;t find an option for in goimports, namely '-s'
272                 cmd = exec.Command("gofmt", "-s", "-w", filename)
273                 if err = cmd.Run(); err != nil {
274                         log.Panic("failed execute \"gofmt\" for file ", filename, ": ", err)
275                 }
276
277                 filename = fmt.Sprintf(locFilename, trans.Locale, trans.Locale+"_test")
278
279                 if _, err := os.Stat(filename); err == nil {
280                         fmt.Println("*************** test file exists, skipping:", filename)
281                         continue
282                 }
283
284                 output, err = os.Create(filename)
285                 if err != nil {
286                         log.Fatal(err)
287                 }
288                 defer output.Close()
289
290                 if err := tmpl.ExecuteTemplate(output, "tests", trans); err != nil {
291                         log.Fatal(err)
292                 }
293
294                 output.Close()
295
296                 // after file written run gofmt on file to ensure best formatting
297                 cmd = exec.Command("goimports", "-w", filename)
298                 if err = cmd.Run(); err != nil {
299                         log.Panic("failed execute \"goimports\" for file ", filename, ": ", err)
300                 }
301
302                 // this simplifies some syntax that I can;t find an option for in goimports, namely '-s'
303                 cmd = exec.Command("gofmt", "-s", "-w", filename)
304                 if err = cmd.Run(); err != nil {
305                         log.Panic("failed execute \"gofmt\" for file ", filename, ": ", err)
306                 }
307         }
308 }
309
310 func applyOverrides(trans *translator) {
311
312         if trans.BaseLocale == "ru" {
313                 trans.PercentNumberFormat = "#,##0%"
314         }
315 }
316
317 func postProcess(cldr *cldr.CLDR) {
318
319         for _, v := range timezones {
320
321                 // no DST
322                 if len(v.daylight) == 0 {
323                         v.daylight = v.standard
324                 }
325         }
326
327         var inherited *translator
328         var base *translator
329         var inheritedFound, baseFound bool
330
331         for _, trans := range translators {
332
333                 fmt.Println("Post Processing:", trans.Locale)
334
335                 // cardinal plural rules
336                 trans.CardinalFunc, trans.Plurals = parseCardinalPluralRuleFunc(cldr, trans.Locale, trans.BaseLocale)
337
338                 //ordinal plural rules
339                 trans.OrdinalFunc, trans.PluralsOrdinal = parseOrdinalPluralRuleFunc(cldr, trans.BaseLocale)
340
341                 // range plural rules
342                 trans.RangeFunc, trans.PluralsRange = parseRangePluralRuleFunc(cldr, trans.BaseLocale)
343
344                 // ignore base locales
345                 if trans.BaseLocale == trans.Locale {
346                         inheritedFound = false
347                         baseFound = false
348                 } else {
349
350                         inheritedFound = false
351
352                         if trans.BaseLocale == "en" {
353                                 if inherit, found := enInheritance[trans.Locale]; found {
354                                         inherited, inheritedFound = translators[inherit]
355                                 }
356                         }
357
358                         base, baseFound = baseTranslators[trans.BaseLocale]
359                 }
360
361                 // Numbers
362
363                 if len(trans.Decimal) == 0 {
364
365                         if inheritedFound {
366                                 trans.Decimal = inherited.Decimal
367                         }
368
369                         if len(trans.Decimal) == 0 && baseFound {
370                                 trans.Decimal = base.Decimal
371                         }
372
373                         if len(trans.Decimal) == 0 {
374                                 trans.Decimal = ""
375                         }
376                 }
377
378                 if len(trans.Group) == 0 {
379
380                         if inheritedFound {
381                                 trans.Group = inherited.Group
382                         }
383
384                         if len(trans.Group) == 0 && baseFound {
385                                 trans.Group = base.Group
386                         }
387
388                         if len(trans.Group) == 0 {
389                                 trans.Group = ""
390                         }
391                 }
392
393                 if len(trans.Minus) == 0 {
394
395                         if inheritedFound {
396                                 trans.Minus = inherited.Minus
397                         }
398
399                         if len(trans.Minus) == 0 && baseFound {
400                                 trans.Minus = base.Minus
401                         }
402
403                         if len(trans.Minus) == 0 {
404                                 trans.Minus = ""
405                         }
406                 }
407
408                 if len(trans.Percent) == 0 {
409
410                         if inheritedFound {
411                                 trans.Percent = inherited.Percent
412                         }
413
414                         if len(trans.Percent) == 0 && baseFound {
415                                 trans.Percent = base.Percent
416                         }
417
418                         if len(trans.Percent) == 0 {
419                                 trans.Percent = ""
420                         }
421                 }
422
423                 if len(trans.PerMille) == 0 {
424
425                         if inheritedFound {
426                                 trans.PerMille = inherited.PerMille
427                         }
428
429                         if len(trans.PerMille) == 0 && baseFound {
430                                 trans.PerMille = base.PerMille
431                         }
432
433                         if len(trans.PerMille) == 0 {
434                                 trans.PerMille = ""
435                         }
436                 }
437
438                 if len(trans.TimeSeparator) == 0 && inheritedFound {
439                         trans.TimeSeparator = inherited.TimeSeparator
440                 }
441
442                 if len(trans.TimeSeparator) == 0 && baseFound {
443                         trans.TimeSeparator = base.TimeSeparator
444                 }
445
446                 if len(trans.Infinity) == 0 && inheritedFound {
447                         trans.Infinity = inherited.Infinity
448                 }
449
450                 if len(trans.Infinity) == 0 && baseFound {
451                         trans.Infinity = base.Infinity
452                 }
453
454                 // Currency
455
456                 // number values
457
458                 if len(trans.DecimalNumberFormat) == 0 && inheritedFound {
459                         trans.DecimalNumberFormat = inherited.DecimalNumberFormat
460                 }
461
462                 if len(trans.DecimalNumberFormat) == 0 && baseFound {
463                         trans.DecimalNumberFormat = base.DecimalNumberFormat
464                 }
465
466                 if len(trans.PercentNumberFormat) == 0 && inheritedFound {
467                         trans.PercentNumberFormat = inherited.PercentNumberFormat
468                 }
469
470                 if len(trans.PercentNumberFormat) == 0 && baseFound {
471                         trans.PercentNumberFormat = base.PercentNumberFormat
472                 }
473
474                 if len(trans.CurrencyNumberFormat) == 0 && inheritedFound {
475                         trans.CurrencyNumberFormat = inherited.CurrencyNumberFormat
476                 }
477
478                 if len(trans.CurrencyNumberFormat) == 0 && baseFound {
479                         trans.CurrencyNumberFormat = base.CurrencyNumberFormat
480                 }
481
482                 if len(trans.NegativeCurrencyNumberFormat) == 0 && inheritedFound {
483                         trans.NegativeCurrencyNumberFormat = inherited.NegativeCurrencyNumberFormat
484                 }
485
486                 if len(trans.NegativeCurrencyNumberFormat) == 0 && baseFound {
487                         trans.NegativeCurrencyNumberFormat = base.NegativeCurrencyNumberFormat
488                 }
489
490                 // date values
491
492                 if len(trans.FmtDateFull) == 0 && inheritedFound {
493                         trans.FmtDateFull = inherited.FmtDateFull
494                 }
495
496                 if len(trans.FmtDateFull) == 0 && baseFound {
497                         trans.FmtDateFull = base.FmtDateFull
498                 }
499
500                 if len(trans.FmtDateLong) == 0 && inheritedFound {
501                         trans.FmtDateLong = inherited.FmtDateLong
502                 }
503
504                 if len(trans.FmtDateLong) == 0 && baseFound {
505                         trans.FmtDateLong = base.FmtDateLong
506                 }
507
508                 if len(trans.FmtDateMedium) == 0 && inheritedFound {
509                         trans.FmtDateMedium = inherited.FmtDateMedium
510                 }
511
512                 if len(trans.FmtDateMedium) == 0 && baseFound {
513                         trans.FmtDateMedium = base.FmtDateMedium
514                 }
515
516                 if len(trans.FmtDateShort) == 0 && inheritedFound {
517                         trans.FmtDateShort = inherited.FmtDateShort
518                 }
519
520                 if len(trans.FmtDateShort) == 0 && baseFound {
521                         trans.FmtDateShort = base.FmtDateShort
522                 }
523
524                 // time values
525
526                 if len(trans.FmtTimeFull) == 0 && inheritedFound {
527                         trans.FmtTimeFull = inherited.FmtTimeFull
528                 }
529
530                 if len(trans.FmtTimeFull) == 0 && baseFound {
531                         trans.FmtTimeFull = base.FmtTimeFull
532                 }
533
534                 if len(trans.FmtTimeLong) == 0 && inheritedFound {
535                         trans.FmtTimeLong = inherited.FmtTimeLong
536                 }
537
538                 if len(trans.FmtTimeLong) == 0 && baseFound {
539                         trans.FmtTimeLong = base.FmtTimeLong
540                 }
541
542                 if len(trans.FmtTimeMedium) == 0 && inheritedFound {
543                         trans.FmtTimeMedium = inherited.FmtTimeMedium
544                 }
545
546                 if len(trans.FmtTimeMedium) == 0 && baseFound {
547                         trans.FmtTimeMedium = base.FmtTimeMedium
548                 }
549
550                 if len(trans.FmtTimeShort) == 0 && inheritedFound {
551                         trans.FmtTimeShort = inherited.FmtTimeShort
552                 }
553
554                 if len(trans.FmtTimeShort) == 0 && baseFound {
555                         trans.FmtTimeShort = base.FmtTimeShort
556                 }
557
558                 // month values
559
560                 if len(trans.FmtMonthsAbbreviated) == 0 && inheritedFound {
561                         trans.FmtMonthsAbbreviated = inherited.FmtMonthsAbbreviated
562                 }
563
564                 if len(trans.FmtMonthsAbbreviated) == 0 && baseFound {
565                         trans.FmtMonthsAbbreviated = base.FmtMonthsAbbreviated
566                 }
567
568                 if len(trans.FmtMonthsNarrow) == 0 && inheritedFound {
569                         trans.FmtMonthsNarrow = inherited.FmtMonthsNarrow
570                 }
571
572                 if len(trans.FmtMonthsNarrow) == 0 && baseFound {
573                         trans.FmtMonthsNarrow = base.FmtMonthsNarrow
574                 }
575
576                 if len(trans.FmtMonthsWide) == 0 && inheritedFound {
577                         trans.FmtMonthsWide = inherited.FmtMonthsWide
578                 }
579
580                 if len(trans.FmtMonthsWide) == 0 && baseFound {
581                         trans.FmtMonthsWide = base.FmtMonthsWide
582                 }
583
584                 // day values
585
586                 if len(trans.FmtDaysAbbreviated) == 0 && inheritedFound {
587                         trans.FmtDaysAbbreviated = inherited.FmtDaysAbbreviated
588                 }
589
590                 if len(trans.FmtDaysAbbreviated) == 0 && baseFound {
591                         trans.FmtDaysAbbreviated = base.FmtDaysAbbreviated
592                 }
593
594                 if len(trans.FmtDaysNarrow) == 0 && inheritedFound {
595                         trans.FmtDaysNarrow = inherited.FmtDaysNarrow
596                 }
597
598                 if len(trans.FmtDaysNarrow) == 0 && baseFound {
599                         trans.FmtDaysNarrow = base.FmtDaysNarrow
600                 }
601
602                 if len(trans.FmtDaysShort) == 0 && inheritedFound {
603                         trans.FmtDaysShort = inherited.FmtDaysShort
604                 }
605
606                 if len(trans.FmtDaysShort) == 0 && baseFound {
607                         trans.FmtDaysShort = base.FmtDaysShort
608                 }
609
610                 if len(trans.FmtDaysWide) == 0 && inheritedFound {
611                         trans.FmtDaysWide = inherited.FmtDaysWide
612                 }
613
614                 if len(trans.FmtDaysWide) == 0 && baseFound {
615                         trans.FmtDaysWide = base.FmtDaysWide
616                 }
617
618                 // period values
619
620                 if len(trans.FmtPeriodsAbbreviated) == 0 && inheritedFound {
621                         trans.FmtPeriodsAbbreviated = inherited.FmtPeriodsAbbreviated
622                 }
623
624                 if len(trans.FmtPeriodsAbbreviated) == 0 && baseFound {
625                         trans.FmtPeriodsAbbreviated = base.FmtPeriodsAbbreviated
626                 }
627
628                 if len(trans.FmtPeriodsNarrow) == 0 && inheritedFound {
629                         trans.FmtPeriodsNarrow = inherited.FmtPeriodsNarrow
630                 }
631
632                 if len(trans.FmtPeriodsNarrow) == 0 && baseFound {
633                         trans.FmtPeriodsNarrow = base.FmtPeriodsNarrow
634                 }
635
636                 if len(trans.FmtPeriodsShort) == 0 && inheritedFound {
637                         trans.FmtPeriodsShort = inherited.FmtPeriodsShort
638                 }
639
640                 if len(trans.FmtPeriodsShort) == 0 && baseFound {
641                         trans.FmtPeriodsShort = base.FmtPeriodsShort
642                 }
643
644                 if len(trans.FmtPeriodsWide) == 0 && inheritedFound {
645                         trans.FmtPeriodsWide = inherited.FmtPeriodsWide
646                 }
647
648                 if len(trans.FmtPeriodsWide) == 0 && baseFound {
649                         trans.FmtPeriodsWide = base.FmtPeriodsWide
650                 }
651
652                 // era values
653
654                 if len(trans.FmtErasAbbreviated) == 0 && inheritedFound {
655                         trans.FmtErasAbbreviated = inherited.FmtErasAbbreviated
656                 }
657
658                 if len(trans.FmtErasAbbreviated) == 0 && baseFound {
659                         trans.FmtErasAbbreviated = base.FmtErasAbbreviated
660                 }
661
662                 if len(trans.FmtErasNarrow) == 0 && inheritedFound {
663                         trans.FmtErasNarrow = inherited.FmtErasNarrow
664                 }
665
666                 if len(trans.FmtErasNarrow) == 0 && baseFound {
667                         trans.FmtErasNarrow = base.FmtErasNarrow
668                 }
669
670                 if len(trans.FmtErasWide) == 0 && inheritedFound {
671                         trans.FmtErasWide = inherited.FmtErasWide
672                 }
673
674                 if len(trans.FmtErasWide) == 0 && baseFound {
675                         trans.FmtErasWide = base.FmtErasWide
676                 }
677
678                 ldml := cldr.RawLDML(trans.Locale)
679
680                 currencies := make([]string, len(globalCurrencies), len(globalCurrencies))
681
682                 var kval string
683
684                 for k, v := range globCurrencyIdxMap {
685
686                         kval = k
687                         // if kval[:len(kval)-1] != " " {
688                         //      kval += " "
689                         // }
690
691                         currencies[v] = kval
692                 }
693
694                 // some just have no data...
695                 if ldml.Numbers != nil {
696
697                         if ldml.Numbers.Currencies != nil {
698                                 for _, currency := range ldml.Numbers.Currencies.Currency {
699
700                                         if len(currency.Symbol) == 0 {
701                                                 continue
702                                         }
703
704                                         if len(currency.Symbol[0].Data()) == 0 {
705                                                 continue
706                                         }
707
708                                         if len(currency.Type) == 0 {
709                                                 continue
710                                         }
711
712                                         currencies[globCurrencyIdxMap[currency.Type]] = currency.Symbol[0].Data()
713                                 }
714                         }
715                 }
716
717                 trans.Currencies = fmt.Sprintf("%#v", currencies)
718
719                 // timezones
720
721                 if (trans.timezones == nil || len(trans.timezones) == 0) && inheritedFound {
722                         trans.timezones = inherited.timezones
723                 }
724
725                 if (trans.timezones == nil || len(trans.timezones) == 0) && baseFound {
726                         trans.timezones = base.timezones
727                 }
728
729                 // make sure all inherited timezones are part of sub locale timezones
730                 if inheritedFound {
731
732                         var ok bool
733
734                         for k, v := range inherited.timezones {
735
736                                 if _, ok = trans.timezones[k]; ok {
737                                         continue
738                                 }
739
740                                 trans.timezones[k] = v
741                         }
742                 }
743
744                 // make sure all base timezones are part of sub locale timezones
745                 if baseFound {
746
747                         var ok bool
748
749                         for k, v := range base.timezones {
750
751                                 if _, ok = trans.timezones[k]; ok {
752                                         continue
753                                 }
754
755                                 trans.timezones[k] = v
756                         }
757                 }
758
759                 applyOverrides(trans)
760
761                 parseDecimalNumberFormat(trans)
762                 parsePercentNumberFormat(trans)
763                 parseCurrencyNumberFormat(trans)
764         }
765
766         for _, trans := range translators {
767
768                 fmt.Println("Final Processing:", trans.Locale)
769
770                 // if it's still nill.....
771                 if trans.timezones == nil {
772                         trans.timezones = make(map[string]*zoneAbbrev)
773                 }
774
775                 tz := make(map[string]string) // key = abbrev locale eg. EST, EDT, MST, PST... value = long locale eg. Eastern Standard Time, Pacific Time.....
776
777                 for k, v := range timezones {
778
779                         ttz, ok := trans.timezones[k]
780                         if !ok {
781                                 ttz = v
782                                 trans.timezones[k] = v
783                         }
784
785                         tz[v.standard] = ttz.standard
786                         tz[v.daylight] = ttz.daylight
787                 }
788
789                 trans.FmtTimezones = fmt.Sprintf("%#v", tz)
790
791                 if len(trans.TimeSeparator) == 0 {
792                         trans.TimeSeparator = ":"
793                 }
794
795                 trans.FmtDateShort, trans.FmtDateMedium, trans.FmtDateLong, trans.FmtDateFull = parseDateFormats(trans, trans.FmtDateShort, trans.FmtDateMedium, trans.FmtDateLong, trans.FmtDateFull)
796                 trans.FmtTimeShort, trans.FmtTimeMedium, trans.FmtTimeLong, trans.FmtTimeFull = parseDateFormats(trans, trans.FmtTimeShort, trans.FmtTimeMedium, trans.FmtTimeLong, trans.FmtTimeFull)
797         }
798 }
799
800 // preprocesses maps, array etc... just requires multiple passes no choice....
801 func preProcess(cldrVar *cldr.CLDR) {
802
803         for _, l := range cldrVar.Locales() {
804
805                 fmt.Println("Pre Processing:", l)
806
807                 split := strings.SplitN(l, "_", 2)
808                 baseLocale := split[0]
809                 // inheritedLocale := baseLocale
810
811                 // // one of the inherited english locales
812                 // // http://cldr.unicode.org/development/development-process/design-proposals/english-inheritance
813                 // if l == "en_001" || l == "en_GB" {
814                 //      inheritedLocale = l
815                 // }
816
817                 trans := &translator{
818                         Locale:     l,
819                         BaseLocale: baseLocale,
820                         // InheritedLocale: inheritedLocale,
821                 }
822
823                 // if is a base locale
824                 if len(split) == 1 {
825                         baseTranslators[baseLocale] = trans
826                 }
827
828                 // baseTranslators[l] = trans
829                 // baseTranslators[baseLocale] = trans // allowing for unofficial fallback if none exists
830                 translators[l] = trans
831
832                 // get number, currency and datetime symbols
833
834                 // number values
835                 ldml := cldrVar.RawLDML(l)
836
837                 // some just have no data...
838                 if ldml.Numbers != nil {
839
840                         if len(ldml.Numbers.Symbols) > 0 {
841
842                                 symbol := ldml.Numbers.Symbols[0]
843
844                                 // Try to get the default numbering system instead of the first one
845                                 systems := ldml.Numbers.DefaultNumberingSystem
846                                 // There shouldn't really be more than one DefaultNumberingSystem
847                                 if len(systems) > 0 {
848                                         if dns := systems[0].Data(); dns != "" {
849                                                 for k := range ldml.Numbers.Symbols {
850                                                         if ldml.Numbers.Symbols[k].NumberSystem == dns {
851                                                                 symbol = ldml.Numbers.Symbols[k]
852                                                                 break
853                                                         }
854                                                 }
855                                         }
856                                 }
857
858                                 if len(symbol.Decimal) > 0 {
859                                         trans.Decimal = symbol.Decimal[0].Data()
860                                 }
861                                 if len(symbol.Group) > 0 {
862                                         trans.Group = symbol.Group[0].Data()
863                                 }
864                                 if len(symbol.MinusSign) > 0 {
865                                         trans.Minus = symbol.MinusSign[0].Data()
866                                 }
867                                 if len(symbol.PercentSign) > 0 {
868                                         trans.Percent = symbol.PercentSign[0].Data()
869                                 }
870                                 if len(symbol.PerMille) > 0 {
871                                         trans.PerMille = symbol.PerMille[0].Data()
872                                 }
873
874                                 if len(symbol.TimeSeparator) > 0 {
875                                         trans.TimeSeparator = symbol.TimeSeparator[0].Data()
876                                 }
877
878                                 if len(symbol.Infinity) > 0 {
879                                         trans.Infinity = symbol.Infinity[0].Data()
880                                 }
881                         }
882
883                         if ldml.Numbers.Currencies != nil {
884
885                                 for _, currency := range ldml.Numbers.Currencies.Currency {
886
887                                         if len(strings.TrimSpace(currency.Type)) == 0 {
888                                                 continue
889                                         }
890
891                                         globalCurrenciesMap[currency.Type] = struct{}{}
892                                 }
893                         }
894
895                         if len(ldml.Numbers.DecimalFormats) > 0 && len(ldml.Numbers.DecimalFormats[0].DecimalFormatLength) > 0 {
896
897                                 for _, dfl := range ldml.Numbers.DecimalFormats[0].DecimalFormatLength {
898                                         if len(dfl.Type) == 0 {
899                                                 trans.DecimalNumberFormat = dfl.DecimalFormat[0].Pattern[0].Data()
900                                                 break
901                                         }
902                                 }
903                         }
904
905                         if len(ldml.Numbers.PercentFormats) > 0 && len(ldml.Numbers.PercentFormats[0].PercentFormatLength) > 0 {
906
907                                 for _, dfl := range ldml.Numbers.PercentFormats[0].PercentFormatLength {
908                                         if len(dfl.Type) == 0 {
909                                                 trans.PercentNumberFormat = dfl.PercentFormat[0].Pattern[0].Data()
910                                                 break
911                                         }
912                                 }
913                         }
914
915                         if len(ldml.Numbers.CurrencyFormats) > 0 && len(ldml.Numbers.CurrencyFormats[0].CurrencyFormatLength) > 0 {
916
917                                 if len(ldml.Numbers.CurrencyFormats[0].CurrencyFormatLength[0].CurrencyFormat) > 1 {
918
919                                         split := strings.SplitN(ldml.Numbers.CurrencyFormats[0].CurrencyFormatLength[0].CurrencyFormat[1].Pattern[0].Data(), ";", 2)
920
921                                         trans.CurrencyNumberFormat = split[0]
922
923                                         if len(split) > 1 && len(split[1]) > 0 {
924                                                 trans.NegativeCurrencyNumberFormat = split[1]
925                                         } else {
926                                                 trans.NegativeCurrencyNumberFormat = trans.CurrencyNumberFormat
927                                         }
928                                 } else {
929                                         trans.CurrencyNumberFormat = ldml.Numbers.CurrencyFormats[0].CurrencyFormatLength[0].CurrencyFormat[0].Pattern[0].Data()
930                                         trans.NegativeCurrencyNumberFormat = trans.CurrencyNumberFormat
931                                 }
932                         }
933                 }
934
935                 if ldml.Dates != nil {
936
937                         if ldml.Dates.TimeZoneNames != nil {
938
939                                 for _, zone := range ldml.Dates.TimeZoneNames.Metazone {
940
941                                         for _, short := range zone.Short {
942
943                                                 if len(short.Standard) > 0 {
944                                                         za, ok := timezones[zone.Type]
945                                                         if !ok {
946                                                                 za = new(zoneAbbrev)
947                                                                 timezones[zone.Type] = za
948                                                         }
949                                                         za.standard = short.Standard[0].Data()
950                                                 }
951
952                                                 if len(short.Daylight) > 0 {
953                                                         za, ok := timezones[zone.Type]
954                                                         if !ok {
955                                                                 za = new(zoneAbbrev)
956                                                                 timezones[zone.Type] = za
957                                                         }
958                                                         za.daylight = short.Daylight[0].Data()
959                                                 }
960                                         }
961
962                                         for _, long := range zone.Long {
963
964                                                 if trans.timezones == nil {
965                                                         trans.timezones = make(map[string]*zoneAbbrev)
966                                                 }
967
968                                                 if len(long.Standard) > 0 {
969                                                         za, ok := trans.timezones[zone.Type]
970                                                         if !ok {
971                                                                 za = new(zoneAbbrev)
972                                                                 trans.timezones[zone.Type] = za
973                                                         }
974                                                         za.standard = long.Standard[0].Data()
975                                                 }
976
977                                                 za, ok := trans.timezones[zone.Type]
978                                                 if !ok {
979                                                         za = new(zoneAbbrev)
980                                                         trans.timezones[zone.Type] = za
981                                                 }
982
983                                                 if len(long.Daylight) > 0 {
984                                                         za.daylight = long.Daylight[0].Data()
985                                                 } else {
986                                                         za.daylight = za.standard
987                                                 }
988                                         }
989                                 }
990                         }
991
992                         if ldml.Dates.Calendars != nil {
993
994                                 var calendar *cldr.Calendar
995
996                                 for _, cal := range ldml.Dates.Calendars.Calendar {
997                                         if cal.Type == "gregorian" {
998                                                 calendar = cal
999                                         }
1000                                 }
1001
1002                                 if calendar != nil {
1003
1004                                         if calendar.DateFormats != nil {
1005
1006                                                 for _, datefmt := range calendar.DateFormats.DateFormatLength {
1007
1008                                                         switch datefmt.Type {
1009                                                         case "full":
1010                                                                 trans.FmtDateFull = datefmt.DateFormat[0].Pattern[0].Data()
1011
1012                                                         case "long":
1013                                                                 trans.FmtDateLong = datefmt.DateFormat[0].Pattern[0].Data()
1014
1015                                                         case "medium":
1016                                                                 trans.FmtDateMedium = datefmt.DateFormat[0].Pattern[0].Data()
1017
1018                                                         case "short":
1019                                                                 trans.FmtDateShort = datefmt.DateFormat[0].Pattern[0].Data()
1020                                                         }
1021                                                 }
1022                                         }
1023
1024                                         if calendar.TimeFormats != nil {
1025
1026                                                 for _, datefmt := range calendar.TimeFormats.TimeFormatLength {
1027
1028                                                         switch datefmt.Type {
1029                                                         case "full":
1030                                                                 trans.FmtTimeFull = datefmt.TimeFormat[0].Pattern[0].Data()
1031                                                         case "long":
1032                                                                 trans.FmtTimeLong = datefmt.TimeFormat[0].Pattern[0].Data()
1033                                                         case "medium":
1034                                                                 trans.FmtTimeMedium = datefmt.TimeFormat[0].Pattern[0].Data()
1035                                                         case "short":
1036                                                                 trans.FmtTimeShort = datefmt.TimeFormat[0].Pattern[0].Data()
1037                                                         }
1038                                                 }
1039                                         }
1040
1041                                         if calendar.Months != nil {
1042
1043                                                 // month context starts at 'format', but there is also has 'stand-alone'
1044                                                 // I'm making the decision to use the 'stand-alone' if, and only if,
1045                                                 // the value does not exist in the 'format' month context
1046                                                 var abbrSet, narrSet, wideSet bool
1047
1048                                                 for _, monthctx := range calendar.Months.MonthContext {
1049
1050                                                         for _, months := range monthctx.MonthWidth {
1051
1052                                                                 var monthData []string
1053
1054                                                                 for _, m := range months.Month {
1055
1056                                                                         if len(m.Data()) == 0 {
1057                                                                                 continue
1058                                                                         }
1059
1060                                                                         switch m.Type {
1061                                                                         case "1":
1062                                                                                 monthData = append(monthData, m.Data())
1063                                                                         case "2":
1064                                                                                 monthData = append(monthData, m.Data())
1065                                                                         case "3":
1066                                                                                 monthData = append(monthData, m.Data())
1067                                                                         case "4":
1068                                                                                 monthData = append(monthData, m.Data())
1069                                                                         case "5":
1070                                                                                 monthData = append(monthData, m.Data())
1071                                                                         case "6":
1072                                                                                 monthData = append(monthData, m.Data())
1073                                                                         case "7":
1074                                                                                 monthData = append(monthData, m.Data())
1075                                                                         case "8":
1076                                                                                 monthData = append(monthData, m.Data())
1077                                                                         case "9":
1078                                                                                 monthData = append(monthData, m.Data())
1079                                                                         case "10":
1080                                                                                 monthData = append(monthData, m.Data())
1081                                                                         case "11":
1082                                                                                 monthData = append(monthData, m.Data())
1083                                                                         case "12":
1084                                                                                 monthData = append(monthData, m.Data())
1085                                                                         }
1086                                                                 }
1087
1088                                                                 if len(monthData) > 0 {
1089
1090                                                                         // making array indexes line up with month values
1091                                                                         // so I'll have an extra empty value, it's way faster
1092                                                                         // than a switch over all type values...
1093                                                                         monthData = append(make([]string, 1, len(monthData)+1), monthData...)
1094
1095                                                                         switch months.Type {
1096                                                                         case "abbreviated":
1097                                                                                 if !abbrSet {
1098                                                                                         abbrSet = true
1099                                                                                         trans.FmtMonthsAbbreviated = fmt.Sprintf("%#v", monthData)
1100                                                                                 }
1101                                                                         case "narrow":
1102                                                                                 if !narrSet {
1103                                                                                         narrSet = true
1104                                                                                         trans.FmtMonthsNarrow = fmt.Sprintf("%#v", monthData)
1105                                                                                 }
1106                                                                         case "wide":
1107                                                                                 if !wideSet {
1108                                                                                         wideSet = true
1109                                                                                         trans.FmtMonthsWide = fmt.Sprintf("%#v", monthData)
1110                                                                                 }
1111                                                                         }
1112                                                                 }
1113                                                         }
1114                                                 }
1115                                         }
1116
1117                                         if calendar.Days != nil {
1118
1119                                                 // day context starts at 'format', but there is also has 'stand-alone'
1120                                                 // I'm making the decision to use the 'stand-alone' if, and only if,
1121                                                 // the value does not exist in the 'format' day context
1122                                                 var abbrSet, narrSet, shortSet, wideSet bool
1123
1124                                                 for _, dayctx := range calendar.Days.DayContext {
1125
1126                                                         for _, days := range dayctx.DayWidth {
1127
1128                                                                 var dayData []string
1129
1130                                                                 for _, d := range days.Day {
1131
1132                                                                         switch d.Type {
1133                                                                         case "sun":
1134                                                                                 dayData = append(dayData, d.Data())
1135                                                                         case "mon":
1136                                                                                 dayData = append(dayData, d.Data())
1137                                                                         case "tue":
1138                                                                                 dayData = append(dayData, d.Data())
1139                                                                         case "wed":
1140                                                                                 dayData = append(dayData, d.Data())
1141                                                                         case "thu":
1142                                                                                 dayData = append(dayData, d.Data())
1143                                                                         case "fri":
1144                                                                                 dayData = append(dayData, d.Data())
1145                                                                         case "sat":
1146                                                                                 dayData = append(dayData, d.Data())
1147                                                                         }
1148                                                                 }
1149
1150                                                                 if len(dayData) > 0 {
1151                                                                         switch days.Type {
1152                                                                         case "abbreviated":
1153                                                                                 if !abbrSet {
1154                                                                                         abbrSet = true
1155                                                                                         trans.FmtDaysAbbreviated = fmt.Sprintf("%#v", dayData)
1156                                                                                 }
1157                                                                         case "narrow":
1158                                                                                 if !narrSet {
1159                                                                                         narrSet = true
1160                                                                                         trans.FmtDaysNarrow = fmt.Sprintf("%#v", dayData)
1161                                                                                 }
1162                                                                         case "short":
1163                                                                                 if !shortSet {
1164                                                                                         shortSet = true
1165                                                                                         trans.FmtDaysShort = fmt.Sprintf("%#v", dayData)
1166                                                                                 }
1167                                                                         case "wide":
1168                                                                                 if !wideSet {
1169                                                                                         wideSet = true
1170                                                                                         trans.FmtDaysWide = fmt.Sprintf("%#v", dayData)
1171                                                                                 }
1172                                                                         }
1173                                                                 }
1174                                                         }
1175                                                 }
1176                                         }
1177
1178                                         if calendar.DayPeriods != nil {
1179
1180                                                 // day periods context starts at 'format', but there is also has 'stand-alone'
1181                                                 // I'm making the decision to use the 'stand-alone' if, and only if,
1182                                                 // the value does not exist in the 'format' day period context
1183                                                 var abbrSet, narrSet, shortSet, wideSet bool
1184
1185                                                 for _, ctx := range calendar.DayPeriods.DayPeriodContext {
1186
1187                                                         for _, width := range ctx.DayPeriodWidth {
1188
1189                                                                 // [0] = AM
1190                                                                 // [0] = PM
1191                                                                 ampm := make([]string, 2, 2)
1192
1193                                                                 for _, d := range width.DayPeriod {
1194
1195                                                                         if d.Type == "am" {
1196                                                                                 ampm[0] = d.Data()
1197                                                                                 continue
1198                                                                         }
1199
1200                                                                         if d.Type == "pm" {
1201                                                                                 ampm[1] = d.Data()
1202                                                                         }
1203                                                                 }
1204
1205                                                                 switch width.Type {
1206                                                                 case "abbreviated":
1207                                                                         if !abbrSet {
1208                                                                                 abbrSet = true
1209                                                                                 trans.FmtPeriodsAbbreviated = fmt.Sprintf("%#v", ampm)
1210                                                                         }
1211                                                                 case "narrow":
1212                                                                         if !narrSet {
1213                                                                                 narrSet = true
1214                                                                                 trans.FmtPeriodsNarrow = fmt.Sprintf("%#v", ampm)
1215                                                                         }
1216                                                                 case "short":
1217                                                                         if !shortSet {
1218                                                                                 shortSet = true
1219                                                                                 trans.FmtPeriodsShort = fmt.Sprintf("%#v", ampm)
1220                                                                         }
1221                                                                 case "wide":
1222                                                                         if !wideSet {
1223                                                                                 wideSet = true
1224                                                                                 trans.FmtPeriodsWide = fmt.Sprintf("%#v", ampm)
1225                                                                         }
1226                                                                 }
1227                                                         }
1228                                                 }
1229                                         }
1230
1231                                         if calendar.Eras != nil {
1232
1233                                                 // [0] = BC
1234                                                 // [0] = AD
1235                                                 abbrev := make([]string, 2, 2)
1236                                                 narr := make([]string, 2, 2)
1237                                                 wide := make([]string, 2, 2)
1238
1239                                                 if calendar.Eras.EraAbbr != nil {
1240
1241                                                         if len(calendar.Eras.EraAbbr.Era) == 4 {
1242                                                                 abbrev[0] = calendar.Eras.EraAbbr.Era[0].Data()
1243                                                                 abbrev[1] = calendar.Eras.EraAbbr.Era[2].Data()
1244                                                         } else if len(calendar.Eras.EraAbbr.Era) == 2 {
1245                                                                 abbrev[0] = calendar.Eras.EraAbbr.Era[0].Data()
1246                                                                 abbrev[1] = calendar.Eras.EraAbbr.Era[1].Data()
1247                                                         }
1248                                                 }
1249
1250                                                 if calendar.Eras.EraNarrow != nil {
1251
1252                                                         if len(calendar.Eras.EraNarrow.Era) == 4 {
1253                                                                 narr[0] = calendar.Eras.EraNarrow.Era[0].Data()
1254                                                                 narr[1] = calendar.Eras.EraNarrow.Era[2].Data()
1255                                                         } else if len(calendar.Eras.EraNarrow.Era) == 2 {
1256                                                                 narr[0] = calendar.Eras.EraNarrow.Era[0].Data()
1257                                                                 narr[1] = calendar.Eras.EraNarrow.Era[1].Data()
1258                                                         }
1259                                                 }
1260
1261                                                 if calendar.Eras.EraNames != nil {
1262
1263                                                         if len(calendar.Eras.EraNames.Era) == 4 {
1264                                                                 wide[0] = calendar.Eras.EraNames.Era[0].Data()
1265                                                                 wide[1] = calendar.Eras.EraNames.Era[2].Data()
1266                                                         } else if len(calendar.Eras.EraNames.Era) == 2 {
1267                                                                 wide[0] = calendar.Eras.EraNames.Era[0].Data()
1268                                                                 wide[1] = calendar.Eras.EraNames.Era[1].Data()
1269                                                         }
1270                                                 }
1271
1272                                                 trans.FmtErasAbbreviated = fmt.Sprintf("%#v", abbrev)
1273                                                 trans.FmtErasNarrow = fmt.Sprintf("%#v", narr)
1274                                                 trans.FmtErasWide = fmt.Sprintf("%#v", wide)
1275                                         }
1276                                 }
1277                         }
1278                 }
1279         }
1280
1281         for k := range globalCurrenciesMap {
1282                 globalCurrencies = append(globalCurrencies, k)
1283         }
1284
1285         sort.Strings(globalCurrencies)
1286
1287         for i, loc := range globalCurrencies {
1288                 globCurrencyIdxMap[loc] = i
1289         }
1290 }
1291
1292 func parseDateFormats(trans *translator, shortFormat, mediumFormat, longFormat, fullFormat string) (short, medium, long, full string) {
1293
1294         // Short Date Parsing
1295
1296         short = parseDateTimeFormat(trans.BaseLocale, shortFormat, 2)
1297         medium = parseDateTimeFormat(trans.BaseLocale, mediumFormat, 2)
1298         long = parseDateTimeFormat(trans.BaseLocale, longFormat, 1)
1299         full = parseDateTimeFormat(trans.BaseLocale, fullFormat, 0)
1300
1301         // End Short Data Parsing
1302
1303         return
1304 }
1305
1306 func parseDateTimeFormat(baseLocale, format string, eraScore uint8) (results string) {
1307
1308         // rules:
1309         // y = four digit year
1310         // yy = two digit year
1311
1312         // var b []byte
1313
1314         var inConstantText bool
1315         var start int
1316
1317         for i := 0; i < len(format); i++ {
1318
1319                 switch format[i] {
1320
1321                 // time separator
1322                 case ':':
1323
1324                         if inConstantText {
1325                                 inConstantText = false
1326                                 results += "b = append(b, " + fmt.Sprintf("%#v", []byte(format[start:i])) + "...)\n"
1327                         }
1328
1329                         results += "b = append(b, " + baseLocale + ".timeSeparator...)"
1330                 case '\'':
1331
1332                         i++
1333                         startI := i
1334
1335                         // peek to see if ''
1336                         if len(format) != i && format[i] == '\'' {
1337
1338                                 if inConstantText {
1339                                         inConstantText = false
1340                                         results += "b = append(b, " + fmt.Sprintf("%#v", []byte(format[start:i-1])) + "...)\n"
1341                                 } else {
1342                                         inConstantText = true
1343                                         start = i
1344                                 }
1345
1346                                 continue
1347                         }
1348
1349                         // not '' so whatever comes between '' is constant
1350
1351                         if len(format) != i {
1352
1353                                 // advance i to the next single quote + 1
1354                                 for ; i < len(format); i++ {
1355                                         if format[i] == '\'' {
1356
1357                                                 if inConstantText {
1358                                                         inConstantText = false
1359                                                         b := []byte(format[start : startI-1])
1360                                                         b = append(b, []byte(format[startI:i])...)
1361
1362                                                         results += "b = append(b, " + fmt.Sprintf("%#v", b) + "...)\n"
1363
1364                                                 } else {
1365                                                         results += "b = append(b, " + fmt.Sprintf("%#v", []byte(format[startI:i])) + "...)\n"
1366                                                 }
1367
1368                                                 break
1369                                         }
1370                                 }
1371                         }
1372
1373                 // 24 hour
1374                 case 'H':
1375
1376                         if inConstantText {
1377                                 inConstantText = false
1378                                 results += "b = append(b, " + fmt.Sprintf("%#v", []byte(format[start:i])) + "...)\n"
1379                         }
1380
1381                         // peek
1382                         // two digit hour required?
1383                         if len(format) != i+1 && format[i+1] == 'H' {
1384                                 i++
1385                                 results += `
1386                                         if t.Hour() < 10 {
1387                                                 b = append(b, '0')
1388                                         }
1389
1390                                 `
1391                         }
1392
1393                         results += "b = strconv.AppendInt(b, int64(t.Hour()), 10)\n"
1394
1395                 // hour
1396                 case 'h':
1397
1398                         if inConstantText {
1399                                 inConstantText = false
1400                                 results += "b = append(b, " + fmt.Sprintf("%#v", []byte(format[start:i])) + "...)\n"
1401                         }
1402
1403                         results += `
1404                                 h := t.Hour()
1405
1406                                 if h > 12 {
1407                                         h -= 12
1408                                 }
1409
1410                         `
1411
1412                         // peek
1413                         // two digit hour required?
1414                         if len(format) != i+1 && format[i+1] == 'h' {
1415                                 i++
1416                                 results += `
1417                                         if h < 10 {
1418                                                 b = append(b, '0')
1419                                         }
1420
1421                                 `
1422                         }
1423
1424                         results += "b = strconv.AppendInt(b, int64(h), 10)\n"
1425
1426                 // minute
1427                 case 'm':
1428
1429                         if inConstantText {
1430                                 inConstantText = false
1431                                 results += "b = append(b, " + fmt.Sprintf("%#v", []byte(format[start:i])) + "...)\n"
1432                         }
1433
1434                         // peek
1435                         // two digit minute required?
1436                         if len(format) != i+1 && format[i+1] == 'm' {
1437                                 i++
1438                                 results += `
1439
1440                                         if t.Minute() < 10 {
1441                                                 b = append(b, '0')
1442                                         }
1443
1444                                 `
1445                         }
1446
1447                         results += "b = strconv.AppendInt(b, int64(t.Minute()), 10)\n"
1448
1449                 // second
1450                 case 's':
1451
1452                         if inConstantText {
1453                                 inConstantText = false
1454                                 results += "b = append(b, " + fmt.Sprintf("%#v", []byte(format[start:i])) + "...)\n"
1455                         }
1456
1457                         // peek
1458                         // two digit minute required?
1459                         if len(format) != i+1 && format[i+1] == 's' {
1460                                 i++
1461                                 results += `
1462
1463                                         if t.Second() < 10 {
1464                                                 b = append(b, '0')
1465                                         }
1466
1467                                 `
1468                         }
1469
1470                         results += "b = strconv.AppendInt(b, int64(t.Second()), 10)\n"
1471
1472                 // day period
1473                 case 'a':
1474
1475                         if inConstantText {
1476                                 inConstantText = false
1477                                 results += "b = append(b, " + fmt.Sprintf("%#v", []byte(format[start:i])) + "...)\n"
1478                         }
1479
1480                         // only used with 'h', patterns should not contains 'a' without 'h' so not checking
1481
1482                         // choosing to use abbreviated, didn't see any rules about which should be used with which
1483                         // date format....
1484
1485                         results += `
1486
1487                                 if t.Hour() < 12 {
1488                                         b = append(b, ` + baseLocale + `.periodsAbbreviated[0]...)
1489                                 } else {
1490                                         b = append(b, ` + baseLocale + `.periodsAbbreviated[1]...)
1491                                 }
1492
1493                         `
1494
1495                 // timezone
1496                 case 'z', 'v':
1497
1498                         if inConstantText {
1499                                 inConstantText = false
1500                                 results += "b = append(b, " + fmt.Sprintf("%#v", []byte(format[start:i])) + "...)\n"
1501                         }
1502
1503                         // consume multiple, only handling Abbrev tz from time.Time for the moment...
1504
1505                         var count int
1506
1507                         if format[i] == 'z' {
1508                                 for j := i; j < len(format); j++ {
1509                                         if format[j] == 'z' {
1510                                                 count++
1511                                         } else {
1512                                                 break
1513                                         }
1514                                 }
1515                         }
1516
1517                         if format[i] == 'v' {
1518                                 for j := i; j < len(format); j++ {
1519                                         if format[j] == 'v' {
1520                                                 count++
1521                                         } else {
1522                                                 break
1523                                         }
1524                                 }
1525                         }
1526
1527                         i += count - 1
1528
1529                         // using the timezone on the Go time object, eg. EST, EDT, MST.....
1530
1531                         if count < 4 {
1532
1533                                 results += `
1534
1535                                         tz, _ := t.Zone()
1536                                         b = append(b, tz...)
1537
1538                                 `
1539                         } else {
1540
1541                                 results += `
1542                                         tz, _ := t.Zone()
1543
1544                                         if btz, ok := ` + baseLocale + `.timezones[tz]; ok {
1545                                                 b = append(b, btz...)
1546                                         } else {
1547                                                 b = append(b, tz...)
1548                                         }
1549
1550                                 `
1551                         }
1552
1553                 // day
1554                 case 'd':
1555
1556                         if inConstantText {
1557                                 inConstantText = false
1558                                 results += "b = append(b, " + fmt.Sprintf("%#v", []byte(format[start:i])) + "...)\n"
1559                         }
1560
1561                         // peek
1562                         // two digit day required?
1563                         if len(format) != i+1 && format[i+1] == 'd' {
1564                                 i++
1565                                 results += `
1566
1567                                         if t.Day() < 10 {
1568                                                 b = append(b, '0')
1569                                         }
1570
1571                                 `
1572                         }
1573
1574                         results += "b = strconv.AppendInt(b, int64(t.Day()), 10)\n"
1575
1576                 // month
1577                 case 'M':
1578
1579                         if inConstantText {
1580                                 inConstantText = false
1581                                 results += "b = append(b, " + fmt.Sprintf("%#v", []byte(format[start:i])) + "...)\n"
1582                         }
1583
1584                         var count int
1585
1586                         // count # of M's
1587                         for j := i; j < len(format); j++ {
1588                                 if format[j] == 'M' {
1589                                         count++
1590                                 } else {
1591                                         break
1592                                 }
1593                         }
1594
1595                         switch count {
1596
1597                         // Numeric form, at least 1 digit
1598                         case 1:
1599
1600                                 results += "b = strconv.AppendInt(b, int64(t.Month()), 10)\n"
1601
1602                         // Number form, at least 2 digits (padding with 0)
1603                         case 2:
1604
1605                                 results += `
1606
1607                                 if t.Month() < 10 {
1608                                         b = append(b, '0')
1609                                 }
1610                                         
1611                                 b = strconv.AppendInt(b, int64(t.Month()), 10)
1612
1613                                 `
1614
1615                         // Abbreviated form
1616                         case 3:
1617
1618                                 results += "b = append(b, " + baseLocale + ".monthsAbbreviated[t.Month()]...)\n"
1619
1620                         // Full/Wide form
1621                         case 4:
1622
1623                                 results += "b = append(b, " + baseLocale + ".monthsWide[t.Month()]...)\n"
1624
1625                         // Narrow form - only used in where context makes it clear, such as headers in a calendar.
1626                         // Should be one character wherever possible.
1627                         case 5:
1628
1629                                 results += "b = append(b, " + baseLocale + ".monthsNarrow[t.Month()]...)\n"
1630                         }
1631
1632                         // skip over M's
1633                         i += count - 1
1634
1635                 // year
1636                 case 'y':
1637
1638                         if inConstantText {
1639                                 inConstantText = false
1640                                 results += "b = append(b, " + fmt.Sprintf("%#v", []byte(format[start:i])) + "...)\n"
1641                         }
1642
1643                         // peek
1644                         // two digit year
1645                         if len(format) != i+1 && format[i+1] == 'y' {
1646                                 i++
1647                                 results += `
1648
1649                                         if t.Year() > 9 {
1650                                                 b = append(b, strconv.Itoa(t.Year())[2:]...)
1651                                         } else {
1652                                                 b = append(b, strconv.Itoa(t.Year())[1:]...)
1653                                         }
1654
1655                                 `
1656                         } else {
1657                                 // four digit year
1658                                 results += `
1659
1660                                         if t.Year() > 0 {
1661                                                 b = strconv.AppendInt(b, int64(t.Year()), 10)
1662                                         } else {
1663                                                 b = strconv.AppendInt(b, int64(-t.Year()), 10)
1664                                         }
1665
1666                                 `
1667                         }
1668
1669                 // weekday
1670                 // I know I only see 'EEEE' in the xml, but just in case handled all posibilities
1671                 case 'E':
1672
1673                         if inConstantText {
1674                                 inConstantText = false
1675                                 results += "b = append(b, " + fmt.Sprintf("%#v", []byte(format[start:i])) + "...)\n"
1676                         }
1677
1678                         var count int
1679
1680                         // count # of E's
1681                         for j := i; j < len(format); j++ {
1682                                 if format[j] == 'E' {
1683                                         count++
1684                                 } else {
1685                                         break
1686                                 }
1687                         }
1688
1689                         switch count {
1690
1691                         // Narrow
1692                         case 1:
1693
1694                                 results += "b = append(b, " + baseLocale + ".daysNarrow[t.Weekday()]...)\n"
1695
1696                         // Short
1697                         case 2:
1698
1699                                 results += "b = append(b, " + baseLocale + ".daysShort[t.Weekday()]...)\n"
1700
1701                         // Abbreviated
1702                         case 3:
1703
1704                                 results += "b = append(b, " + baseLocale + ".daysAbbreviated[t.Weekday()]...)\n"
1705
1706                         // Full/Wide
1707                         case 4:
1708
1709                                 results += "b = append(b, " + baseLocale + ".daysWide[t.Weekday()]...)\n"
1710                         }
1711
1712                         // skip over E's
1713                         i += count - 1
1714
1715                 // era eg. AD, BC
1716                 case 'G':
1717
1718                         if inConstantText {
1719                                 inConstantText = false
1720                                 results += "b = append(b, " + fmt.Sprintf("%#v", []byte(format[start:i])) + "...)\n"
1721                         }
1722
1723                         switch eraScore {
1724                         case 0:
1725                                 results += `
1726
1727                                 if t.Year() < 0 {
1728                                         b = append(b, ` + baseLocale + `.erasWide[0]...)
1729                                 } else {
1730                                         b = append(b, ` + baseLocale + `.erasWide[1]...)
1731                                 }
1732
1733                         `
1734                         case 1, 2:
1735                                 results += `
1736
1737                                 if t.Year() < 0 {
1738                                         b = append(b, ` + baseLocale + `.erasAbbreviated[0]...)
1739                                 } else {
1740                                         b = append(b, ` + baseLocale + `.erasAbbreviated[1]...)
1741                                 }
1742
1743                         `
1744                         }
1745
1746                 default:
1747                         // append all non matched text as they are constants
1748                         if !inConstantText {
1749                                 inConstantText = true
1750                                 start = i
1751                         }
1752                 }
1753         }
1754
1755         // if we were inConstantText when the string ended, add what's left.
1756         if inConstantText {
1757                 // inContantText = false
1758                 results += "b = append(b, " + fmt.Sprintf("%#v", []byte(format[start:])) + "...)\n"
1759         }
1760
1761         return
1762 }
1763
1764 func parseCurrencyNumberFormat(trans *translator) {
1765
1766         if len(trans.CurrencyNumberFormat) == 0 {
1767                 return
1768         }
1769
1770         trans.FmtCurrencyExists = true
1771         negativeEqual := trans.CurrencyNumberFormat == trans.NegativeCurrencyNumberFormat
1772
1773         match := groupLenRegex.FindString(trans.CurrencyNumberFormat)
1774         if len(match) > 0 {
1775                 trans.FmtCurrencyGroupLen = len(match) - 2
1776         }
1777
1778         match = requiredDecimalRegex.FindString(trans.CurrencyNumberFormat)
1779         if len(match) > 0 {
1780                 trans.FmtCurrencyMinDecimalLen = len(match) - 1
1781         }
1782
1783         match = secondaryGroupLenRegex.FindString(trans.CurrencyNumberFormat)
1784         if len(match) > 0 {
1785                 trans.FmtCurrencySecondaryGroupLen = len(match) - 2
1786         }
1787
1788         idx := 0
1789
1790         for idx = 0; idx < len(trans.CurrencyNumberFormat); idx++ {
1791                 if trans.CurrencyNumberFormat[idx] == '#' || trans.CurrencyNumberFormat[idx] == '0' {
1792                         trans.FmtCurrencyPrefix = trans.CurrencyNumberFormat[:idx]
1793                         break
1794                 }
1795         }
1796
1797         for idx = len(trans.CurrencyNumberFormat) - 1; idx >= 0; idx-- {
1798                 if trans.CurrencyNumberFormat[idx] == '#' || trans.CurrencyNumberFormat[idx] == '0' {
1799                         idx++
1800                         trans.FmtCurrencySuffix = trans.CurrencyNumberFormat[idx:]
1801                         break
1802                 }
1803         }
1804
1805         for idx = 0; idx < len(trans.FmtCurrencyPrefix); idx++ {
1806                 if trans.FmtCurrencyPrefix[idx] == '¤' {
1807
1808                         trans.FmtCurrencyInPrefix = true
1809                         trans.FmtCurrencyPrefix = strings.Replace(trans.FmtCurrencyPrefix, string(trans.FmtCurrencyPrefix[idx]), "", 1)
1810
1811                         if idx == 0 {
1812                                 trans.FmtCurrencyLeft = true
1813                         } else {
1814                                 trans.FmtCurrencyLeft = false
1815                         }
1816
1817                         break
1818                 }
1819         }
1820
1821         for idx = 0; idx < len(trans.FmtCurrencySuffix); idx++ {
1822                 if trans.FmtCurrencySuffix[idx] == '¤' {
1823
1824                         trans.FmtCurrencyInPrefix = false
1825                         trans.FmtCurrencySuffix = strings.Replace(trans.FmtCurrencySuffix, string(trans.FmtCurrencySuffix[idx]), "", 1)
1826
1827                         if idx == 0 {
1828                                 trans.FmtCurrencyLeft = true
1829                         } else {
1830                                 trans.FmtCurrencyLeft = false
1831                         }
1832
1833                         break
1834                 }
1835         }
1836
1837         // if len(trans.FmtCurrencyPrefix) > 0 {
1838         //      trans.FmtCurrencyPrefix = fmt.Sprintf("%#v", []byte(trans.FmtCurrencyPrefix))
1839         // }
1840
1841         // if len(trans.FmtCurrencySuffix) > 0 {
1842         //      trans.FmtCurrencySuffix = fmt.Sprintf("%#v", []byte(trans.FmtCurrencySuffix))
1843         // }
1844
1845         // no need to parse again if true....
1846         if negativeEqual {
1847
1848                 trans.FmtCurrencyNegativePrefix = trans.FmtCurrencyPrefix
1849                 trans.FmtCurrencyNegativeSuffix = trans.FmtCurrencySuffix
1850                 trans.FmtCurrencyNegativeInPrefix = trans.FmtCurrencyInPrefix
1851                 trans.FmtCurrencyNegativeLeft = trans.FmtCurrencyLeft
1852
1853                 return
1854         }
1855
1856         trans.FmtCurrencyNegativeExists = true
1857
1858         for idx = 0; idx < len(trans.NegativeCurrencyNumberFormat); idx++ {
1859                 if trans.NegativeCurrencyNumberFormat[idx] == '#' || trans.NegativeCurrencyNumberFormat[idx] == '0' {
1860
1861                         trans.FmtCurrencyNegativePrefix = trans.NegativeCurrencyNumberFormat[:idx]
1862                         break
1863                 }
1864         }
1865
1866         for idx = len(trans.NegativeCurrencyNumberFormat) - 1; idx >= 0; idx-- {
1867                 if trans.NegativeCurrencyNumberFormat[idx] == '#' || trans.NegativeCurrencyNumberFormat[idx] == '0' {
1868                         idx++
1869                         trans.FmtCurrencyNegativeSuffix = trans.NegativeCurrencyNumberFormat[idx:]
1870                         break
1871                 }
1872         }
1873
1874         for idx = 0; idx < len(trans.FmtCurrencyNegativePrefix); idx++ {
1875                 if trans.FmtCurrencyNegativePrefix[idx] == '¤' {
1876
1877                         trans.FmtCurrencyNegativeInPrefix = true
1878                         trans.FmtCurrencyNegativePrefix = strings.Replace(trans.FmtCurrencyNegativePrefix, string(trans.FmtCurrencyNegativePrefix[idx]), "", 1)
1879
1880                         if idx == 0 {
1881                                 trans.FmtCurrencyNegativeLeft = true
1882                         } else {
1883                                 trans.FmtCurrencyNegativeLeft = false
1884                         }
1885
1886                         break
1887                 }
1888         }
1889
1890         for idx = 0; idx < len(trans.FmtCurrencyNegativeSuffix); idx++ {
1891                 if trans.FmtCurrencyNegativeSuffix[idx] == '¤' {
1892
1893                         trans.FmtCurrencyNegativeInPrefix = false
1894                         trans.FmtCurrencyNegativeSuffix = strings.Replace(trans.FmtCurrencyNegativeSuffix, string(trans.FmtCurrencyNegativeSuffix[idx]), "", 1)
1895
1896                         if idx == 0 {
1897                                 trans.FmtCurrencyNegativeLeft = true
1898                         } else {
1899                                 trans.FmtCurrencyNegativeLeft = false
1900                         }
1901
1902                         break
1903                 }
1904         }
1905
1906         // if len(trans.FmtCurrencyNegativePrefix) > 0 {
1907         //      trans.FmtCurrencyNegativePrefix = fmt.Sprintf("%#v", []byte(trans.FmtCurrencyNegativePrefix))
1908         // }
1909
1910         // if len(trans.FmtCurrencyNegativeSuffix) > 0 {
1911         //      trans.FmtCurrencyNegativeSuffix = fmt.Sprintf("%#v", []byte(trans.FmtCurrencyNegativeSuffix))
1912         // }
1913
1914         return
1915 }
1916
1917 func parsePercentNumberFormat(trans *translator) {
1918
1919         if len(trans.PercentNumberFormat) == 0 {
1920                 return
1921         }
1922
1923         trans.FmtPercentExists = true
1924
1925         match := groupLenPercentRegex.FindString(trans.PercentNumberFormat)
1926         if len(match) > 0 {
1927                 trans.FmtPercentGroupLen = len(match) - 1
1928         }
1929
1930         match = requiredDecimalRegex.FindString(trans.PercentNumberFormat)
1931         if len(match) > 0 {
1932                 trans.FmtPercentMinDecimalLen = len(match) - 1
1933         }
1934
1935         match = secondaryGroupLenRegex.FindString(trans.PercentNumberFormat)
1936         if len(match) > 0 {
1937                 trans.FmtPercentSecondaryGroupLen = len(match) - 2
1938         }
1939
1940         idx := 0
1941
1942         for idx = 0; idx < len(trans.PercentNumberFormat); idx++ {
1943                 if trans.PercentNumberFormat[idx] == '#' || trans.PercentNumberFormat[idx] == '0' {
1944                         trans.FmtPercentPrefix = trans.PercentNumberFormat[:idx]
1945                         break
1946                 }
1947         }
1948
1949         for idx = len(trans.PercentNumberFormat) - 1; idx >= 0; idx-- {
1950                 if trans.PercentNumberFormat[idx] == '#' || trans.PercentNumberFormat[idx] == '0' {
1951                         idx++
1952                         trans.FmtPercentSuffix = trans.PercentNumberFormat[idx:]
1953                         break
1954                 }
1955         }
1956
1957         for idx = 0; idx < len(trans.FmtPercentPrefix); idx++ {
1958                 if trans.FmtPercentPrefix[idx] == '%' {
1959
1960                         trans.FmtPercentInPrefix = true
1961                         trans.FmtPercentPrefix = strings.Replace(trans.FmtPercentPrefix, string(trans.FmtPercentPrefix[idx]), "", 1)
1962
1963                         if idx == 0 {
1964                                 trans.FmtPercentLeft = true
1965                         } else {
1966                                 trans.FmtPercentLeft = false
1967                         }
1968
1969                         break
1970                 }
1971         }
1972
1973         for idx = 0; idx < len(trans.FmtPercentSuffix); idx++ {
1974                 if trans.FmtPercentSuffix[idx] == '%' {
1975
1976                         trans.FmtPercentInPrefix = false
1977                         trans.FmtPercentSuffix = strings.Replace(trans.FmtPercentSuffix, string(trans.FmtPercentSuffix[idx]), "", 1)
1978
1979                         if idx == 0 {
1980                                 trans.FmtPercentLeft = true
1981                         } else {
1982                                 trans.FmtPercentLeft = false
1983                         }
1984
1985                         break
1986                 }
1987         }
1988
1989         // if len(trans.FmtPercentPrefix) > 0 {
1990         //      trans.FmtPercentPrefix = fmt.Sprintf("%#v", []byte(trans.FmtPercentPrefix))
1991         // }
1992
1993         // if len(trans.FmtPercentSuffix) > 0 {
1994         //      trans.FmtPercentSuffix = fmt.Sprintf("%#v", []byte(trans.FmtPercentSuffix))
1995         // }
1996
1997         return
1998 }
1999
2000 func parseDecimalNumberFormat(trans *translator) {
2001
2002         if len(trans.DecimalNumberFormat) == 0 {
2003                 return
2004         }
2005
2006         trans.FmtNumberExists = true
2007
2008         formats := strings.SplitN(trans.DecimalNumberFormat, ";", 2)
2009
2010         match := groupLenRegex.FindString(formats[0])
2011         if len(match) > 0 {
2012                 trans.FmtNumberGroupLen = len(match) - 2
2013         }
2014
2015         match = requiredDecimalRegex.FindString(formats[0])
2016         if len(match) > 0 {
2017                 trans.FmtNumberMinDecimalLen = len(match) - 1
2018         }
2019
2020         match = secondaryGroupLenRegex.FindString(formats[0])
2021         if len(match) > 0 {
2022                 trans.FmtNumberSecondaryGroupLen = len(match) - 2
2023         }
2024
2025         return
2026 }
2027
2028 type sortRank struct {
2029         Rank  uint8
2030         Value string
2031 }
2032
2033 type ByRank []sortRank
2034
2035 func (a ByRank) Len() int           { return len(a) }
2036 func (a ByRank) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
2037 func (a ByRank) Less(i, j int) bool { return a[i].Rank < a[j].Rank }
2038
2039 type ByPluralRule []locales.PluralRule
2040
2041 func (a ByPluralRule) Len() int           { return len(a) }
2042 func (a ByPluralRule) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
2043 func (a ByPluralRule) Less(i, j int) bool { return a[i] < a[j] }
2044
2045 // TODO: refine generated code a bit, some combinations end up with same plural rule,
2046 // could check all at once; but it works and that's step 1 complete
2047 func parseRangePluralRuleFunc(current *cldr.CLDR, baseLocale string) (results string, plurals string) {
2048
2049         var pluralRange *struct {
2050                 cldr.Common
2051                 Locales     string `xml:"locales,attr"`
2052                 PluralRange []*struct {
2053                         cldr.Common
2054                         Start  string `xml:"start,attr"`
2055                         End    string `xml:"end,attr"`
2056                         Result string `xml:"result,attr"`
2057                 } `xml:"pluralRange"`
2058         }
2059
2060         var pluralArr []locales.PluralRule
2061
2062         for _, pr := range current.Supplemental().Plurals[1].PluralRanges {
2063
2064                 locs := strings.Split(pr.Locales, " ")
2065
2066                 for _, loc := range locs {
2067
2068                         if loc == baseLocale {
2069                                 pluralRange = pr
2070                         }
2071                 }
2072         }
2073
2074         // no range plural rules for locale
2075         if pluralRange == nil {
2076                 plurals = "nil"
2077                 results = "return locales.PluralRuleUnknown"
2078                 return
2079         }
2080
2081         mp := make(map[string]struct{})
2082
2083         // pre-process if all the same
2084         for _, rule := range pluralRange.PluralRange {
2085                 mp[rule.Result] = struct{}{}
2086         }
2087
2088         for k := range mp {
2089                 psI := pluralStringToInt(k)
2090                 pluralArr = append(pluralArr, psI)
2091         }
2092
2093         if len(mp) == 1 {
2094                 results += "return locales." + pluralStringToString(pluralRange.PluralRange[0].Result)
2095                 plurals = fmt.Sprintf("%#v", pluralArr)
2096                 return
2097         }
2098
2099         multiple := len(pluralRange.PluralRange) > 1
2100
2101         if multiple {
2102                 results += "start := " + baseLocale + ".CardinalPluralRule(num1, v1)\n"
2103                 results += "end := " + baseLocale + ".CardinalPluralRule(num2, v2)\n\n"
2104         }
2105
2106         first := true
2107
2108         // pre parse for variables
2109         for i, rule := range pluralRange.PluralRange {
2110
2111                 if i == len(pluralRange.PluralRange)-1 {
2112
2113                         if multiple {
2114                                 results += "\n\n"
2115                         }
2116                         results += "return locales." + pluralStringToString(rule.Result)
2117                         continue
2118                 }
2119
2120                 if first {
2121                         results += "if"
2122                         first = false
2123                 } else {
2124                         results += "else if"
2125                 }
2126
2127                 results += " start == locales." + pluralStringToString(rule.Start) + " && end == locales." + pluralStringToString(rule.End) + " {\n return locales." + pluralStringToString(rule.Result) + "\n} "
2128
2129         }
2130
2131         if multiple {
2132                 results = "\n" + results + "\n"
2133         }
2134
2135         if len(pluralArr) == 0 {
2136                 plurals = "nil"
2137         } else {
2138
2139                 ints := make([]int, len(pluralArr))
2140                 for i := 0; i < len(pluralArr); i++ {
2141                         ints[i] = int(pluralArr[i])
2142                 }
2143
2144                 sort.Ints(ints)
2145
2146                 for i := 0; i < len(ints); i++ {
2147                         pluralArr[i] = locales.PluralRule(ints[i])
2148                 }
2149
2150                 plurals = fmt.Sprintf("%#v", pluralArr)
2151         }
2152
2153         return
2154
2155 }
2156
2157 // TODO: cleanup function logic perhaps write a lexer... but it's working right now, and
2158 // I'm already farther down the rabbit hole than I'd like and so pulling the chute here.
2159 func parseOrdinalPluralRuleFunc(current *cldr.CLDR, baseLocale string) (results string, plurals string) {
2160
2161         var prOrdinal *struct {
2162                 cldr.Common
2163                 Locales    string "xml:\"locales,attr\""
2164                 PluralRule []*struct {
2165                         cldr.Common
2166                         Count string "xml:\"count,attr\""
2167                 } "xml:\"pluralRule\""
2168         }
2169
2170         var pluralArr []locales.PluralRule
2171
2172         // idx 0 is ordinal rules
2173         for _, pr := range current.Supplemental().Plurals[0].PluralRules {
2174
2175                 locs := strings.Split(pr.Locales, " ")
2176
2177                 for _, loc := range locs {
2178
2179                         if loc == baseLocale {
2180
2181                                 prOrdinal = pr
2182                                 // for _, pl := range pr.PluralRule {
2183                                 //      fmt.Println(pl.Count, pl.Common.Data())
2184                                 // }
2185                         }
2186                 }
2187         }
2188
2189         // no plural rules for locale
2190         if prOrdinal == nil {
2191                 plurals = "nil"
2192                 results = "return locales.PluralRuleUnknown"
2193                 return
2194         }
2195
2196         vals := make(map[string]struct{})
2197         first := true
2198
2199         // pre parse for variables
2200         for _, rule := range prOrdinal.PluralRule {
2201
2202                 ps1 := pluralStringToString(rule.Count)
2203                 psI := pluralStringToInt(rule.Count)
2204                 pluralArr = append(pluralArr, psI)
2205
2206                 data := strings.Replace(strings.Replace(strings.Replace(strings.TrimSpace(strings.SplitN(rule.Common.Data(), "@", 2)[0]), " = ", " == ", -1), " or ", " || ", -1), " and ", " && ", -1)
2207
2208                 if len(data) == 0 {
2209                         if len(prOrdinal.PluralRule) == 1 {
2210
2211                                 results = "return locales." + ps1
2212
2213                         } else {
2214
2215                                 results += "\n\nreturn locales." + ps1
2216                                 // results += "else {\nreturn locales." + locales.PluralStringToString(rule.Count) + ", nil\n}"
2217                         }
2218
2219                         continue
2220                 }
2221
2222                 // // All need n, so always add
2223                 // if strings.Contains(data, "n") {
2224                 //      vals[prVarFuncs["n"]] = struct{}{}
2225                 // }
2226
2227                 if strings.Contains(data, "i") {
2228                         vals[prVarFuncs["i"]] = struct{}{}
2229                 }
2230
2231                 // v is inherently avaialable as an argument
2232                 // if strings.Contains(data, "v") {
2233                 //      vals[prVarFuncs["v"]] = struct{}{}
2234                 // }
2235
2236                 if strings.Contains(data, "w") {
2237                         vals[prVarFuncs["w"]] = struct{}{}
2238                 }
2239
2240                 if strings.Contains(data, "f") {
2241                         vals[prVarFuncs["f"]] = struct{}{}
2242                 }
2243
2244                 if strings.Contains(data, "t") {
2245                         vals[prVarFuncs["t"]] = struct{}{}
2246                 }
2247
2248                 if first {
2249                         results += "if "
2250                         first = false
2251                 } else {
2252                         results += "else if "
2253                 }
2254
2255                 stmt := ""
2256
2257                 // real work here
2258                 //
2259                 // split by 'or' then by 'and' allowing to better
2260                 // determine bracketing for formula
2261
2262                 ors := strings.Split(data, "||")
2263
2264                 for _, or := range ors {
2265
2266                         stmt += "("
2267
2268                         ands := strings.Split(strings.TrimSpace(or), "&&")
2269
2270                         for _, and := range ands {
2271
2272                                 inArg := false
2273                                 pre := ""
2274                                 lft := ""
2275                                 preOperator := ""
2276                                 args := strings.Split(strings.TrimSpace(and), " ")
2277
2278                                 for _, a := range args {
2279
2280                                         if inArg {
2281                                                 // check to see if is a value range 2..9
2282
2283                                                 multiRange := strings.Count(a, "..") > 1
2284                                                 cargs := strings.Split(strings.TrimSpace(a), ",")
2285                                                 hasBracket := len(cargs) > 1
2286                                                 bracketAdded := false
2287                                                 lastWasRange := false
2288
2289                                                 for _, carg := range cargs {
2290
2291                                                         if rng := strings.Split(carg, ".."); len(rng) > 1 {
2292
2293                                                                 if multiRange {
2294                                                                         pre += " ("
2295                                                                 } else {
2296                                                                         pre += " "
2297                                                                 }
2298
2299                                                                 switch preOperator {
2300                                                                 case "==":
2301                                                                         pre += lft + " >= " + rng[0] + " && " + lft + "<=" + rng[1]
2302                                                                 case "!=":
2303                                                                         pre += "(" + lft + " < " + rng[0] + " || " + lft + " > " + rng[1] + ")"
2304                                                                 }
2305
2306                                                                 if multiRange {
2307                                                                         pre += ") || "
2308                                                                 } else {
2309                                                                         pre += " || "
2310                                                                 }
2311
2312                                                                 lastWasRange = true
2313                                                                 continue
2314                                                         }
2315
2316                                                         if lastWasRange {
2317                                                                 pre = strings.TrimRight(pre, " || ") + " && "
2318                                                         }
2319
2320                                                         lastWasRange = false
2321
2322                                                         if hasBracket && !bracketAdded {
2323                                                                 pre += "("
2324                                                                 bracketAdded = true
2325                                                         }
2326
2327                                                         // single comma separated values
2328                                                         switch preOperator {
2329                                                         case "==":
2330                                                                 pre += " " + lft + preOperator + carg + " || "
2331                                                         case "!=":
2332                                                                 pre += " " + lft + preOperator + carg + " && "
2333                                                         }
2334
2335                                                 }
2336
2337                                                 pre = strings.TrimRight(pre, " || ")
2338                                                 pre = strings.TrimRight(pre, " && ")
2339                                                 pre = strings.TrimRight(pre, " || ")
2340
2341                                                 if hasBracket && bracketAdded {
2342                                                         pre += ")"
2343                                                 }
2344
2345                                                 continue
2346                                         }
2347
2348                                         if strings.Contains(a, "=") || a == ">" || a == "<" {
2349                                                 inArg = true
2350                                                 preOperator = a
2351                                                 continue
2352                                         }
2353
2354                                         lft += a
2355                                 }
2356
2357                                 stmt += pre + " && "
2358                         }
2359
2360                         stmt = strings.TrimRight(stmt, " && ") + ") || "
2361                 }
2362
2363                 stmt = strings.TrimRight(stmt, " || ")
2364
2365                 results += stmt
2366
2367                 results += " {\n"
2368
2369                 // return plural rule here
2370                 results += "return locales." + ps1 + "\n"
2371
2372                 results += "}"
2373         }
2374
2375         pre := "\n"
2376
2377         // always needed
2378         vals[prVarFuncs["n"]] = struct{}{}
2379
2380         sorted := make([]sortRank, 0, len(vals))
2381
2382         for k := range vals {
2383                 switch k[:1] {
2384                 case "n":
2385                         sorted = append(sorted, sortRank{
2386                                 Value: prVarFuncs["n"],
2387                                 Rank:  1,
2388                         })
2389                 case "i":
2390                         sorted = append(sorted, sortRank{
2391                                 Value: prVarFuncs["i"],
2392                                 Rank:  2,
2393                         })
2394                 case "w":
2395                         sorted = append(sorted, sortRank{
2396                                 Value: prVarFuncs["w"],
2397                                 Rank:  3,
2398                         })
2399                 case "f":
2400                         sorted = append(sorted, sortRank{
2401                                 Value: prVarFuncs["f"],
2402                                 Rank:  4,
2403                         })
2404                 case "t":
2405                         sorted = append(sorted, sortRank{
2406                                 Value: prVarFuncs["t"],
2407                                 Rank:  5,
2408                         })
2409                 }
2410         }
2411
2412         sort.Sort(ByRank(sorted))
2413
2414         for _, k := range sorted {
2415                 pre += k.Value
2416         }
2417
2418         if len(results) == 0 {
2419                 results = "return locales.PluralRuleUnknown"
2420         } else {
2421
2422                 if !strings.HasPrefix(results, "return") {
2423
2424                         results = manyToSingleVars(results)
2425                         // pre += "\n"
2426                         results = pre + results
2427                 }
2428         }
2429
2430         if len(pluralArr) == 0 {
2431                 plurals = "nil"
2432         } else {
2433                 plurals = fmt.Sprintf("%#v", pluralArr)
2434         }
2435
2436         return
2437 }
2438
2439 // TODO: cleanup function logic perhaps write a lexer... but it's working right now, and
2440 // I'm already farther down the rabbit hole than I'd like and so pulling the chute here.
2441 //
2442 // updated to also accept actual locale as 'pt_PT' exists in cardinal rules different from 'pt'
2443 func parseCardinalPluralRuleFunc(current *cldr.CLDR, locale, baseLocale string) (results string, plurals string) {
2444
2445         var prCardinal *struct {
2446                 cldr.Common
2447                 Locales    string "xml:\"locales,attr\""
2448                 PluralRule []*struct {
2449                         cldr.Common
2450                         Count string "xml:\"count,attr\""
2451                 } "xml:\"pluralRule\""
2452         }
2453
2454         var pluralArr []locales.PluralRule
2455         var inBaseLocale bool
2456         l := locale
2457
2458 FIND:
2459         // idx 2 is cardinal rules
2460         for _, pr := range current.Supplemental().Plurals[2].PluralRules {
2461
2462                 locs := strings.Split(pr.Locales, " ")
2463
2464                 for _, loc := range locs {
2465
2466                         if loc == l {
2467                                 prCardinal = pr
2468                         }
2469                 }
2470         }
2471
2472         // no plural rules for locale
2473         if prCardinal == nil {
2474
2475                 if !inBaseLocale {
2476                         inBaseLocale = true
2477                         l = baseLocale
2478                         goto FIND
2479                 }
2480
2481                 plurals = "nil"
2482                 results = "return locales.PluralRuleUnknown"
2483                 return
2484         }
2485
2486         vals := make(map[string]struct{})
2487         first := true
2488
2489         // pre parse for variables
2490         for _, rule := range prCardinal.PluralRule {
2491
2492                 ps1 := pluralStringToString(rule.Count)
2493                 psI := pluralStringToInt(rule.Count)
2494                 pluralArr = append(pluralArr, psI)
2495
2496                 data := strings.Replace(strings.Replace(strings.Replace(strings.TrimSpace(strings.SplitN(rule.Common.Data(), "@", 2)[0]), " = ", " == ", -1), " or ", " || ", -1), " and ", " && ", -1)
2497
2498                 if len(data) == 0 {
2499                         if len(prCardinal.PluralRule) == 1 {
2500
2501                                 results = "return locales." + ps1
2502
2503                         } else {
2504
2505                                 results += "\n\nreturn locales." + ps1
2506                                 // results += "else {\nreturn locales." + locales.PluralStringToString(rule.Count) + ", nil\n}"
2507                         }
2508
2509                         continue
2510                 }
2511
2512                 // // All need n, so always add
2513                 // if strings.Contains(data, "n") {
2514                 //      vals[prVarFuncs["n"]] = struct{}{}
2515                 // }
2516
2517                 if strings.Contains(data, "i") {
2518                         vals[prVarFuncs["i"]] = struct{}{}
2519                 }
2520
2521                 // v is inherently avaialable as an argument
2522                 // if strings.Contains(data, "v") {
2523                 //      vals[prVarFuncs["v"]] = struct{}{}
2524                 // }
2525
2526                 if strings.Contains(data, "w") {
2527                         vals[prVarFuncs["w"]] = struct{}{}
2528                 }
2529
2530                 if strings.Contains(data, "f") {
2531                         vals[prVarFuncs["f"]] = struct{}{}
2532                 }
2533
2534                 if strings.Contains(data, "t") {
2535                         vals[prVarFuncs["t"]] = struct{}{}
2536                 }
2537
2538                 if first {
2539                         results += "if "
2540                         first = false
2541                 } else {
2542                         results += "else if "
2543                 }
2544
2545                 stmt := ""
2546
2547                 // real work here
2548                 //
2549                 // split by 'or' then by 'and' allowing to better
2550                 // determine bracketing for formula
2551
2552                 ors := strings.Split(data, "||")
2553
2554                 for _, or := range ors {
2555
2556                         stmt += "("
2557
2558                         ands := strings.Split(strings.TrimSpace(or), "&&")
2559
2560                         for _, and := range ands {
2561
2562                                 inArg := false
2563                                 pre := ""
2564                                 lft := ""
2565                                 preOperator := ""
2566                                 args := strings.Split(strings.TrimSpace(and), " ")
2567
2568                                 for _, a := range args {
2569
2570                                         if inArg {
2571                                                 // check to see if is a value range 2..9
2572
2573                                                 multiRange := strings.Count(a, "..") > 1
2574                                                 cargs := strings.Split(strings.TrimSpace(a), ",")
2575                                                 hasBracket := len(cargs) > 1
2576                                                 bracketAdded := false
2577                                                 lastWasRange := false
2578
2579                                                 for _, carg := range cargs {
2580
2581                                                         if rng := strings.Split(carg, ".."); len(rng) > 1 {
2582
2583                                                                 if multiRange {
2584                                                                         pre += " ("
2585                                                                 } else {
2586                                                                         pre += " "
2587                                                                 }
2588
2589                                                                 switch preOperator {
2590                                                                 case "==":
2591                                                                         pre += lft + " >= " + rng[0] + " && " + lft + "<=" + rng[1]
2592                                                                 case "!=":
2593                                                                         pre += "(" + lft + " < " + rng[0] + " || " + lft + " > " + rng[1] + ")"
2594                                                                 }
2595
2596                                                                 if multiRange {
2597                                                                         pre += ") || "
2598                                                                 } else {
2599                                                                         pre += " || "
2600                                                                 }
2601
2602                                                                 lastWasRange = true
2603                                                                 continue
2604                                                         }
2605
2606                                                         if lastWasRange {
2607                                                                 pre = strings.TrimRight(pre, " || ") + " && "
2608                                                         }
2609
2610                                                         lastWasRange = false
2611
2612                                                         if hasBracket && !bracketAdded {
2613                                                                 pre += "("
2614                                                                 bracketAdded = true
2615                                                         }
2616
2617                                                         // single comma separated values
2618                                                         switch preOperator {
2619                                                         case "==":
2620                                                                 pre += " " + lft + preOperator + carg + " || "
2621                                                         case "!=":
2622                                                                 pre += " " + lft + preOperator + carg + " && "
2623                                                         }
2624
2625                                                 }
2626
2627                                                 pre = strings.TrimRight(pre, " || ")
2628                                                 pre = strings.TrimRight(pre, " && ")
2629                                                 pre = strings.TrimRight(pre, " || ")
2630
2631                                                 if hasBracket && bracketAdded {
2632                                                         pre += ")"
2633                                                 }
2634
2635                                                 continue
2636                                         }
2637
2638                                         if strings.Contains(a, "=") || a == ">" || a == "<" {
2639                                                 inArg = true
2640                                                 preOperator = a
2641                                                 continue
2642                                         }
2643
2644                                         lft += a
2645                                 }
2646
2647                                 stmt += pre + " && "
2648                         }
2649
2650                         stmt = strings.TrimRight(stmt, " && ") + ") || "
2651                 }
2652
2653                 stmt = strings.TrimRight(stmt, " || ")
2654
2655                 results += stmt
2656
2657                 results += " {\n"
2658
2659                 // return plural rule here
2660                 results += "return locales." + ps1 + "\n"
2661
2662                 results += "}"
2663         }
2664
2665         pre := "\n"
2666
2667         // always needed
2668         vals[prVarFuncs["n"]] = struct{}{}
2669
2670         sorted := make([]sortRank, 0, len(vals))
2671
2672         for k := range vals {
2673                 switch k[:1] {
2674                 case "n":
2675                         sorted = append(sorted, sortRank{
2676                                 Value: prVarFuncs["n"],
2677                                 Rank:  1,
2678                         })
2679                 case "i":
2680                         sorted = append(sorted, sortRank{
2681                                 Value: prVarFuncs["i"],
2682                                 Rank:  2,
2683                         })
2684                 case "w":
2685                         sorted = append(sorted, sortRank{
2686                                 Value: prVarFuncs["w"],
2687                                 Rank:  3,
2688                         })
2689                 case "f":
2690                         sorted = append(sorted, sortRank{
2691                                 Value: prVarFuncs["f"],
2692                                 Rank:  4,
2693                         })
2694                 case "t":
2695                         sorted = append(sorted, sortRank{
2696                                 Value: prVarFuncs["t"],
2697                                 Rank:  5,
2698                         })
2699                 }
2700         }
2701
2702         sort.Sort(ByRank(sorted))
2703
2704         for _, k := range sorted {
2705                 pre += k.Value
2706         }
2707
2708         if len(results) == 0 {
2709                 results = "return locales.PluralRuleUnknown"
2710         } else {
2711
2712                 if !strings.HasPrefix(results, "return") {
2713
2714                         results = manyToSingleVars(results)
2715                         // pre += "\n"
2716                         results = pre + results
2717                 }
2718         }
2719
2720         if len(pluralArr) == 0 {
2721                 plurals = "nil"
2722         } else {
2723                 plurals = fmt.Sprintf("%#v", pluralArr)
2724         }
2725
2726         return
2727 }
2728
2729 func manyToSingleVars(input string) (results string) {
2730
2731         matches := nModRegex.FindAllString(input, -1)
2732         mp := make(map[string][]string) // map of formula to variable
2733         var found bool
2734         var split []string
2735         var variable string
2736
2737         for _, formula := range matches {
2738
2739                 if _, found = mp[formula]; found {
2740                         continue
2741                 }
2742
2743                 split = strings.SplitN(formula, "%", 2)
2744
2745                 mp[formula] = []string{split[1], "math.Mod(" + split[0] + ", " + split[1] + ")"}
2746         }
2747
2748         for k, v := range mp {
2749                 variable = "nMod" + v[0]
2750                 results += variable + " := " + v[1] + "\n"
2751                 input = strings.Replace(input, k, variable, -1)
2752         }
2753
2754         matches = iModRegex.FindAllString(input, -1)
2755         mp = make(map[string][]string) // map of formula to variable
2756
2757         for _, formula := range matches {
2758
2759                 if _, found = mp[formula]; found {
2760                         continue
2761                 }
2762
2763                 split = strings.SplitN(formula, "%", 2)
2764
2765                 mp[formula] = []string{split[1], formula}
2766         }
2767
2768         for k, v := range mp {
2769                 variable = "iMod" + v[0]
2770                 results += variable + " := " + v[1] + "\n"
2771                 input = strings.Replace(input, k, variable, -1)
2772         }
2773
2774         matches = wModRegex.FindAllString(input, -1)
2775         mp = make(map[string][]string) // map of formula to variable
2776
2777         for _, formula := range matches {
2778
2779                 if _, found = mp[formula]; found {
2780                         continue
2781                 }
2782
2783                 split = strings.SplitN(formula, "%", 2)
2784
2785                 mp[formula] = []string{split[1], formula}
2786         }
2787
2788         for k, v := range mp {
2789                 variable = "wMod" + v[0]
2790                 results += variable + " := " + v[1] + "\n"
2791                 input = strings.Replace(input, k, variable, -1)
2792         }
2793
2794         matches = fModRegex.FindAllString(input, -1)
2795         mp = make(map[string][]string) // map of formula to variable
2796
2797         for _, formula := range matches {
2798
2799                 if _, found = mp[formula]; found {
2800                         continue
2801                 }
2802
2803                 split = strings.SplitN(formula, "%", 2)
2804
2805                 mp[formula] = []string{split[1], formula}
2806         }
2807
2808         for k, v := range mp {
2809                 variable = "fMod" + v[0]
2810                 results += variable + " := " + v[1] + "\n"
2811                 input = strings.Replace(input, k, variable, -1)
2812         }
2813
2814         matches = tModRegex.FindAllString(input, -1)
2815         mp = make(map[string][]string) // map of formula to variable
2816
2817         for _, formula := range matches {
2818
2819                 if _, found = mp[formula]; found {
2820                         continue
2821                 }
2822
2823                 split = strings.SplitN(formula, "%", 2)
2824
2825                 mp[formula] = []string{split[1], formula}
2826         }
2827
2828         for k, v := range mp {
2829                 variable = "tMod" + v[0]
2830                 results += variable + " := " + v[1] + "\n"
2831                 input = strings.Replace(input, k, variable, -1)
2832         }
2833
2834         results = results + "\n" + input
2835
2836         return
2837 }
2838
2839 // pluralStringToInt returns the enum value of 'plural' provided
2840 func pluralStringToInt(plural string) locales.PluralRule {
2841
2842         switch plural {
2843         case "zero":
2844                 return locales.PluralRuleZero
2845         case "one":
2846                 return locales.PluralRuleOne
2847         case "two":
2848                 return locales.PluralRuleTwo
2849         case "few":
2850                 return locales.PluralRuleFew
2851         case "many":
2852                 return locales.PluralRuleMany
2853         case "other":
2854                 return locales.PluralRuleOther
2855         default:
2856                 return locales.PluralRuleUnknown
2857         }
2858 }
2859
2860 func pluralStringToString(pr string) string {
2861
2862         pr = strings.TrimSpace(pr)
2863
2864         switch pr {
2865         case "zero":
2866                 return "PluralRuleZero"
2867         case "one":
2868                 return "PluralRuleOne"
2869         case "two":
2870                 return "PluralRuleTwo"
2871         case "few":
2872                 return "PluralRuleFew"
2873         case "many":
2874                 return "PluralRuleMany"
2875         case "other":
2876                 return "PluralRuleOther"
2877         default:
2878                 return "PluralRuleUnknown"
2879         }
2880 }