package main import ( "bytes" "fmt" "go/format" "io/ioutil" "os" "path/filepath" "strings" "sync" "text/template" "time" "gopkg.in/yaml.v2" "golang.org/x/text/unicode/cldr" i18n "github.com/go-playground/universal-translator" ) // numbers: // symbols: // decimal: . // group: ',' // negative: '-' // percent: '%' // permille: "\u2030" // formats: // decimal: '#,##0.###' // currency: "\xA4#,##0.00;(\xA4#,##0.00)" // percent: '#,##0%' // currencies: // JPY: // symbol: "\xA5" // USD: // symbol: $ type pluralInfo struct { path string locale string plural string } func main() { //plurals rules := "data/rules" plurals := map[string]*pluralInfo{} basePlurals := map[string]string{} err := filepath.Walk(rules, func(path string, info os.FileInfo, err error) error { if err != nil { panic(err) } if info.IsDir() { return nil } in, err := ioutil.ReadFile(path) if err != nil { panic(err) } var out yaml.MapSlice if err = yaml.Unmarshal(in, &out); err != nil { panic(err) } var plural string for _, item := range out { if item.Key == "plural" { plural = item.Value.(string) } } locale := strings.Replace(info.Name(), filepath.Ext(info.Name()), "", 1) locale = strings.ToLower(strings.Replace(locale, "-", "_", -1)) plurals[locale] = &pluralInfo{ path: path, locale: locale, plural: plural, } if plural == "" { return nil } basePlurals[locale] = plural return nil }) if err != nil { panic(err) } for _, p := range plurals { if p.plural == "" { var ok bool fmt.Print("can't find plurals in ", p.path, " attempting to locate base language plural rules...") base := strings.SplitN(p.locale, "_", 2) p.plural, ok = basePlurals[base[0]] if !ok { fmt.Println("Not Found") continue } fmt.Println("Found") } } // cldr var decoder cldr.Decoder cldr, err := decoder.DecodePath("data/core") if err != nil { panic(err) } locs := map[string]string{} numbers := map[string]i18n.Number{} calendars := map[string]i18n.Calendar{} locales := cldr.Locales() for _, loc := range locales { ldml := cldr.RawLDML(loc) if ldml.Numbers == nil { continue } var number i18n.Number number.Currencies = make(i18n.CurrencyFormatValue) if len(ldml.Numbers.Symbols) > 0 { symbol := ldml.Numbers.Symbols[0] if len(symbol.Decimal) > 0 { number.Symbols.Decimal = symbol.Decimal[0].Data() } if len(symbol.Group) > 0 { number.Symbols.Group = symbol.Group[0].Data() } if len(symbol.MinusSign) > 0 { number.Symbols.Negative = symbol.MinusSign[0].Data() } if len(symbol.PercentSign) > 0 { number.Symbols.Percent = symbol.PercentSign[0].Data() } if len(symbol.PerMille) > 0 { number.Symbols.PerMille = symbol.PerMille[0].Data() } } if len(ldml.Numbers.DecimalFormats) > 0 && len(ldml.Numbers.DecimalFormats[0].DecimalFormatLength) > 0 { number.Formats.Decimal = ldml.Numbers.DecimalFormats[0].DecimalFormatLength[0].DecimalFormat[0].Pattern[0].Data() } if len(ldml.Numbers.CurrencyFormats) > 0 && len(ldml.Numbers.CurrencyFormats[0].CurrencyFormatLength) > 0 { number.Formats.Currency = ldml.Numbers.CurrencyFormats[0].CurrencyFormatLength[0].CurrencyFormat[0].Pattern[0].Data() number.Formats.CurrencyAccounting = number.Formats.Currency if len(ldml.Numbers.CurrencyFormats[0].CurrencyFormatLength[0].CurrencyFormat) > 1 { number.Formats.CurrencyAccounting = ldml.Numbers.CurrencyFormats[0].CurrencyFormatLength[0].CurrencyFormat[1].Pattern[0].Data() } } if len(ldml.Numbers.PercentFormats) > 0 && len(ldml.Numbers.PercentFormats[0].PercentFormatLength) > 0 { number.Formats.Percent = ldml.Numbers.PercentFormats[0].PercentFormatLength[0].PercentFormat[0].Pattern[0].Data() } if ldml.Numbers.Currencies != nil { for _, currency := range ldml.Numbers.Currencies.Currency { var c i18n.Currency c.Currency = currency.Type if len(currency.DisplayName) > 0 { c.DisplayName = currency.DisplayName[0].Data() } if len(currency.Symbol) > 0 { c.Symbol = currency.Symbol[0].Data() } number.Currencies[c.Currency] = c } } numbers[loc] = number locs[loc] = strings.ToLower(strings.Replace(loc, "_", "", -1)) if ldml.Dates != nil && ldml.Dates.Calendars != nil { var calendar i18n.Calendar ldmlCar := ldml.Dates.Calendars.Calendar[0] for _, cal := range ldml.Dates.Calendars.Calendar { if cal.Type == "gregorian" { ldmlCar = cal } } if ldmlCar.DateFormats != nil { for _, datefmt := range ldmlCar.DateFormats.DateFormatLength { switch datefmt.Type { case "full": calendar.Formats.DateEra.BC.Full = datefmt.DateFormat[0].Pattern[0].Data() calendar.Formats.DateEra.AD.Full = calendar.Formats.DateEra.BC.Full case "long": calendar.Formats.DateEra.BC.Long = datefmt.DateFormat[0].Pattern[0].Data() calendar.Formats.DateEra.AD.Long = calendar.Formats.DateEra.BC.Long // Overrides TODO: Split Each Section into separate function, getting to big to maintain case "medium": calendar.Formats.DateEra.BC.Medium = datefmt.DateFormat[0].Pattern[0].Data() calendar.Formats.DateEra.AD.Medium = calendar.Formats.DateEra.BC.Medium case "short": calendar.Formats.DateEra.BC.Short = datefmt.DateFormat[0].Pattern[0].Data() calendar.Formats.DateEra.AD.Short = calendar.Formats.DateEra.BC.Short } } // Overrides TODO: Split Each Section into separate function, getting to big to maintain switch loc { case "th": calendar.Formats.DateEra.BC.Full = "EEEEที่ d MMMM GGGG y" calendar.Formats.DateEra.AD.Full = "EEEEที่ d MMMM y GGGG" calendar.Formats.DateEra.BC.Long = "d MMMM GG y" calendar.Formats.DateEra.AD.Long = "d MMMM y GG" case "en": calendar.Formats.DateEra.BC.Full = "EEEE, MMMM d, y GGGG" calendar.Formats.DateEra.BC.Long = "MMMM d, y GG" // calendar.Formats.DateEra.BC.Medium = "MMM d, y GG" // calendar.Formats.DateEra.BC.Short = "M/d/yy G" } } if ldmlCar.TimeFormats != nil { for _, datefmt := range ldmlCar.TimeFormats.TimeFormatLength { switch datefmt.Type { case "full": calendar.Formats.Time.Full = datefmt.TimeFormat[0].Pattern[0].Data() case "long": calendar.Formats.Time.Long = datefmt.TimeFormat[0].Pattern[0].Data() case "medium": calendar.Formats.Time.Medium = datefmt.TimeFormat[0].Pattern[0].Data() case "short": calendar.Formats.Time.Short = datefmt.TimeFormat[0].Pattern[0].Data() } } } if ldmlCar.DateTimeFormats != nil { for _, datefmt := range ldmlCar.DateTimeFormats.DateTimeFormatLength { switch datefmt.Type { case "full": calendar.Formats.DateTime.Full = datefmt.DateTimeFormat[0].Pattern[0].Data() case "long": calendar.Formats.DateTime.Long = datefmt.DateTimeFormat[0].Pattern[0].Data() case "medium": calendar.Formats.DateTime.Medium = datefmt.DateTimeFormat[0].Pattern[0].Data() case "short": calendar.Formats.DateTime.Short = datefmt.DateTimeFormat[0].Pattern[0].Data() } } } if ldmlCar.Months != nil { for _, monthctx := range ldmlCar.Months.MonthContext { for _, months := range monthctx.MonthWidth { i18nMonth := make(i18n.CalendarMonthFormatNameValue) for _, m := range months.Month { switch m.Type { case "1": i18nMonth[time.January] = m.Data() case "2": i18nMonth[time.February] = m.Data() case "3": i18nMonth[time.March] = m.Data() case "4": i18nMonth[time.April] = m.Data() case "5": i18nMonth[time.May] = m.Data() case "6": i18nMonth[time.June] = m.Data() case "7": i18nMonth[time.July] = m.Data() case "8": i18nMonth[time.August] = m.Data() case "9": i18nMonth[time.September] = m.Data() case "10": i18nMonth[time.October] = m.Data() case "11": i18nMonth[time.November] = m.Data() case "12": i18nMonth[time.December] = m.Data() } } switch months.Type { case "abbreviated": calendar.FormatNames.Months.Abbreviated = i18nMonth case "narrow": calendar.FormatNames.Months.Narrow = i18nMonth case "short": calendar.FormatNames.Months.Short = i18nMonth case "wide": calendar.FormatNames.Months.Wide = i18nMonth } } } } if ldmlCar.Days != nil { for _, dayctx := range ldmlCar.Days.DayContext { for _, days := range dayctx.DayWidth { i18nDay := make(i18n.CalendarDayFormatNameValue) for _, d := range days.Day { switch d.Type { case "sun": i18nDay[time.Sunday] = d.Data() case "mon": i18nDay[time.Monday] = d.Data() case "tue": i18nDay[time.Tuesday] = d.Data() case "wed": i18nDay[time.Wednesday] = d.Data() case "thu": i18nDay[time.Thursday] = d.Data() case "fri": i18nDay[time.Friday] = d.Data() case "sat": i18nDay[time.Saturday] = d.Data() } } switch days.Type { case "abbreviated": calendar.FormatNames.Days.Abbreviated = i18nDay case "narrow": calendar.FormatNames.Days.Narrow = i18nDay case "short": calendar.FormatNames.Days.Short = i18nDay case "wide": calendar.FormatNames.Days.Wide = i18nDay } } } } if ldmlCar.DayPeriods != nil { for _, ctx := range ldmlCar.DayPeriods.DayPeriodContext { for _, width := range ctx.DayPeriodWidth { // var i18nPeriod i18n.CalendarPeriodFormatNameValue i18nPeriod := make(i18n.CalendarPeriodFormatNameValue) for _, d := range width.DayPeriod { if _, ok := i18nPeriod[d.Type]; !ok { i18nPeriod[d.Type] = d.Data() } } switch width.Type { case "abbreviated": calendar.FormatNames.Periods.Abbreviated = i18nPeriod case "narrow": calendar.FormatNames.Periods.Narrow = i18nPeriod case "short": calendar.FormatNames.Periods.Short = i18nPeriod case "wide": calendar.FormatNames.Periods.Wide = i18nPeriod } } } // var empty i18n.CalendarPeriodFormatNameValue // if calendar.FormatNames.Periods.Abbreviated == empty { // calendar.FormatNames.Periods.Abbreviated = calendar.FormatNames.Periods.Wide // } } if ldmlCar.Eras != nil { var eras i18n.Eras if ldmlCar.Eras.EraNames != nil && len(ldmlCar.Eras.EraNames.Era) > 0 { eras.BC.Full = ldmlCar.Eras.EraNames.Era[0].Data() } if ldmlCar.Eras.EraAbbr != nil && len(ldmlCar.Eras.EraAbbr.Era) > 0 { eras.BC.Abbrev = ldmlCar.Eras.EraAbbr.Era[0].Data() } if ldmlCar.Eras.EraNarrow != nil && len(ldmlCar.Eras.EraNarrow.Era) > 0 { eras.BC.Narrow = ldmlCar.Eras.EraNarrow.Era[0].Data() } if ldmlCar.Eras.EraNames != nil && len(ldmlCar.Eras.EraNames.Era) > 2 { eras.AD.Full = ldmlCar.Eras.EraNames.Era[2].Data() } if ldmlCar.Eras.EraAbbr != nil && len(ldmlCar.Eras.EraAbbr.Era) > 2 { eras.AD.Abbrev = ldmlCar.Eras.EraAbbr.Era[2].Data() } if ldmlCar.Eras.EraNarrow != nil && len(ldmlCar.Eras.EraNarrow.Era) > 2 { eras.AD.Narrow = ldmlCar.Eras.EraNarrow.Era[2].Data() } calendar.FormatNames.Eras = eras } calendars[loc] = calendar } } for locale := range locs { if !strings.Contains(locale, "_") { continue } calendar := calendars[locale] bString := strings.SplitN(locale, "_", 2) base := bString[0] baseCal := calendars[base] // copy base calendar objects // Dates if calendar.Formats.DateEra.AD.Full == "" { calendar.Formats.DateEra.BC.Full = baseCal.Formats.DateEra.BC.Full calendar.Formats.DateEra.AD.Full = baseCal.Formats.DateEra.AD.Full } if calendar.Formats.DateEra.AD.Long == "" { calendar.Formats.DateEra.BC.Long = baseCal.Formats.DateEra.BC.Long calendar.Formats.DateEra.AD.Long = baseCal.Formats.DateEra.AD.Long } if calendar.Formats.DateEra.AD.Medium == "" { calendar.Formats.DateEra.BC.Medium = baseCal.Formats.DateEra.BC.Medium calendar.Formats.DateEra.AD.Medium = baseCal.Formats.DateEra.AD.Medium } if calendar.Formats.DateEra.AD.Short == "" { calendar.Formats.DateEra.BC.Short = baseCal.Formats.DateEra.BC.Short calendar.Formats.DateEra.AD.Short = baseCal.Formats.DateEra.AD.Short } // times if calendar.Formats.Time.Full == "" { calendar.Formats.Time.Full = baseCal.Formats.Time.Full } if calendar.Formats.Time.Long == "" { calendar.Formats.Time.Long = baseCal.Formats.Time.Long } if calendar.Formats.Time.Medium == "" { calendar.Formats.Time.Medium = baseCal.Formats.Time.Medium } if calendar.Formats.Time.Short == "" { calendar.Formats.Time.Short = baseCal.Formats.Time.Short } // date & times if calendar.Formats.DateTime.Full == "" { calendar.Formats.DateTime.Full = baseCal.Formats.DateTime.Full } if calendar.Formats.DateTime.Long == "" { calendar.Formats.DateTime.Long = baseCal.Formats.DateTime.Long } if calendar.Formats.DateTime.Medium == "" { calendar.Formats.DateTime.Medium = baseCal.Formats.DateTime.Medium } if calendar.Formats.DateTime.Short == "" { calendar.Formats.DateTime.Short = baseCal.Formats.DateTime.Short } // months if calendar.FormatNames.Months.Abbreviated == nil { calendar.FormatNames.Months.Abbreviated = make(i18n.CalendarMonthFormatNameValue) } if calendar.FormatNames.Months.Narrow == nil { calendar.FormatNames.Months.Narrow = make(i18n.CalendarMonthFormatNameValue) } if calendar.FormatNames.Months.Short == nil { calendar.FormatNames.Months.Short = make(i18n.CalendarMonthFormatNameValue) } if calendar.FormatNames.Months.Wide == nil { calendar.FormatNames.Months.Wide = make(i18n.CalendarMonthFormatNameValue) } for k, v := range baseCal.FormatNames.Months.Abbreviated { val, ok := calendar.FormatNames.Months.Abbreviated[k] if !ok { calendar.FormatNames.Months.Abbreviated[k] = v continue } if val == "" { calendar.FormatNames.Months.Abbreviated[k] = v } } for k, v := range baseCal.FormatNames.Months.Narrow { val, ok := calendar.FormatNames.Months.Narrow[k] if !ok { calendar.FormatNames.Months.Narrow[k] = v continue } if val == "" { calendar.FormatNames.Months.Narrow[k] = v } } for k, v := range baseCal.FormatNames.Months.Short { val, ok := calendar.FormatNames.Months.Short[k] if !ok { calendar.FormatNames.Months.Short[k] = v continue } if val == "" { calendar.FormatNames.Months.Short[k] = v } } for k, v := range baseCal.FormatNames.Months.Wide { val, ok := calendar.FormatNames.Months.Wide[k] if !ok { calendar.FormatNames.Months.Wide[k] = v continue } if val == "" { calendar.FormatNames.Months.Wide[k] = v } } // days if calendar.FormatNames.Days.Abbreviated == nil { calendar.FormatNames.Days.Abbreviated = make(i18n.CalendarDayFormatNameValue) } if calendar.FormatNames.Days.Narrow == nil { calendar.FormatNames.Days.Narrow = make(i18n.CalendarDayFormatNameValue) } if calendar.FormatNames.Days.Short == nil { calendar.FormatNames.Days.Short = make(i18n.CalendarDayFormatNameValue) } if calendar.FormatNames.Days.Wide == nil { calendar.FormatNames.Days.Wide = make(i18n.CalendarDayFormatNameValue) } for k, v := range baseCal.FormatNames.Days.Abbreviated { val, ok := calendar.FormatNames.Days.Abbreviated[k] if !ok { calendar.FormatNames.Days.Abbreviated[k] = v continue } if val == "" { calendar.FormatNames.Days.Abbreviated[k] = v } } for k, v := range baseCal.FormatNames.Days.Narrow { val, ok := calendar.FormatNames.Days.Narrow[k] if !ok { calendar.FormatNames.Days.Narrow[k] = v continue } if val == "" { calendar.FormatNames.Days.Narrow[k] = v } } for k, v := range baseCal.FormatNames.Days.Short { val, ok := calendar.FormatNames.Days.Short[k] if !ok { calendar.FormatNames.Days.Short[k] = v continue } if val == "" { calendar.FormatNames.Days.Short[k] = v } } for k, v := range baseCal.FormatNames.Days.Wide { val, ok := calendar.FormatNames.Days.Wide[k] if !ok { calendar.FormatNames.Days.Wide[k] = v continue } if val == "" { calendar.FormatNames.Days.Wide[k] = v } } // periods if calendar.FormatNames.Periods.Abbreviated == nil { calendar.FormatNames.Periods.Abbreviated = make(i18n.CalendarPeriodFormatNameValue) } if calendar.FormatNames.Periods.Narrow == nil { calendar.FormatNames.Periods.Narrow = make(i18n.CalendarPeriodFormatNameValue) } if calendar.FormatNames.Periods.Short == nil { calendar.FormatNames.Periods.Short = make(i18n.CalendarPeriodFormatNameValue) } if calendar.FormatNames.Periods.Wide == nil { calendar.FormatNames.Periods.Wide = make(i18n.CalendarPeriodFormatNameValue) } for k, v := range baseCal.FormatNames.Periods.Abbreviated { val, ok := calendar.FormatNames.Periods.Abbreviated[k] if !ok { calendar.FormatNames.Periods.Abbreviated[k] = v continue } if val == "" { calendar.FormatNames.Periods.Abbreviated[k] = v } } for k, v := range baseCal.FormatNames.Periods.Narrow { val, ok := calendar.FormatNames.Periods.Narrow[k] if !ok { calendar.FormatNames.Periods.Narrow[k] = v continue } if val == "" { calendar.FormatNames.Periods.Narrow[k] = v } } for k, v := range baseCal.FormatNames.Periods.Short { val, ok := calendar.FormatNames.Periods.Short[k] if !ok { calendar.FormatNames.Periods.Short[k] = v continue } if val == "" { calendar.FormatNames.Periods.Short[k] = v } } for k, v := range baseCal.FormatNames.Periods.Wide { val, ok := calendar.FormatNames.Periods.Wide[k] if !ok { calendar.FormatNames.Periods.Wide[k] = v continue } if val == "" { calendar.FormatNames.Periods.Wide[k] = v } } calendars[locale] = calendar number := numbers[locale] baseNum := numbers[base] // symbols if number.Symbols.Decimal == "" { number.Symbols.Decimal = baseNum.Symbols.Decimal } if number.Symbols.Group == "" { number.Symbols.Group = baseNum.Symbols.Group } if number.Symbols.Negative == "" { number.Symbols.Negative = baseNum.Symbols.Negative } if number.Symbols.Percent == "" { number.Symbols.Percent = baseNum.Symbols.Percent } if number.Symbols.PerMille == "" { number.Symbols.PerMille = baseNum.Symbols.PerMille } // formats if number.Formats.Decimal == "" { number.Formats.Decimal = baseNum.Formats.Decimal } if number.Formats.Currency == "" { number.Formats.Currency = baseNum.Formats.Currency } if number.Formats.CurrencyAccounting == "" { number.Formats.CurrencyAccounting = baseNum.Formats.CurrencyAccounting } if number.Formats.Percent == "" { number.Formats.Percent = baseNum.Formats.Percent } // currency for k, v := range baseNum.Currencies { val, ok := number.Currencies[k] if !ok { // number.Currencies[k] = v continue } if val.Currency == "" { val.Currency = v.Currency } if val.DisplayName == "" { val.DisplayName = v.DisplayName } if val.Symbol == "" { val.Symbol = v.Symbol } number.Currencies[k] = val } numbers[locale] = number } var wg sync.WaitGroup wg.Add(len(numbers)) for locale, number := range numbers { go func(locale string, number i18n.Number) { localeLowercase := strings.ToLower(locale) defer func() { wg.Done() }() path := "../../resources/locales/" + locale if _, err := os.Stat(path); err != nil { if err = os.MkdirAll(path, 0777); err != nil { panic(err) } } path += "/" mainFile, err := os.Create(path + "main.go") if err != nil { panic(err) } defer mainFile.Close() calendar := calendars[locale] mainCodes, err := format.Source([]byte(fmt.Sprintf(`package %s import "github.com/go-playground/universal-translator" var locale = &ut.Locale{ Locale: %q, Number: ut.Number{ Symbols: symbols, Formats: formats, Currencies: currencies, }, Calendar: calendar, PluralRule: pluralRule, } func init() { ut.RegisterLocale(locale) } `, locale, locale))) if err != nil { panic(err) } fmt.Fprintf(mainFile, "%s", mainCodes) numberFile, err := os.Create(path + "number.go") if err != nil { panic(err) } defer numberFile.Close() numberCodes, err := format.Source([]byte(fmt.Sprintf(`package %s import "github.com/go-playground/universal-translator" var ( symbols = %#v formats = %#v ) `, locale, number.Symbols, number.Formats))) if err != nil { panic(err) } fmt.Fprintf(numberFile, "%s", numberCodes) currencyFile, err := os.Create(path + "currency.go") if err != nil { panic(err) } defer currencyFile.Close() currencyCodes, err := format.Source([]byte(fmt.Sprintf(`package %s import "github.com/go-playground/universal-translator" var currencies = %# v `, locale, number.Currencies))) if err != nil { panic(err) } fmt.Fprintf(currencyFile, "%s", currencyCodes) calendarFile, err := os.Create(path + "calendar.go") if err != nil { panic(err) } defer calendarFile.Close() calendarCodes, err := format.Source([]byte(fmt.Sprintf(`package %s import "github.com/go-playground/universal-translator" var calendar = %#v `, locale, calendar))) if err != nil { panic(err) } fmt.Fprintf(calendarFile, "%s", calendarCodes) var ok bool pluralCode := "1" pInfo, ok := plurals[localeLowercase] if ok && pInfo.plural != "" { pluralCode = pInfo.plural } pluralFile, err := os.Create(path + "plural.go") if err != nil { panic(err) } defer pluralFile.Close() pluralCodes, err := format.Source([]byte(fmt.Sprintf(`package %s var pluralRule = %q `, locale, pluralCode))) if err != nil { panic(err) } fmt.Fprintf(pluralFile, "%s", pluralCodes) }(locale, number) } wg.Wait() // TODO: make switch with all of the locales + function to return new! localesFile, err := os.Create("../../resources/locales/all.go") if err != nil { panic(err) } defer localesFile.Close() tmpl, err := template.New("").Parse(`package locales // Imports for all locales import ( {{range $locale, $_ := .}}// Locale "{{$locale}}" import that automatically registers itslef with the universal-translator package _ "github.com/go-playground/universal-translator/resources/locales/{{$locale}}" {{end}}) `) if err != nil { panic(err) } var buf bytes.Buffer if err := tmpl.Execute(&buf, locs); err != nil { panic(err) } allCodes, err := format.Source(buf.Bytes()) if err != nil { panic(err) } _, err = localesFile.Write(allCodes) if err != nil { panic(err) } }