13 "github.com/go-playground/locales"
15 "golang.org/x/text/unicode/cldr"
22 locFilename = locDir + "/%s.go"
26 tfuncs = template.FuncMap{
27 "is_multibyte": func(s string) bool {
28 return len([]byte(s)) > 1
30 "reverse_bytes": func(s string) string {
31 b := make([]byte, 0, 8)
33 for j := len(s) - 1; j >= 0; j-- {
37 return fmt.Sprintf("%#v", b)
39 "byte_count": func(s ...string) string {
42 for i := 0; i < len(s); i++ {
43 count += len([]byte(s[i]))
46 return strconv.Itoa(count)
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",
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]+)")
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",
80 type translator struct {
83 // InheritedLocale string
101 FmtNumberGroupLen int
102 FmtNumberSecondaryGroupLen int
103 FmtNumberMinDecimalLen int
106 FmtPercentExists bool
107 FmtPercentGroupLen int
108 FmtPercentSecondaryGroupLen int
109 FmtPercentMinDecimalLen int
110 FmtPercentPrefix string
111 FmtPercentSuffix string
112 FmtPercentInPrefix bool
116 FmtCurrencyExists bool
117 FmtCurrencyGroupLen int
118 FmtCurrencySecondaryGroupLen int
119 FmtCurrencyMinDecimalLen int
120 FmtCurrencyPrefix string
121 FmtCurrencySuffix string
122 FmtCurrencyInPrefix bool
124 FmtCurrencyNegativeExists bool
125 FmtCurrencyNegativePrefix string
126 FmtCurrencyNegativeSuffix string
127 FmtCurrencyNegativeInPrefix bool
128 FmtCurrencyNegativeLeft bool
131 FmtCalendarExists bool
133 FmtMonthsAbbreviated string
134 FmtMonthsNarrow string
137 FmtDaysAbbreviated string
142 FmtPeriodsAbbreviated string
143 FmtPeriodsNarrow string
144 FmtPeriodsShort string
145 FmtPeriodsWide string
147 FmtErasAbbreviated string
153 // calculation only fields below this point...
154 DecimalNumberFormat string
155 PercentNumberFormat string
156 CurrencyNumberFormat string
157 NegativeCurrencyNumberFormat string
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.....
175 type zoneAbbrev struct {
180 var timezones = map[string]*zoneAbbrev{} // key = type eg. America_Eastern zone Abbrev eg. EST & EDT
187 tmpl, err = template.New("all").Funcs(tfuncs).ParseGlob("*.tmpl")
192 // load CLDR recourses
193 var decoder cldr.Decoder
195 cldr, err := decoder.DecodePath("data/core")
197 panic("failed decode CLDR data; " + err.Error())
203 var currencies string
205 for i, curr := range globalCurrencies {
208 currencies = curr + " Type = iota\n"
212 currencies += curr + "\n"
215 if err = os.MkdirAll(fmt.Sprintf(locDir, "currency"), 0777); err != nil {
219 filename := fmt.Sprintf(locFilename, "currency", "currency")
221 output, err := os.Create(filename)
227 if err := tmpl.ExecuteTemplate(output, "currencies", currencies); err != nil {
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)
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)
244 for _, trans := range translators {
245 fmt.Println("Writing Data:", trans.Locale)
247 if err = os.MkdirAll(fmt.Sprintf(locDir, trans.Locale), 0777); err != nil {
251 filename = fmt.Sprintf(locFilename, trans.Locale, trans.Locale)
253 output, err := os.Create(filename)
259 if err := tmpl.ExecuteTemplate(output, "translator", trans); err != nil {
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)
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)
277 filename = fmt.Sprintf(locFilename, trans.Locale, trans.Locale+"_test")
279 if _, err := os.Stat(filename); err == nil {
280 fmt.Println("*************** test file exists, skipping:", filename)
284 output, err = os.Create(filename)
290 if err := tmpl.ExecuteTemplate(output, "tests", trans); err != nil {
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)
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)
310 func applyOverrides(trans *translator) {
312 if trans.BaseLocale == "ru" {
313 trans.PercentNumberFormat = "#,##0%"
317 func postProcess(cldr *cldr.CLDR) {
319 for _, v := range timezones {
322 if len(v.daylight) == 0 {
323 v.daylight = v.standard
327 var inherited *translator
329 var inheritedFound, baseFound bool
331 for _, trans := range translators {
333 fmt.Println("Post Processing:", trans.Locale)
335 // cardinal plural rules
336 trans.CardinalFunc, trans.Plurals = parseCardinalPluralRuleFunc(cldr, trans.Locale, trans.BaseLocale)
338 //ordinal plural rules
339 trans.OrdinalFunc, trans.PluralsOrdinal = parseOrdinalPluralRuleFunc(cldr, trans.BaseLocale)
341 // range plural rules
342 trans.RangeFunc, trans.PluralsRange = parseRangePluralRuleFunc(cldr, trans.BaseLocale)
344 // ignore base locales
345 if trans.BaseLocale == trans.Locale {
346 inheritedFound = false
350 inheritedFound = false
352 if trans.BaseLocale == "en" {
353 if inherit, found := enInheritance[trans.Locale]; found {
354 inherited, inheritedFound = translators[inherit]
358 base, baseFound = baseTranslators[trans.BaseLocale]
363 if len(trans.Decimal) == 0 {
366 trans.Decimal = inherited.Decimal
369 if len(trans.Decimal) == 0 && baseFound {
370 trans.Decimal = base.Decimal
373 if len(trans.Decimal) == 0 {
378 if len(trans.Group) == 0 {
381 trans.Group = inherited.Group
384 if len(trans.Group) == 0 && baseFound {
385 trans.Group = base.Group
388 if len(trans.Group) == 0 {
393 if len(trans.Minus) == 0 {
396 trans.Minus = inherited.Minus
399 if len(trans.Minus) == 0 && baseFound {
400 trans.Minus = base.Minus
403 if len(trans.Minus) == 0 {
408 if len(trans.Percent) == 0 {
411 trans.Percent = inherited.Percent
414 if len(trans.Percent) == 0 && baseFound {
415 trans.Percent = base.Percent
418 if len(trans.Percent) == 0 {
423 if len(trans.PerMille) == 0 {
426 trans.PerMille = inherited.PerMille
429 if len(trans.PerMille) == 0 && baseFound {
430 trans.PerMille = base.PerMille
433 if len(trans.PerMille) == 0 {
438 if len(trans.TimeSeparator) == 0 && inheritedFound {
439 trans.TimeSeparator = inherited.TimeSeparator
442 if len(trans.TimeSeparator) == 0 && baseFound {
443 trans.TimeSeparator = base.TimeSeparator
446 if len(trans.Infinity) == 0 && inheritedFound {
447 trans.Infinity = inherited.Infinity
450 if len(trans.Infinity) == 0 && baseFound {
451 trans.Infinity = base.Infinity
458 if len(trans.DecimalNumberFormat) == 0 && inheritedFound {
459 trans.DecimalNumberFormat = inherited.DecimalNumberFormat
462 if len(trans.DecimalNumberFormat) == 0 && baseFound {
463 trans.DecimalNumberFormat = base.DecimalNumberFormat
466 if len(trans.PercentNumberFormat) == 0 && inheritedFound {
467 trans.PercentNumberFormat = inherited.PercentNumberFormat
470 if len(trans.PercentNumberFormat) == 0 && baseFound {
471 trans.PercentNumberFormat = base.PercentNumberFormat
474 if len(trans.CurrencyNumberFormat) == 0 && inheritedFound {
475 trans.CurrencyNumberFormat = inherited.CurrencyNumberFormat
478 if len(trans.CurrencyNumberFormat) == 0 && baseFound {
479 trans.CurrencyNumberFormat = base.CurrencyNumberFormat
482 if len(trans.NegativeCurrencyNumberFormat) == 0 && inheritedFound {
483 trans.NegativeCurrencyNumberFormat = inherited.NegativeCurrencyNumberFormat
486 if len(trans.NegativeCurrencyNumberFormat) == 0 && baseFound {
487 trans.NegativeCurrencyNumberFormat = base.NegativeCurrencyNumberFormat
492 if len(trans.FmtDateFull) == 0 && inheritedFound {
493 trans.FmtDateFull = inherited.FmtDateFull
496 if len(trans.FmtDateFull) == 0 && baseFound {
497 trans.FmtDateFull = base.FmtDateFull
500 if len(trans.FmtDateLong) == 0 && inheritedFound {
501 trans.FmtDateLong = inherited.FmtDateLong
504 if len(trans.FmtDateLong) == 0 && baseFound {
505 trans.FmtDateLong = base.FmtDateLong
508 if len(trans.FmtDateMedium) == 0 && inheritedFound {
509 trans.FmtDateMedium = inherited.FmtDateMedium
512 if len(trans.FmtDateMedium) == 0 && baseFound {
513 trans.FmtDateMedium = base.FmtDateMedium
516 if len(trans.FmtDateShort) == 0 && inheritedFound {
517 trans.FmtDateShort = inherited.FmtDateShort
520 if len(trans.FmtDateShort) == 0 && baseFound {
521 trans.FmtDateShort = base.FmtDateShort
526 if len(trans.FmtTimeFull) == 0 && inheritedFound {
527 trans.FmtTimeFull = inherited.FmtTimeFull
530 if len(trans.FmtTimeFull) == 0 && baseFound {
531 trans.FmtTimeFull = base.FmtTimeFull
534 if len(trans.FmtTimeLong) == 0 && inheritedFound {
535 trans.FmtTimeLong = inherited.FmtTimeLong
538 if len(trans.FmtTimeLong) == 0 && baseFound {
539 trans.FmtTimeLong = base.FmtTimeLong
542 if len(trans.FmtTimeMedium) == 0 && inheritedFound {
543 trans.FmtTimeMedium = inherited.FmtTimeMedium
546 if len(trans.FmtTimeMedium) == 0 && baseFound {
547 trans.FmtTimeMedium = base.FmtTimeMedium
550 if len(trans.FmtTimeShort) == 0 && inheritedFound {
551 trans.FmtTimeShort = inherited.FmtTimeShort
554 if len(trans.FmtTimeShort) == 0 && baseFound {
555 trans.FmtTimeShort = base.FmtTimeShort
560 if len(trans.FmtMonthsAbbreviated) == 0 && inheritedFound {
561 trans.FmtMonthsAbbreviated = inherited.FmtMonthsAbbreviated
564 if len(trans.FmtMonthsAbbreviated) == 0 && baseFound {
565 trans.FmtMonthsAbbreviated = base.FmtMonthsAbbreviated
568 if len(trans.FmtMonthsNarrow) == 0 && inheritedFound {
569 trans.FmtMonthsNarrow = inherited.FmtMonthsNarrow
572 if len(trans.FmtMonthsNarrow) == 0 && baseFound {
573 trans.FmtMonthsNarrow = base.FmtMonthsNarrow
576 if len(trans.FmtMonthsWide) == 0 && inheritedFound {
577 trans.FmtMonthsWide = inherited.FmtMonthsWide
580 if len(trans.FmtMonthsWide) == 0 && baseFound {
581 trans.FmtMonthsWide = base.FmtMonthsWide
586 if len(trans.FmtDaysAbbreviated) == 0 && inheritedFound {
587 trans.FmtDaysAbbreviated = inherited.FmtDaysAbbreviated
590 if len(trans.FmtDaysAbbreviated) == 0 && baseFound {
591 trans.FmtDaysAbbreviated = base.FmtDaysAbbreviated
594 if len(trans.FmtDaysNarrow) == 0 && inheritedFound {
595 trans.FmtDaysNarrow = inherited.FmtDaysNarrow
598 if len(trans.FmtDaysNarrow) == 0 && baseFound {
599 trans.FmtDaysNarrow = base.FmtDaysNarrow
602 if len(trans.FmtDaysShort) == 0 && inheritedFound {
603 trans.FmtDaysShort = inherited.FmtDaysShort
606 if len(trans.FmtDaysShort) == 0 && baseFound {
607 trans.FmtDaysShort = base.FmtDaysShort
610 if len(trans.FmtDaysWide) == 0 && inheritedFound {
611 trans.FmtDaysWide = inherited.FmtDaysWide
614 if len(trans.FmtDaysWide) == 0 && baseFound {
615 trans.FmtDaysWide = base.FmtDaysWide
620 if len(trans.FmtPeriodsAbbreviated) == 0 && inheritedFound {
621 trans.FmtPeriodsAbbreviated = inherited.FmtPeriodsAbbreviated
624 if len(trans.FmtPeriodsAbbreviated) == 0 && baseFound {
625 trans.FmtPeriodsAbbreviated = base.FmtPeriodsAbbreviated
628 if len(trans.FmtPeriodsNarrow) == 0 && inheritedFound {
629 trans.FmtPeriodsNarrow = inherited.FmtPeriodsNarrow
632 if len(trans.FmtPeriodsNarrow) == 0 && baseFound {
633 trans.FmtPeriodsNarrow = base.FmtPeriodsNarrow
636 if len(trans.FmtPeriodsShort) == 0 && inheritedFound {
637 trans.FmtPeriodsShort = inherited.FmtPeriodsShort
640 if len(trans.FmtPeriodsShort) == 0 && baseFound {
641 trans.FmtPeriodsShort = base.FmtPeriodsShort
644 if len(trans.FmtPeriodsWide) == 0 && inheritedFound {
645 trans.FmtPeriodsWide = inherited.FmtPeriodsWide
648 if len(trans.FmtPeriodsWide) == 0 && baseFound {
649 trans.FmtPeriodsWide = base.FmtPeriodsWide
654 if len(trans.FmtErasAbbreviated) == 0 && inheritedFound {
655 trans.FmtErasAbbreviated = inherited.FmtErasAbbreviated
658 if len(trans.FmtErasAbbreviated) == 0 && baseFound {
659 trans.FmtErasAbbreviated = base.FmtErasAbbreviated
662 if len(trans.FmtErasNarrow) == 0 && inheritedFound {
663 trans.FmtErasNarrow = inherited.FmtErasNarrow
666 if len(trans.FmtErasNarrow) == 0 && baseFound {
667 trans.FmtErasNarrow = base.FmtErasNarrow
670 if len(trans.FmtErasWide) == 0 && inheritedFound {
671 trans.FmtErasWide = inherited.FmtErasWide
674 if len(trans.FmtErasWide) == 0 && baseFound {
675 trans.FmtErasWide = base.FmtErasWide
678 ldml := cldr.RawLDML(trans.Locale)
680 currencies := make([]string, len(globalCurrencies), len(globalCurrencies))
684 for k, v := range globCurrencyIdxMap {
687 // if kval[:len(kval)-1] != " " {
694 // some just have no data...
695 if ldml.Numbers != nil {
697 if ldml.Numbers.Currencies != nil {
698 for _, currency := range ldml.Numbers.Currencies.Currency {
700 if len(currency.Symbol) == 0 {
704 if len(currency.Symbol[0].Data()) == 0 {
708 if len(currency.Type) == 0 {
712 currencies[globCurrencyIdxMap[currency.Type]] = currency.Symbol[0].Data()
717 trans.Currencies = fmt.Sprintf("%#v", currencies)
721 if (trans.timezones == nil || len(trans.timezones) == 0) && inheritedFound {
722 trans.timezones = inherited.timezones
725 if (trans.timezones == nil || len(trans.timezones) == 0) && baseFound {
726 trans.timezones = base.timezones
729 // make sure all inherited timezones are part of sub locale timezones
734 for k, v := range inherited.timezones {
736 if _, ok = trans.timezones[k]; ok {
740 trans.timezones[k] = v
744 // make sure all base timezones are part of sub locale timezones
749 for k, v := range base.timezones {
751 if _, ok = trans.timezones[k]; ok {
755 trans.timezones[k] = v
759 applyOverrides(trans)
761 parseDecimalNumberFormat(trans)
762 parsePercentNumberFormat(trans)
763 parseCurrencyNumberFormat(trans)
766 for _, trans := range translators {
768 fmt.Println("Final Processing:", trans.Locale)
770 // if it's still nill.....
771 if trans.timezones == nil {
772 trans.timezones = make(map[string]*zoneAbbrev)
775 tz := make(map[string]string) // key = abbrev locale eg. EST, EDT, MST, PST... value = long locale eg. Eastern Standard Time, Pacific Time.....
777 for k, v := range timezones {
779 ttz, ok := trans.timezones[k]
782 trans.timezones[k] = v
785 tz[v.standard] = ttz.standard
786 tz[v.daylight] = ttz.daylight
789 trans.FmtTimezones = fmt.Sprintf("%#v", tz)
791 if len(trans.TimeSeparator) == 0 {
792 trans.TimeSeparator = ":"
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)
800 // preprocesses maps, array etc... just requires multiple passes no choice....
801 func preProcess(cldrVar *cldr.CLDR) {
803 for _, l := range cldrVar.Locales() {
805 fmt.Println("Pre Processing:", l)
807 split := strings.SplitN(l, "_", 2)
808 baseLocale := split[0]
809 // inheritedLocale := baseLocale
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
817 trans := &translator{
819 BaseLocale: baseLocale,
820 // InheritedLocale: inheritedLocale,
823 // if is a base locale
825 baseTranslators[baseLocale] = trans
828 // baseTranslators[l] = trans
829 // baseTranslators[baseLocale] = trans // allowing for unofficial fallback if none exists
830 translators[l] = trans
832 // get number, currency and datetime symbols
835 ldml := cldrVar.RawLDML(l)
837 // some just have no data...
838 if ldml.Numbers != nil {
840 if len(ldml.Numbers.Symbols) > 0 {
842 symbol := ldml.Numbers.Symbols[0]
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]
858 if len(symbol.Decimal) > 0 {
859 trans.Decimal = symbol.Decimal[0].Data()
861 if len(symbol.Group) > 0 {
862 trans.Group = symbol.Group[0].Data()
864 if len(symbol.MinusSign) > 0 {
865 trans.Minus = symbol.MinusSign[0].Data()
867 if len(symbol.PercentSign) > 0 {
868 trans.Percent = symbol.PercentSign[0].Data()
870 if len(symbol.PerMille) > 0 {
871 trans.PerMille = symbol.PerMille[0].Data()
874 if len(symbol.TimeSeparator) > 0 {
875 trans.TimeSeparator = symbol.TimeSeparator[0].Data()
878 if len(symbol.Infinity) > 0 {
879 trans.Infinity = symbol.Infinity[0].Data()
883 if ldml.Numbers.Currencies != nil {
885 for _, currency := range ldml.Numbers.Currencies.Currency {
887 if len(strings.TrimSpace(currency.Type)) == 0 {
891 globalCurrenciesMap[currency.Type] = struct{}{}
895 if len(ldml.Numbers.DecimalFormats) > 0 && len(ldml.Numbers.DecimalFormats[0].DecimalFormatLength) > 0 {
897 for _, dfl := range ldml.Numbers.DecimalFormats[0].DecimalFormatLength {
898 if len(dfl.Type) == 0 {
899 trans.DecimalNumberFormat = dfl.DecimalFormat[0].Pattern[0].Data()
905 if len(ldml.Numbers.PercentFormats) > 0 && len(ldml.Numbers.PercentFormats[0].PercentFormatLength) > 0 {
907 for _, dfl := range ldml.Numbers.PercentFormats[0].PercentFormatLength {
908 if len(dfl.Type) == 0 {
909 trans.PercentNumberFormat = dfl.PercentFormat[0].Pattern[0].Data()
915 if len(ldml.Numbers.CurrencyFormats) > 0 && len(ldml.Numbers.CurrencyFormats[0].CurrencyFormatLength) > 0 {
917 if len(ldml.Numbers.CurrencyFormats[0].CurrencyFormatLength[0].CurrencyFormat) > 1 {
919 split := strings.SplitN(ldml.Numbers.CurrencyFormats[0].CurrencyFormatLength[0].CurrencyFormat[1].Pattern[0].Data(), ";", 2)
921 trans.CurrencyNumberFormat = split[0]
923 if len(split) > 1 && len(split[1]) > 0 {
924 trans.NegativeCurrencyNumberFormat = split[1]
926 trans.NegativeCurrencyNumberFormat = trans.CurrencyNumberFormat
929 trans.CurrencyNumberFormat = ldml.Numbers.CurrencyFormats[0].CurrencyFormatLength[0].CurrencyFormat[0].Pattern[0].Data()
930 trans.NegativeCurrencyNumberFormat = trans.CurrencyNumberFormat
935 if ldml.Dates != nil {
937 if ldml.Dates.TimeZoneNames != nil {
939 for _, zone := range ldml.Dates.TimeZoneNames.Metazone {
941 for _, short := range zone.Short {
943 if len(short.Standard) > 0 {
944 za, ok := timezones[zone.Type]
947 timezones[zone.Type] = za
949 za.standard = short.Standard[0].Data()
952 if len(short.Daylight) > 0 {
953 za, ok := timezones[zone.Type]
956 timezones[zone.Type] = za
958 za.daylight = short.Daylight[0].Data()
962 for _, long := range zone.Long {
964 if trans.timezones == nil {
965 trans.timezones = make(map[string]*zoneAbbrev)
968 if len(long.Standard) > 0 {
969 za, ok := trans.timezones[zone.Type]
972 trans.timezones[zone.Type] = za
974 za.standard = long.Standard[0].Data()
977 za, ok := trans.timezones[zone.Type]
980 trans.timezones[zone.Type] = za
983 if len(long.Daylight) > 0 {
984 za.daylight = long.Daylight[0].Data()
986 za.daylight = za.standard
992 if ldml.Dates.Calendars != nil {
994 var calendar *cldr.Calendar
996 for _, cal := range ldml.Dates.Calendars.Calendar {
997 if cal.Type == "gregorian" {
1002 if calendar != nil {
1004 if calendar.DateFormats != nil {
1006 for _, datefmt := range calendar.DateFormats.DateFormatLength {
1008 switch datefmt.Type {
1010 trans.FmtDateFull = datefmt.DateFormat[0].Pattern[0].Data()
1013 trans.FmtDateLong = datefmt.DateFormat[0].Pattern[0].Data()
1016 trans.FmtDateMedium = datefmt.DateFormat[0].Pattern[0].Data()
1019 trans.FmtDateShort = datefmt.DateFormat[0].Pattern[0].Data()
1024 if calendar.TimeFormats != nil {
1026 for _, datefmt := range calendar.TimeFormats.TimeFormatLength {
1028 switch datefmt.Type {
1030 trans.FmtTimeFull = datefmt.TimeFormat[0].Pattern[0].Data()
1032 trans.FmtTimeLong = datefmt.TimeFormat[0].Pattern[0].Data()
1034 trans.FmtTimeMedium = datefmt.TimeFormat[0].Pattern[0].Data()
1036 trans.FmtTimeShort = datefmt.TimeFormat[0].Pattern[0].Data()
1041 if calendar.Months != nil {
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
1048 for _, monthctx := range calendar.Months.MonthContext {
1050 for _, months := range monthctx.MonthWidth {
1052 var monthData []string
1054 for _, m := range months.Month {
1056 if len(m.Data()) == 0 {
1062 monthData = append(monthData, m.Data())
1064 monthData = append(monthData, m.Data())
1066 monthData = append(monthData, m.Data())
1068 monthData = append(monthData, m.Data())
1070 monthData = append(monthData, m.Data())
1072 monthData = append(monthData, m.Data())
1074 monthData = append(monthData, m.Data())
1076 monthData = append(monthData, m.Data())
1078 monthData = append(monthData, m.Data())
1080 monthData = append(monthData, m.Data())
1082 monthData = append(monthData, m.Data())
1084 monthData = append(monthData, m.Data())
1088 if len(monthData) > 0 {
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...)
1095 switch months.Type {
1099 trans.FmtMonthsAbbreviated = fmt.Sprintf("%#v", monthData)
1104 trans.FmtMonthsNarrow = fmt.Sprintf("%#v", monthData)
1109 trans.FmtMonthsWide = fmt.Sprintf("%#v", monthData)
1117 if calendar.Days != nil {
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
1124 for _, dayctx := range calendar.Days.DayContext {
1126 for _, days := range dayctx.DayWidth {
1128 var dayData []string
1130 for _, d := range days.Day {
1134 dayData = append(dayData, d.Data())
1136 dayData = append(dayData, d.Data())
1138 dayData = append(dayData, d.Data())
1140 dayData = append(dayData, d.Data())
1142 dayData = append(dayData, d.Data())
1144 dayData = append(dayData, d.Data())
1146 dayData = append(dayData, d.Data())
1150 if len(dayData) > 0 {
1155 trans.FmtDaysAbbreviated = fmt.Sprintf("%#v", dayData)
1160 trans.FmtDaysNarrow = fmt.Sprintf("%#v", dayData)
1165 trans.FmtDaysShort = fmt.Sprintf("%#v", dayData)
1170 trans.FmtDaysWide = fmt.Sprintf("%#v", dayData)
1178 if calendar.DayPeriods != nil {
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
1185 for _, ctx := range calendar.DayPeriods.DayPeriodContext {
1187 for _, width := range ctx.DayPeriodWidth {
1191 ampm := make([]string, 2, 2)
1193 for _, d := range width.DayPeriod {
1209 trans.FmtPeriodsAbbreviated = fmt.Sprintf("%#v", ampm)
1214 trans.FmtPeriodsNarrow = fmt.Sprintf("%#v", ampm)
1219 trans.FmtPeriodsShort = fmt.Sprintf("%#v", ampm)
1224 trans.FmtPeriodsWide = fmt.Sprintf("%#v", ampm)
1231 if calendar.Eras != nil {
1235 abbrev := make([]string, 2, 2)
1236 narr := make([]string, 2, 2)
1237 wide := make([]string, 2, 2)
1239 if calendar.Eras.EraAbbr != nil {
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()
1250 if calendar.Eras.EraNarrow != nil {
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()
1261 if calendar.Eras.EraNames != nil {
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()
1272 trans.FmtErasAbbreviated = fmt.Sprintf("%#v", abbrev)
1273 trans.FmtErasNarrow = fmt.Sprintf("%#v", narr)
1274 trans.FmtErasWide = fmt.Sprintf("%#v", wide)
1281 for k := range globalCurrenciesMap {
1282 globalCurrencies = append(globalCurrencies, k)
1285 sort.Strings(globalCurrencies)
1287 for i, loc := range globalCurrencies {
1288 globCurrencyIdxMap[loc] = i
1292 func parseDateFormats(trans *translator, shortFormat, mediumFormat, longFormat, fullFormat string) (short, medium, long, full string) {
1294 // Short Date Parsing
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)
1301 // End Short Data Parsing
1306 func parseDateTimeFormat(baseLocale, format string, eraScore uint8) (results string) {
1309 // y = four digit year
1310 // yy = two digit year
1314 var inConstantText bool
1317 for i := 0; i < len(format); i++ {
1325 inConstantText = false
1326 results += "b = append(b, " + fmt.Sprintf("%#v", []byte(format[start:i])) + "...)\n"
1329 results += "b = append(b, " + baseLocale + ".timeSeparator...)"
1335 // peek to see if ''
1336 if len(format) != i && format[i] == '\'' {
1339 inConstantText = false
1340 results += "b = append(b, " + fmt.Sprintf("%#v", []byte(format[start:i-1])) + "...)\n"
1342 inConstantText = true
1349 // not '' so whatever comes between '' is constant
1351 if len(format) != i {
1353 // advance i to the next single quote + 1
1354 for ; i < len(format); i++ {
1355 if format[i] == '\'' {
1358 inConstantText = false
1359 b := []byte(format[start : startI-1])
1360 b = append(b, []byte(format[startI:i])...)
1362 results += "b = append(b, " + fmt.Sprintf("%#v", b) + "...)\n"
1365 results += "b = append(b, " + fmt.Sprintf("%#v", []byte(format[startI:i])) + "...)\n"
1377 inConstantText = false
1378 results += "b = append(b, " + fmt.Sprintf("%#v", []byte(format[start:i])) + "...)\n"
1382 // two digit hour required?
1383 if len(format) != i+1 && format[i+1] == 'H' {
1393 results += "b = strconv.AppendInt(b, int64(t.Hour()), 10)\n"
1399 inConstantText = false
1400 results += "b = append(b, " + fmt.Sprintf("%#v", []byte(format[start:i])) + "...)\n"
1413 // two digit hour required?
1414 if len(format) != i+1 && format[i+1] == 'h' {
1424 results += "b = strconv.AppendInt(b, int64(h), 10)\n"
1430 inConstantText = false
1431 results += "b = append(b, " + fmt.Sprintf("%#v", []byte(format[start:i])) + "...)\n"
1435 // two digit minute required?
1436 if len(format) != i+1 && format[i+1] == 'm' {
1440 if t.Minute() < 10 {
1447 results += "b = strconv.AppendInt(b, int64(t.Minute()), 10)\n"
1453 inConstantText = false
1454 results += "b = append(b, " + fmt.Sprintf("%#v", []byte(format[start:i])) + "...)\n"
1458 // two digit minute required?
1459 if len(format) != i+1 && format[i+1] == 's' {
1463 if t.Second() < 10 {
1470 results += "b = strconv.AppendInt(b, int64(t.Second()), 10)\n"
1476 inConstantText = false
1477 results += "b = append(b, " + fmt.Sprintf("%#v", []byte(format[start:i])) + "...)\n"
1480 // only used with 'h', patterns should not contains 'a' without 'h' so not checking
1482 // choosing to use abbreviated, didn't see any rules about which should be used with which
1488 b = append(b, ` + baseLocale + `.periodsAbbreviated[0]...)
1490 b = append(b, ` + baseLocale + `.periodsAbbreviated[1]...)
1499 inConstantText = false
1500 results += "b = append(b, " + fmt.Sprintf("%#v", []byte(format[start:i])) + "...)\n"
1503 // consume multiple, only handling Abbrev tz from time.Time for the moment...
1507 if format[i] == 'z' {
1508 for j := i; j < len(format); j++ {
1509 if format[j] == 'z' {
1517 if format[i] == 'v' {
1518 for j := i; j < len(format); j++ {
1519 if format[j] == 'v' {
1529 // using the timezone on the Go time object, eg. EST, EDT, MST.....
1536 b = append(b, tz...)
1544 if btz, ok := ` + baseLocale + `.timezones[tz]; ok {
1545 b = append(b, btz...)
1547 b = append(b, tz...)
1557 inConstantText = false
1558 results += "b = append(b, " + fmt.Sprintf("%#v", []byte(format[start:i])) + "...)\n"
1562 // two digit day required?
1563 if len(format) != i+1 && format[i+1] == 'd' {
1574 results += "b = strconv.AppendInt(b, int64(t.Day()), 10)\n"
1580 inConstantText = false
1581 results += "b = append(b, " + fmt.Sprintf("%#v", []byte(format[start:i])) + "...)\n"
1587 for j := i; j < len(format); j++ {
1588 if format[j] == 'M' {
1597 // Numeric form, at least 1 digit
1600 results += "b = strconv.AppendInt(b, int64(t.Month()), 10)\n"
1602 // Number form, at least 2 digits (padding with 0)
1611 b = strconv.AppendInt(b, int64(t.Month()), 10)
1618 results += "b = append(b, " + baseLocale + ".monthsAbbreviated[t.Month()]...)\n"
1623 results += "b = append(b, " + baseLocale + ".monthsWide[t.Month()]...)\n"
1625 // Narrow form - only used in where context makes it clear, such as headers in a calendar.
1626 // Should be one character wherever possible.
1629 results += "b = append(b, " + baseLocale + ".monthsNarrow[t.Month()]...)\n"
1639 inConstantText = false
1640 results += "b = append(b, " + fmt.Sprintf("%#v", []byte(format[start:i])) + "...)\n"
1645 if len(format) != i+1 && format[i+1] == 'y' {
1650 b = append(b, strconv.Itoa(t.Year())[2:]...)
1652 b = append(b, strconv.Itoa(t.Year())[1:]...)
1661 b = strconv.AppendInt(b, int64(t.Year()), 10)
1663 b = strconv.AppendInt(b, int64(-t.Year()), 10)
1670 // I know I only see 'EEEE' in the xml, but just in case handled all posibilities
1674 inConstantText = false
1675 results += "b = append(b, " + fmt.Sprintf("%#v", []byte(format[start:i])) + "...)\n"
1681 for j := i; j < len(format); j++ {
1682 if format[j] == 'E' {
1694 results += "b = append(b, " + baseLocale + ".daysNarrow[t.Weekday()]...)\n"
1699 results += "b = append(b, " + baseLocale + ".daysShort[t.Weekday()]...)\n"
1704 results += "b = append(b, " + baseLocale + ".daysAbbreviated[t.Weekday()]...)\n"
1709 results += "b = append(b, " + baseLocale + ".daysWide[t.Weekday()]...)\n"
1719 inConstantText = false
1720 results += "b = append(b, " + fmt.Sprintf("%#v", []byte(format[start:i])) + "...)\n"
1728 b = append(b, ` + baseLocale + `.erasWide[0]...)
1730 b = append(b, ` + baseLocale + `.erasWide[1]...)
1738 b = append(b, ` + baseLocale + `.erasAbbreviated[0]...)
1740 b = append(b, ` + baseLocale + `.erasAbbreviated[1]...)
1747 // append all non matched text as they are constants
1748 if !inConstantText {
1749 inConstantText = true
1755 // if we were inConstantText when the string ended, add what's left.
1757 // inContantText = false
1758 results += "b = append(b, " + fmt.Sprintf("%#v", []byte(format[start:])) + "...)\n"
1764 func parseCurrencyNumberFormat(trans *translator) {
1766 if len(trans.CurrencyNumberFormat) == 0 {
1770 trans.FmtCurrencyExists = true
1771 negativeEqual := trans.CurrencyNumberFormat == trans.NegativeCurrencyNumberFormat
1773 match := groupLenRegex.FindString(trans.CurrencyNumberFormat)
1775 trans.FmtCurrencyGroupLen = len(match) - 2
1778 match = requiredDecimalRegex.FindString(trans.CurrencyNumberFormat)
1780 trans.FmtCurrencyMinDecimalLen = len(match) - 1
1783 match = secondaryGroupLenRegex.FindString(trans.CurrencyNumberFormat)
1785 trans.FmtCurrencySecondaryGroupLen = len(match) - 2
1790 for idx = 0; idx < len(trans.CurrencyNumberFormat); idx++ {
1791 if trans.CurrencyNumberFormat[idx] == '#' || trans.CurrencyNumberFormat[idx] == '0' {
1792 trans.FmtCurrencyPrefix = trans.CurrencyNumberFormat[:idx]
1797 for idx = len(trans.CurrencyNumberFormat) - 1; idx >= 0; idx-- {
1798 if trans.CurrencyNumberFormat[idx] == '#' || trans.CurrencyNumberFormat[idx] == '0' {
1800 trans.FmtCurrencySuffix = trans.CurrencyNumberFormat[idx:]
1805 for idx = 0; idx < len(trans.FmtCurrencyPrefix); idx++ {
1806 if trans.FmtCurrencyPrefix[idx] == '¤' {
1808 trans.FmtCurrencyInPrefix = true
1809 trans.FmtCurrencyPrefix = strings.Replace(trans.FmtCurrencyPrefix, string(trans.FmtCurrencyPrefix[idx]), "", 1)
1812 trans.FmtCurrencyLeft = true
1814 trans.FmtCurrencyLeft = false
1821 for idx = 0; idx < len(trans.FmtCurrencySuffix); idx++ {
1822 if trans.FmtCurrencySuffix[idx] == '¤' {
1824 trans.FmtCurrencyInPrefix = false
1825 trans.FmtCurrencySuffix = strings.Replace(trans.FmtCurrencySuffix, string(trans.FmtCurrencySuffix[idx]), "", 1)
1828 trans.FmtCurrencyLeft = true
1830 trans.FmtCurrencyLeft = false
1837 // if len(trans.FmtCurrencyPrefix) > 0 {
1838 // trans.FmtCurrencyPrefix = fmt.Sprintf("%#v", []byte(trans.FmtCurrencyPrefix))
1841 // if len(trans.FmtCurrencySuffix) > 0 {
1842 // trans.FmtCurrencySuffix = fmt.Sprintf("%#v", []byte(trans.FmtCurrencySuffix))
1845 // no need to parse again if true....
1848 trans.FmtCurrencyNegativePrefix = trans.FmtCurrencyPrefix
1849 trans.FmtCurrencyNegativeSuffix = trans.FmtCurrencySuffix
1850 trans.FmtCurrencyNegativeInPrefix = trans.FmtCurrencyInPrefix
1851 trans.FmtCurrencyNegativeLeft = trans.FmtCurrencyLeft
1856 trans.FmtCurrencyNegativeExists = true
1858 for idx = 0; idx < len(trans.NegativeCurrencyNumberFormat); idx++ {
1859 if trans.NegativeCurrencyNumberFormat[idx] == '#' || trans.NegativeCurrencyNumberFormat[idx] == '0' {
1861 trans.FmtCurrencyNegativePrefix = trans.NegativeCurrencyNumberFormat[:idx]
1866 for idx = len(trans.NegativeCurrencyNumberFormat) - 1; idx >= 0; idx-- {
1867 if trans.NegativeCurrencyNumberFormat[idx] == '#' || trans.NegativeCurrencyNumberFormat[idx] == '0' {
1869 trans.FmtCurrencyNegativeSuffix = trans.NegativeCurrencyNumberFormat[idx:]
1874 for idx = 0; idx < len(trans.FmtCurrencyNegativePrefix); idx++ {
1875 if trans.FmtCurrencyNegativePrefix[idx] == '¤' {
1877 trans.FmtCurrencyNegativeInPrefix = true
1878 trans.FmtCurrencyNegativePrefix = strings.Replace(trans.FmtCurrencyNegativePrefix, string(trans.FmtCurrencyNegativePrefix[idx]), "", 1)
1881 trans.FmtCurrencyNegativeLeft = true
1883 trans.FmtCurrencyNegativeLeft = false
1890 for idx = 0; idx < len(trans.FmtCurrencyNegativeSuffix); idx++ {
1891 if trans.FmtCurrencyNegativeSuffix[idx] == '¤' {
1893 trans.FmtCurrencyNegativeInPrefix = false
1894 trans.FmtCurrencyNegativeSuffix = strings.Replace(trans.FmtCurrencyNegativeSuffix, string(trans.FmtCurrencyNegativeSuffix[idx]), "", 1)
1897 trans.FmtCurrencyNegativeLeft = true
1899 trans.FmtCurrencyNegativeLeft = false
1906 // if len(trans.FmtCurrencyNegativePrefix) > 0 {
1907 // trans.FmtCurrencyNegativePrefix = fmt.Sprintf("%#v", []byte(trans.FmtCurrencyNegativePrefix))
1910 // if len(trans.FmtCurrencyNegativeSuffix) > 0 {
1911 // trans.FmtCurrencyNegativeSuffix = fmt.Sprintf("%#v", []byte(trans.FmtCurrencyNegativeSuffix))
1917 func parsePercentNumberFormat(trans *translator) {
1919 if len(trans.PercentNumberFormat) == 0 {
1923 trans.FmtPercentExists = true
1925 match := groupLenPercentRegex.FindString(trans.PercentNumberFormat)
1927 trans.FmtPercentGroupLen = len(match) - 1
1930 match = requiredDecimalRegex.FindString(trans.PercentNumberFormat)
1932 trans.FmtPercentMinDecimalLen = len(match) - 1
1935 match = secondaryGroupLenRegex.FindString(trans.PercentNumberFormat)
1937 trans.FmtPercentSecondaryGroupLen = len(match) - 2
1942 for idx = 0; idx < len(trans.PercentNumberFormat); idx++ {
1943 if trans.PercentNumberFormat[idx] == '#' || trans.PercentNumberFormat[idx] == '0' {
1944 trans.FmtPercentPrefix = trans.PercentNumberFormat[:idx]
1949 for idx = len(trans.PercentNumberFormat) - 1; idx >= 0; idx-- {
1950 if trans.PercentNumberFormat[idx] == '#' || trans.PercentNumberFormat[idx] == '0' {
1952 trans.FmtPercentSuffix = trans.PercentNumberFormat[idx:]
1957 for idx = 0; idx < len(trans.FmtPercentPrefix); idx++ {
1958 if trans.FmtPercentPrefix[idx] == '%' {
1960 trans.FmtPercentInPrefix = true
1961 trans.FmtPercentPrefix = strings.Replace(trans.FmtPercentPrefix, string(trans.FmtPercentPrefix[idx]), "", 1)
1964 trans.FmtPercentLeft = true
1966 trans.FmtPercentLeft = false
1973 for idx = 0; idx < len(trans.FmtPercentSuffix); idx++ {
1974 if trans.FmtPercentSuffix[idx] == '%' {
1976 trans.FmtPercentInPrefix = false
1977 trans.FmtPercentSuffix = strings.Replace(trans.FmtPercentSuffix, string(trans.FmtPercentSuffix[idx]), "", 1)
1980 trans.FmtPercentLeft = true
1982 trans.FmtPercentLeft = false
1989 // if len(trans.FmtPercentPrefix) > 0 {
1990 // trans.FmtPercentPrefix = fmt.Sprintf("%#v", []byte(trans.FmtPercentPrefix))
1993 // if len(trans.FmtPercentSuffix) > 0 {
1994 // trans.FmtPercentSuffix = fmt.Sprintf("%#v", []byte(trans.FmtPercentSuffix))
2000 func parseDecimalNumberFormat(trans *translator) {
2002 if len(trans.DecimalNumberFormat) == 0 {
2006 trans.FmtNumberExists = true
2008 formats := strings.SplitN(trans.DecimalNumberFormat, ";", 2)
2010 match := groupLenRegex.FindString(formats[0])
2012 trans.FmtNumberGroupLen = len(match) - 2
2015 match = requiredDecimalRegex.FindString(formats[0])
2017 trans.FmtNumberMinDecimalLen = len(match) - 1
2020 match = secondaryGroupLenRegex.FindString(formats[0])
2022 trans.FmtNumberSecondaryGroupLen = len(match) - 2
2028 type sortRank struct {
2033 type ByRank []sortRank
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 }
2039 type ByPluralRule []locales.PluralRule
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] }
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) {
2049 var pluralRange *struct {
2051 Locales string `xml:"locales,attr"`
2052 PluralRange []*struct {
2054 Start string `xml:"start,attr"`
2055 End string `xml:"end,attr"`
2056 Result string `xml:"result,attr"`
2057 } `xml:"pluralRange"`
2060 var pluralArr []locales.PluralRule
2062 for _, pr := range current.Supplemental().Plurals[1].PluralRanges {
2064 locs := strings.Split(pr.Locales, " ")
2066 for _, loc := range locs {
2068 if loc == baseLocale {
2074 // no range plural rules for locale
2075 if pluralRange == nil {
2077 results = "return locales.PluralRuleUnknown"
2081 mp := make(map[string]struct{})
2083 // pre-process if all the same
2084 for _, rule := range pluralRange.PluralRange {
2085 mp[rule.Result] = struct{}{}
2089 psI := pluralStringToInt(k)
2090 pluralArr = append(pluralArr, psI)
2094 results += "return locales." + pluralStringToString(pluralRange.PluralRange[0].Result)
2095 plurals = fmt.Sprintf("%#v", pluralArr)
2099 multiple := len(pluralRange.PluralRange) > 1
2102 results += "start := " + baseLocale + ".CardinalPluralRule(num1, v1)\n"
2103 results += "end := " + baseLocale + ".CardinalPluralRule(num2, v2)\n\n"
2108 // pre parse for variables
2109 for i, rule := range pluralRange.PluralRange {
2111 if i == len(pluralRange.PluralRange)-1 {
2116 results += "return locales." + pluralStringToString(rule.Result)
2124 results += "else if"
2127 results += " start == locales." + pluralStringToString(rule.Start) + " && end == locales." + pluralStringToString(rule.End) + " {\n return locales." + pluralStringToString(rule.Result) + "\n} "
2132 results = "\n" + results + "\n"
2135 if len(pluralArr) == 0 {
2139 ints := make([]int, len(pluralArr))
2140 for i := 0; i < len(pluralArr); i++ {
2141 ints[i] = int(pluralArr[i])
2146 for i := 0; i < len(ints); i++ {
2147 pluralArr[i] = locales.PluralRule(ints[i])
2150 plurals = fmt.Sprintf("%#v", pluralArr)
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) {
2161 var prOrdinal *struct {
2163 Locales string "xml:\"locales,attr\""
2164 PluralRule []*struct {
2166 Count string "xml:\"count,attr\""
2167 } "xml:\"pluralRule\""
2170 var pluralArr []locales.PluralRule
2172 // idx 0 is ordinal rules
2173 for _, pr := range current.Supplemental().Plurals[0].PluralRules {
2175 locs := strings.Split(pr.Locales, " ")
2177 for _, loc := range locs {
2179 if loc == baseLocale {
2182 // for _, pl := range pr.PluralRule {
2183 // fmt.Println(pl.Count, pl.Common.Data())
2189 // no plural rules for locale
2190 if prOrdinal == nil {
2192 results = "return locales.PluralRuleUnknown"
2196 vals := make(map[string]struct{})
2199 // pre parse for variables
2200 for _, rule := range prOrdinal.PluralRule {
2202 ps1 := pluralStringToString(rule.Count)
2203 psI := pluralStringToInt(rule.Count)
2204 pluralArr = append(pluralArr, psI)
2206 data := strings.Replace(strings.Replace(strings.Replace(strings.TrimSpace(strings.SplitN(rule.Common.Data(), "@", 2)[0]), " = ", " == ", -1), " or ", " || ", -1), " and ", " && ", -1)
2209 if len(prOrdinal.PluralRule) == 1 {
2211 results = "return locales." + ps1
2215 results += "\n\nreturn locales." + ps1
2216 // results += "else {\nreturn locales." + locales.PluralStringToString(rule.Count) + ", nil\n}"
2222 // // All need n, so always add
2223 // if strings.Contains(data, "n") {
2224 // vals[prVarFuncs["n"]] = struct{}{}
2227 if strings.Contains(data, "i") {
2228 vals[prVarFuncs["i"]] = struct{}{}
2231 // v is inherently avaialable as an argument
2232 // if strings.Contains(data, "v") {
2233 // vals[prVarFuncs["v"]] = struct{}{}
2236 if strings.Contains(data, "w") {
2237 vals[prVarFuncs["w"]] = struct{}{}
2240 if strings.Contains(data, "f") {
2241 vals[prVarFuncs["f"]] = struct{}{}
2244 if strings.Contains(data, "t") {
2245 vals[prVarFuncs["t"]] = struct{}{}
2252 results += "else if "
2259 // split by 'or' then by 'and' allowing to better
2260 // determine bracketing for formula
2262 ors := strings.Split(data, "||")
2264 for _, or := range ors {
2268 ands := strings.Split(strings.TrimSpace(or), "&&")
2270 for _, and := range ands {
2276 args := strings.Split(strings.TrimSpace(and), " ")
2278 for _, a := range args {
2281 // check to see if is a value range 2..9
2283 multiRange := strings.Count(a, "..") > 1
2284 cargs := strings.Split(strings.TrimSpace(a), ",")
2285 hasBracket := len(cargs) > 1
2286 bracketAdded := false
2287 lastWasRange := false
2289 for _, carg := range cargs {
2291 if rng := strings.Split(carg, ".."); len(rng) > 1 {
2299 switch preOperator {
2301 pre += lft + " >= " + rng[0] + " && " + lft + "<=" + rng[1]
2303 pre += "(" + lft + " < " + rng[0] + " || " + lft + " > " + rng[1] + ")"
2317 pre = strings.TrimRight(pre, " || ") + " && "
2320 lastWasRange = false
2322 if hasBracket && !bracketAdded {
2327 // single comma separated values
2328 switch preOperator {
2330 pre += " " + lft + preOperator + carg + " || "
2332 pre += " " + lft + preOperator + carg + " && "
2337 pre = strings.TrimRight(pre, " || ")
2338 pre = strings.TrimRight(pre, " && ")
2339 pre = strings.TrimRight(pre, " || ")
2341 if hasBracket && bracketAdded {
2348 if strings.Contains(a, "=") || a == ">" || a == "<" {
2357 stmt += pre + " && "
2360 stmt = strings.TrimRight(stmt, " && ") + ") || "
2363 stmt = strings.TrimRight(stmt, " || ")
2369 // return plural rule here
2370 results += "return locales." + ps1 + "\n"
2378 vals[prVarFuncs["n"]] = struct{}{}
2380 sorted := make([]sortRank, 0, len(vals))
2382 for k := range vals {
2385 sorted = append(sorted, sortRank{
2386 Value: prVarFuncs["n"],
2390 sorted = append(sorted, sortRank{
2391 Value: prVarFuncs["i"],
2395 sorted = append(sorted, sortRank{
2396 Value: prVarFuncs["w"],
2400 sorted = append(sorted, sortRank{
2401 Value: prVarFuncs["f"],
2405 sorted = append(sorted, sortRank{
2406 Value: prVarFuncs["t"],
2412 sort.Sort(ByRank(sorted))
2414 for _, k := range sorted {
2418 if len(results) == 0 {
2419 results = "return locales.PluralRuleUnknown"
2422 if !strings.HasPrefix(results, "return") {
2424 results = manyToSingleVars(results)
2426 results = pre + results
2430 if len(pluralArr) == 0 {
2433 plurals = fmt.Sprintf("%#v", pluralArr)
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.
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) {
2445 var prCardinal *struct {
2447 Locales string "xml:\"locales,attr\""
2448 PluralRule []*struct {
2450 Count string "xml:\"count,attr\""
2451 } "xml:\"pluralRule\""
2454 var pluralArr []locales.PluralRule
2455 var inBaseLocale bool
2459 // idx 2 is cardinal rules
2460 for _, pr := range current.Supplemental().Plurals[2].PluralRules {
2462 locs := strings.Split(pr.Locales, " ")
2464 for _, loc := range locs {
2472 // no plural rules for locale
2473 if prCardinal == nil {
2482 results = "return locales.PluralRuleUnknown"
2486 vals := make(map[string]struct{})
2489 // pre parse for variables
2490 for _, rule := range prCardinal.PluralRule {
2492 ps1 := pluralStringToString(rule.Count)
2493 psI := pluralStringToInt(rule.Count)
2494 pluralArr = append(pluralArr, psI)
2496 data := strings.Replace(strings.Replace(strings.Replace(strings.TrimSpace(strings.SplitN(rule.Common.Data(), "@", 2)[0]), " = ", " == ", -1), " or ", " || ", -1), " and ", " && ", -1)
2499 if len(prCardinal.PluralRule) == 1 {
2501 results = "return locales." + ps1
2505 results += "\n\nreturn locales." + ps1
2506 // results += "else {\nreturn locales." + locales.PluralStringToString(rule.Count) + ", nil\n}"
2512 // // All need n, so always add
2513 // if strings.Contains(data, "n") {
2514 // vals[prVarFuncs["n"]] = struct{}{}
2517 if strings.Contains(data, "i") {
2518 vals[prVarFuncs["i"]] = struct{}{}
2521 // v is inherently avaialable as an argument
2522 // if strings.Contains(data, "v") {
2523 // vals[prVarFuncs["v"]] = struct{}{}
2526 if strings.Contains(data, "w") {
2527 vals[prVarFuncs["w"]] = struct{}{}
2530 if strings.Contains(data, "f") {
2531 vals[prVarFuncs["f"]] = struct{}{}
2534 if strings.Contains(data, "t") {
2535 vals[prVarFuncs["t"]] = struct{}{}
2542 results += "else if "
2549 // split by 'or' then by 'and' allowing to better
2550 // determine bracketing for formula
2552 ors := strings.Split(data, "||")
2554 for _, or := range ors {
2558 ands := strings.Split(strings.TrimSpace(or), "&&")
2560 for _, and := range ands {
2566 args := strings.Split(strings.TrimSpace(and), " ")
2568 for _, a := range args {
2571 // check to see if is a value range 2..9
2573 multiRange := strings.Count(a, "..") > 1
2574 cargs := strings.Split(strings.TrimSpace(a), ",")
2575 hasBracket := len(cargs) > 1
2576 bracketAdded := false
2577 lastWasRange := false
2579 for _, carg := range cargs {
2581 if rng := strings.Split(carg, ".."); len(rng) > 1 {
2589 switch preOperator {
2591 pre += lft + " >= " + rng[0] + " && " + lft + "<=" + rng[1]
2593 pre += "(" + lft + " < " + rng[0] + " || " + lft + " > " + rng[1] + ")"
2607 pre = strings.TrimRight(pre, " || ") + " && "
2610 lastWasRange = false
2612 if hasBracket && !bracketAdded {
2617 // single comma separated values
2618 switch preOperator {
2620 pre += " " + lft + preOperator + carg + " || "
2622 pre += " " + lft + preOperator + carg + " && "
2627 pre = strings.TrimRight(pre, " || ")
2628 pre = strings.TrimRight(pre, " && ")
2629 pre = strings.TrimRight(pre, " || ")
2631 if hasBracket && bracketAdded {
2638 if strings.Contains(a, "=") || a == ">" || a == "<" {
2647 stmt += pre + " && "
2650 stmt = strings.TrimRight(stmt, " && ") + ") || "
2653 stmt = strings.TrimRight(stmt, " || ")
2659 // return plural rule here
2660 results += "return locales." + ps1 + "\n"
2668 vals[prVarFuncs["n"]] = struct{}{}
2670 sorted := make([]sortRank, 0, len(vals))
2672 for k := range vals {
2675 sorted = append(sorted, sortRank{
2676 Value: prVarFuncs["n"],
2680 sorted = append(sorted, sortRank{
2681 Value: prVarFuncs["i"],
2685 sorted = append(sorted, sortRank{
2686 Value: prVarFuncs["w"],
2690 sorted = append(sorted, sortRank{
2691 Value: prVarFuncs["f"],
2695 sorted = append(sorted, sortRank{
2696 Value: prVarFuncs["t"],
2702 sort.Sort(ByRank(sorted))
2704 for _, k := range sorted {
2708 if len(results) == 0 {
2709 results = "return locales.PluralRuleUnknown"
2712 if !strings.HasPrefix(results, "return") {
2714 results = manyToSingleVars(results)
2716 results = pre + results
2720 if len(pluralArr) == 0 {
2723 plurals = fmt.Sprintf("%#v", pluralArr)
2729 func manyToSingleVars(input string) (results string) {
2731 matches := nModRegex.FindAllString(input, -1)
2732 mp := make(map[string][]string) // map of formula to variable
2737 for _, formula := range matches {
2739 if _, found = mp[formula]; found {
2743 split = strings.SplitN(formula, "%", 2)
2745 mp[formula] = []string{split[1], "math.Mod(" + split[0] + ", " + split[1] + ")"}
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)
2754 matches = iModRegex.FindAllString(input, -1)
2755 mp = make(map[string][]string) // map of formula to variable
2757 for _, formula := range matches {
2759 if _, found = mp[formula]; found {
2763 split = strings.SplitN(formula, "%", 2)
2765 mp[formula] = []string{split[1], formula}
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)
2774 matches = wModRegex.FindAllString(input, -1)
2775 mp = make(map[string][]string) // map of formula to variable
2777 for _, formula := range matches {
2779 if _, found = mp[formula]; found {
2783 split = strings.SplitN(formula, "%", 2)
2785 mp[formula] = []string{split[1], formula}
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)
2794 matches = fModRegex.FindAllString(input, -1)
2795 mp = make(map[string][]string) // map of formula to variable
2797 for _, formula := range matches {
2799 if _, found = mp[formula]; found {
2803 split = strings.SplitN(formula, "%", 2)
2805 mp[formula] = []string{split[1], formula}
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)
2814 matches = tModRegex.FindAllString(input, -1)
2815 mp = make(map[string][]string) // map of formula to variable
2817 for _, formula := range matches {
2819 if _, found = mp[formula]; found {
2823 split = strings.SplitN(formula, "%", 2)
2825 mp[formula] = []string{split[1], formula}
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)
2834 results = results + "\n" + input
2839 // pluralStringToInt returns the enum value of 'plural' provided
2840 func pluralStringToInt(plural string) locales.PluralRule {
2844 return locales.PluralRuleZero
2846 return locales.PluralRuleOne
2848 return locales.PluralRuleTwo
2850 return locales.PluralRuleFew
2852 return locales.PluralRuleMany
2854 return locales.PluralRuleOther
2856 return locales.PluralRuleUnknown
2860 func pluralStringToString(pr string) string {
2862 pr = strings.TrimSpace(pr)
2866 return "PluralRuleZero"
2868 return "PluralRuleOne"
2870 return "PluralRuleTwo"
2872 return "PluralRuleFew"
2874 return "PluralRuleMany"
2876 return "PluralRuleOther"
2878 return "PluralRuleUnknown"