...

Source file src/github.com/go-playground/locales/cmd/generate_resources.go

Documentation: github.com/go-playground/locales/cmd

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

View as plain text