| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614 |
- package main
- import (
- "fmt"
- "log"
- "os"
- "os/exec"
- "regexp"
- "sort"
- "strconv"
- "strings"
- "github.com/go-playground/locales"
- "golang.org/x/text/unicode/cldr"
- "text/template"
- )
- const (
- locDir = "../%s"
- locFilename = locDir + "/%s.go"
- )
- var (
- tfuncs = template.FuncMap{
- "is_multibyte": func(s string) bool {
- return len([]byte(s)) > 1
- },
- "reverse_bytes": func(s string) string {
- b := make([]byte, 0, 8)
- for j := len(s) - 1; j >= 0; j-- {
- b = append(b, s[j])
- }
- return fmt.Sprintf("%#v", b)
- },
- "byte_count": func(s ...string) string {
- var count int
- for i := 0; i < len(s); i++ {
- count += len([]byte(s[i]))
- }
- return strconv.Itoa(count)
- },
- }
- prVarFuncs = map[string]string{
- "n": "n := math.Abs(num)\n",
- "i": "i := int64(n)\n",
- // "v": "v := ...", // inherently available as argument
- "w": "w := locales.W(n, v)\n",
- "f": "f := locales.F(n, v)\n",
- "t": "t := locales.T(n, v)\n",
- }
- translators = make(map[string]*translator)
- baseTranslators = make(map[string]*translator)
- globalCurrenciesMap = make(map[string]struct{}) // ["USD"] = "$" currency code, just all currencies for mapping to enum
- globCurrencyIdxMap = make(map[string]int) // ["USD"] = 0
- globalCurrencies = make([]string, 0, 100) // array of currency codes index maps to enum
- tmpl *template.Template
- nModRegex = regexp.MustCompile("(n%[0-9]+)")
- iModRegex = regexp.MustCompile("(i%[0-9]+)")
- wModRegex = regexp.MustCompile("(w%[0-9]+)")
- fModRegex = regexp.MustCompile("(f%[0-9]+)")
- tModRegex = regexp.MustCompile("(t%[0-9]+)")
- groupLenRegex = regexp.MustCompile(",([0-9#]+)\\.")
- groupLenPercentRegex = regexp.MustCompile(",([0-9#]+)$")
- secondaryGroupLenRegex = regexp.MustCompile(",([0-9#]+),")
- requiredNumRegex = regexp.MustCompile("([0-9]+)\\.")
- requiredDecimalRegex = regexp.MustCompile("\\.([0-9]+)")
- )
- type translator struct {
- Locale string
- BaseLocale string
- Plurals string
- CardinalFunc string
- PluralsOrdinal string
- OrdinalFunc string
- PluralsRange string
- RangeFunc string
- Decimal string
- Group string
- Minus string
- Percent string
- PerMille string
- TimeSeparator string
- Infinity string
- Currencies string
- // FmtNumber vars
- FmtNumberExists bool
- FmtNumberGroupLen int
- FmtNumberSecondaryGroupLen int
- FmtNumberMinDecimalLen int
- // FmtPercent vars
- FmtPercentExists bool
- FmtPercentGroupLen int
- FmtPercentSecondaryGroupLen int
- FmtPercentMinDecimalLen int
- FmtPercentPrefix string
- FmtPercentSuffix string
- FmtPercentInPrefix bool
- FmtPercentLeft bool
- // FmtCurrency vars
- FmtCurrencyExists bool
- FmtCurrencyGroupLen int
- FmtCurrencySecondaryGroupLen int
- FmtCurrencyMinDecimalLen int
- FmtCurrencyPrefix string
- FmtCurrencySuffix string
- FmtCurrencyInPrefix bool
- FmtCurrencyLeft bool
- FmtCurrencyNegativeExists bool
- FmtCurrencyNegativePrefix string
- FmtCurrencyNegativeSuffix string
- FmtCurrencyNegativeInPrefix bool
- FmtCurrencyNegativeLeft bool
- // Date & Time
- FmtCalendarExists bool
- FmtMonthsAbbreviated string
- FmtMonthsNarrow string
- FmtMonthsWide string
- FmtDaysAbbreviated string
- FmtDaysNarrow string
- FmtDaysShort string
- FmtDaysWide string
- FmtPeriodsAbbreviated string
- FmtPeriodsNarrow string
- FmtPeriodsShort string
- FmtPeriodsWide string
- FmtErasAbbreviated string
- FmtErasNarrow string
- FmtErasWide string
- FmtTimezones string
- // calculation only fields below this point...
- DecimalNumberFormat string
- PercentNumberFormat string
- CurrencyNumberFormat string
- NegativeCurrencyNumberFormat string
- // Dates
- FmtDateFull string
- FmtDateLong string
- FmtDateMedium string
- FmtDateShort string
- // Times
- FmtTimeFull string
- FmtTimeLong string
- FmtTimeMedium string
- FmtTimeShort string
- // timezones per locale by type
- timezones map[string]*zoneAbbrev // key = type eg. America_Eastern zone Abbrev will be long form eg. Eastern Standard Time, Pacific Standard Time.....
- }
- type zoneAbbrev struct {
- standard string
- daylight string
- }
- var timezones = map[string]*zoneAbbrev{} // key = type eg. America_Eastern zone Abbrev eg. EST & EDT
- func main() {
- var err error
- // load template
- tmpl, err = template.New("all").Funcs(tfuncs).ParseGlob("*.tmpl")
- if err != nil {
- log.Fatal(err)
- }
- // load CLDR recourses
- var decoder cldr.Decoder
- cldr, err := decoder.DecodePath("data/core")
- if err != nil {
- panic(err)
- }
- preProcess(cldr)
- postProcess(cldr)
- var currencies string
- for i, curr := range globalCurrencies {
- if i == 0 {
- currencies = curr + " Type = iota\n"
- continue
- }
- currencies += curr + "\n"
- }
- if err = os.MkdirAll(fmt.Sprintf(locDir, "currency"), 0777); err != nil {
- log.Fatal(err)
- }
- filename := fmt.Sprintf(locFilename, "currency", "currency")
- output, err := os.Create(filename)
- if err != nil {
- log.Fatal(err)
- }
- defer output.Close()
- if err := tmpl.ExecuteTemplate(output, "currencies", currencies); err != nil {
- log.Fatal(err)
- }
- output.Close()
- // after file written run gofmt on file to ensure best formatting
- cmd := exec.Command("goimports", "-w", filename)
- if err = cmd.Run(); err != nil {
- log.Panic(err)
- }
- cmd = exec.Command("gofmt", "-s", "-w", filename)
- if err = cmd.Run(); err != nil {
- log.Panic(err)
- }
- var locMap string
- for _, trans := range translators {
- locMap += `"` + trans.Locale + `" : ` + trans.Locale + `.New,
- `
- fmt.Println("Writing Data:", trans.Locale)
- if err = os.MkdirAll(fmt.Sprintf(locDir, trans.Locale), 0777); err != nil {
- log.Fatal(err)
- }
- filename = fmt.Sprintf(locFilename, trans.Locale, trans.Locale)
- output, err := os.Create(filename)
- if err != nil {
- log.Fatal(err)
- }
- defer output.Close()
- if err := tmpl.ExecuteTemplate(output, "translator", trans); err != nil {
- log.Fatal(err)
- }
- output.Close()
- // after file written run gofmt on file to ensure best formatting
- cmd := exec.Command("goimports", "-w", filename)
- if err = cmd.Run(); err != nil {
- log.Panic(err)
- }
- // this simplifies some syntax that I can;t find an option for in goimports, namely '-s'
- cmd = exec.Command("gofmt", "-s", "-w", filename)
- if err = cmd.Run(); err != nil {
- log.Panic(err)
- }
- }
- }
- func postProcess(cldr *cldr.CLDR) {
- for _, v := range timezones {
- // no DST
- if len(v.daylight) == 0 {
- v.daylight = v.standard
- }
- }
- var base *translator
- var found bool
- for _, trans := range translators {
- fmt.Println("Post Processing:", trans.Locale)
- // cardinal plural rules
- trans.CardinalFunc, trans.Plurals = parseCardinalPluralRuleFunc(cldr, trans.BaseLocale)
- //ordinal plural rules
- trans.OrdinalFunc, trans.PluralsOrdinal = parseOrdinalPluralRuleFunc(cldr, trans.BaseLocale)
- // range plural rules
- trans.RangeFunc, trans.PluralsRange = parseRangePluralRuleFunc(cldr, trans.BaseLocale)
- // ignore base locales
- if trans.BaseLocale == trans.Locale {
- found = false
- } else {
- base, found = baseTranslators[trans.BaseLocale]
- }
- // Numbers
- if len(trans.Decimal) == 0 {
- if found {
- trans.Decimal = base.Decimal
- }
- if len(trans.Decimal) == 0 {
- trans.Decimal = ""
- }
- }
- if len(trans.Group) == 0 {
- if found {
- trans.Group = base.Group
- }
- if len(trans.Group) == 0 {
- trans.Group = ""
- }
- }
- if len(trans.Minus) == 0 {
- if found {
- trans.Minus = base.Minus
- }
- if len(trans.Minus) == 0 {
- trans.Minus = ""
- }
- }
- if len(trans.Percent) == 0 {
- if found {
- trans.Percent = base.Percent
- }
- if len(trans.Percent) == 0 {
- trans.Percent = ""
- }
- }
- if len(trans.PerMille) == 0 {
- if found {
- trans.PerMille = base.PerMille
- }
- if len(trans.PerMille) == 0 {
- trans.PerMille = ""
- }
- }
- if len(trans.TimeSeparator) == 0 && found {
- trans.TimeSeparator = base.TimeSeparator
- }
- if len(trans.Infinity) == 0 && found {
- trans.Infinity = base.Infinity
- }
- // Currency
- // number values
- if len(trans.DecimalNumberFormat) == 0 && found {
- trans.DecimalNumberFormat = base.DecimalNumberFormat
- }
- if len(trans.PercentNumberFormat) == 0 && found {
- trans.PercentNumberFormat = base.PercentNumberFormat
- }
- if len(trans.CurrencyNumberFormat) == 0 && found {
- trans.CurrencyNumberFormat = base.CurrencyNumberFormat
- }
- if len(trans.NegativeCurrencyNumberFormat) == 0 && found {
- trans.NegativeCurrencyNumberFormat = base.NegativeCurrencyNumberFormat
- }
- // date values
- if len(trans.FmtDateFull) == 0 && found {
- trans.FmtDateFull = base.FmtDateFull
- }
- if len(trans.FmtDateLong) == 0 && found {
- trans.FmtDateLong = base.FmtDateLong
- }
- if len(trans.FmtDateMedium) == 0 && found {
- trans.FmtDateMedium = base.FmtDateMedium
- }
- if len(trans.FmtDateShort) == 0 && found {
- trans.FmtDateShort = base.FmtDateShort
- }
- // time values
- if len(trans.FmtTimeFull) == 0 && found {
- trans.FmtTimeFull = base.FmtTimeFull
- }
- if len(trans.FmtTimeLong) == 0 && found {
- trans.FmtTimeLong = base.FmtTimeLong
- }
- if len(trans.FmtTimeMedium) == 0 && found {
- trans.FmtTimeMedium = base.FmtTimeMedium
- }
- if len(trans.FmtTimeShort) == 0 && found {
- trans.FmtTimeShort = base.FmtTimeShort
- }
- // month values
- if len(trans.FmtMonthsAbbreviated) == 0 && found {
- trans.FmtMonthsAbbreviated = base.FmtMonthsAbbreviated
- }
- if len(trans.FmtMonthsNarrow) == 0 && found {
- trans.FmtMonthsNarrow = base.FmtMonthsNarrow
- }
- if len(trans.FmtMonthsWide) == 0 && found {
- trans.FmtMonthsWide = base.FmtMonthsWide
- }
- // day values
- if len(trans.FmtDaysAbbreviated) == 0 && found {
- trans.FmtDaysAbbreviated = base.FmtDaysAbbreviated
- }
- if len(trans.FmtDaysNarrow) == 0 && found {
- trans.FmtDaysNarrow = base.FmtDaysNarrow
- }
- if len(trans.FmtDaysShort) == 0 && found {
- trans.FmtDaysShort = base.FmtDaysShort
- }
- if len(trans.FmtDaysWide) == 0 && found {
- trans.FmtDaysWide = base.FmtDaysWide
- }
- // period values
- if len(trans.FmtPeriodsAbbreviated) == 0 && found {
- trans.FmtPeriodsAbbreviated = base.FmtPeriodsAbbreviated
- }
- if len(trans.FmtPeriodsNarrow) == 0 && found {
- trans.FmtPeriodsNarrow = base.FmtPeriodsNarrow
- }
- if len(trans.FmtPeriodsShort) == 0 && found {
- trans.FmtPeriodsShort = base.FmtPeriodsShort
- }
- if len(trans.FmtPeriodsWide) == 0 && found {
- trans.FmtPeriodsWide = base.FmtPeriodsWide
- }
- // era values
- if len(trans.FmtErasAbbreviated) == 0 && found {
- trans.FmtErasAbbreviated = base.FmtErasAbbreviated
- }
- if len(trans.FmtErasNarrow) == 0 && found {
- trans.FmtErasNarrow = base.FmtErasNarrow
- }
- if len(trans.FmtErasWide) == 0 && found {
- trans.FmtErasWide = base.FmtErasWide
- }
- ldml := cldr.RawLDML(trans.Locale)
- currencies := make([]string, len(globalCurrencies), len(globalCurrencies))
- var kval string
- // add a space for readability for non locale specific currencies eg. -USD<space>10,356.45
- for k, v := range globCurrencyIdxMap {
- kval = k
- if kval[:len(kval)-1] != " " {
- kval += " "
- }
- currencies[v] = kval
- }
- // some just have no data...
- if ldml.Numbers != nil {
- if ldml.Numbers.Currencies != nil {
- for _, currency := range ldml.Numbers.Currencies.Currency {
- if len(currency.Symbol) == 0 {
- continue
- }
- if len(currency.Symbol[0].Data()) == 0 {
- continue
- }
- if len(currency.Type) == 0 {
- continue
- }
- currencies[globCurrencyIdxMap[currency.Type]] = currency.Symbol[0].Data()
- }
- }
- }
- trans.Currencies = fmt.Sprintf("%#v", currencies)
- // timezones
- if (trans.timezones == nil || len(trans.timezones) == 0) && found {
- trans.timezones = base.timezones
- }
- // make sure all base timezones are part of sub locale timezones
- if found {
- var ok bool
- for k, v := range base.timezones {
- if _, ok = trans.timezones[k]; ok {
- continue
- }
- trans.timezones[k] = v
- }
- }
- parseDecimalNumberFormat(trans)
- parsePercentNumberFormat(trans)
- parseCurrencyNumberFormat(trans)
- }
- for _, trans := range translators {
- fmt.Println("Final Processing:", trans.Locale)
- // if it's still nill.....
- if trans.timezones == nil {
- trans.timezones = make(map[string]*zoneAbbrev)
- }
- tz := make(map[string]string) // key = abbrev locale eg. EST, EDT, MST, PST... value = long locale eg. Eastern Standard Time, Pacific Time.....
- for k, v := range timezones {
- ttz, ok := trans.timezones[k]
- if !ok {
- ttz = v
- trans.timezones[k] = v
- }
- tz[v.standard] = ttz.standard
- tz[v.daylight] = ttz.daylight
- }
- trans.FmtTimezones = fmt.Sprintf("%#v", tz)
- if len(trans.TimeSeparator) == 0 {
- trans.TimeSeparator = ":"
- }
- trans.FmtDateShort, trans.FmtDateMedium, trans.FmtDateLong, trans.FmtDateFull = parseDateFormats(trans, trans.FmtDateShort, trans.FmtDateMedium, trans.FmtDateLong, trans.FmtDateFull)
- trans.FmtTimeShort, trans.FmtTimeMedium, trans.FmtTimeLong, trans.FmtTimeFull = parseDateFormats(trans, trans.FmtTimeShort, trans.FmtTimeMedium, trans.FmtTimeLong, trans.FmtTimeFull)
- }
- }
- // preprocesses maps, array etc... just requires multiple passes no choice....
- func preProcess(cldrVar *cldr.CLDR) {
- for _, l := range cldrVar.Locales() {
- fmt.Println("Pre Processing:", l)
- split := strings.SplitN(l, "_", 2)
- baseLocale := split[0]
- trans := &translator{
- Locale: l,
- BaseLocale: baseLocale,
- }
- // if is a base locale
- if len(split) == 1 {
- baseTranslators[baseLocale] = trans
- }
- translators[l] = trans
- // get number, currency and datetime symbols
- // number values
- ldml := cldrVar.RawLDML(l)
- // some just have no data...
- if ldml.Numbers != nil {
- if len(ldml.Numbers.Symbols) > 0 {
- symbol := ldml.Numbers.Symbols[0]
- if len(symbol.Decimal) > 0 {
- trans.Decimal = symbol.Decimal[0].Data()
- }
- if len(symbol.Group) > 0 {
- trans.Group = symbol.Group[0].Data()
- }
- if len(symbol.MinusSign) > 0 {
- trans.Minus = symbol.MinusSign[0].Data()
- }
- if len(symbol.PercentSign) > 0 {
- trans.Percent = symbol.PercentSign[0].Data()
- }
- if len(symbol.PerMille) > 0 {
- trans.PerMille = symbol.PerMille[0].Data()
- }
- if len(symbol.TimeSeparator) > 0 {
- trans.TimeSeparator = symbol.TimeSeparator[0].Data()
- }
- if len(symbol.Infinity) > 0 {
- trans.Infinity = symbol.Infinity[0].Data()
- }
- }
- if ldml.Numbers.Currencies != nil {
- for _, currency := range ldml.Numbers.Currencies.Currency {
- if len(strings.TrimSpace(currency.Type)) == 0 {
- continue
- }
- globalCurrenciesMap[currency.Type] = struct{}{}
- }
- }
- if len(ldml.Numbers.DecimalFormats) > 0 && len(ldml.Numbers.DecimalFormats[0].DecimalFormatLength) > 0 {
- for _, dfl := range ldml.Numbers.DecimalFormats[0].DecimalFormatLength {
- if len(dfl.Type) == 0 {
- trans.DecimalNumberFormat = dfl.DecimalFormat[0].Pattern[0].Data()
- break
- }
- }
- }
- if len(ldml.Numbers.PercentFormats) > 0 && len(ldml.Numbers.PercentFormats[0].PercentFormatLength) > 0 {
- for _, dfl := range ldml.Numbers.PercentFormats[0].PercentFormatLength {
- if len(dfl.Type) == 0 {
- trans.PercentNumberFormat = dfl.PercentFormat[0].Pattern[0].Data()
- break
- }
- }
- }
- if len(ldml.Numbers.CurrencyFormats) > 0 && len(ldml.Numbers.CurrencyFormats[0].CurrencyFormatLength) > 0 {
- if len(ldml.Numbers.CurrencyFormats[0].CurrencyFormatLength[0].CurrencyFormat) > 1 {
- split := strings.SplitN(ldml.Numbers.CurrencyFormats[0].CurrencyFormatLength[0].CurrencyFormat[1].Pattern[0].Data(), ";", 2)
- trans.CurrencyNumberFormat = split[0]
- if len(split) > 1 && len(split[1]) > 0 {
- trans.NegativeCurrencyNumberFormat = split[1]
- } else {
- trans.NegativeCurrencyNumberFormat = trans.CurrencyNumberFormat
- }
- } else {
- trans.CurrencyNumberFormat = ldml.Numbers.CurrencyFormats[0].CurrencyFormatLength[0].CurrencyFormat[0].Pattern[0].Data()
- trans.NegativeCurrencyNumberFormat = trans.CurrencyNumberFormat
- }
- }
- }
- if ldml.Dates != nil {
- if ldml.Dates.TimeZoneNames != nil {
- for _, zone := range ldml.Dates.TimeZoneNames.Metazone {
- for _, short := range zone.Short {
- if len(short.Standard) > 0 {
- za, ok := timezones[zone.Type]
- if !ok {
- za = new(zoneAbbrev)
- timezones[zone.Type] = za
- }
- za.standard = short.Standard[0].Data()
- }
- if len(short.Daylight) > 0 {
- za, ok := timezones[zone.Type]
- if !ok {
- za = new(zoneAbbrev)
- timezones[zone.Type] = za
- }
- za.daylight = short.Daylight[0].Data()
- }
- }
- for _, long := range zone.Long {
- if trans.timezones == nil {
- trans.timezones = make(map[string]*zoneAbbrev)
- }
- if len(long.Standard) > 0 {
- za, ok := trans.timezones[zone.Type]
- if !ok {
- za = new(zoneAbbrev)
- trans.timezones[zone.Type] = za
- }
- za.standard = long.Standard[0].Data()
- }
- za, ok := trans.timezones[zone.Type]
- if !ok {
- za = new(zoneAbbrev)
- trans.timezones[zone.Type] = za
- }
- if len(long.Daylight) > 0 {
- za.daylight = long.Daylight[0].Data()
- } else {
- za.daylight = za.standard
- }
- }
- }
- }
- if ldml.Dates.Calendars != nil {
- var calendar *cldr.Calendar
- for _, cal := range ldml.Dates.Calendars.Calendar {
- if cal.Type == "gregorian" {
- calendar = cal
- }
- }
- if calendar != nil {
- if calendar.DateFormats != nil {
- for _, datefmt := range calendar.DateFormats.DateFormatLength {
- switch datefmt.Type {
- case "full":
- trans.FmtDateFull = datefmt.DateFormat[0].Pattern[0].Data()
- case "long":
- trans.FmtDateLong = datefmt.DateFormat[0].Pattern[0].Data()
- case "medium":
- trans.FmtDateMedium = datefmt.DateFormat[0].Pattern[0].Data()
- case "short":
- trans.FmtDateShort = datefmt.DateFormat[0].Pattern[0].Data()
- }
- }
- }
- if calendar.TimeFormats != nil {
- for _, datefmt := range calendar.TimeFormats.TimeFormatLength {
- switch datefmt.Type {
- case "full":
- trans.FmtTimeFull = datefmt.TimeFormat[0].Pattern[0].Data()
- case "long":
- trans.FmtTimeLong = datefmt.TimeFormat[0].Pattern[0].Data()
- case "medium":
- trans.FmtTimeMedium = datefmt.TimeFormat[0].Pattern[0].Data()
- case "short":
- trans.FmtTimeShort = datefmt.TimeFormat[0].Pattern[0].Data()
- }
- }
- }
- if calendar.Months != nil {
- // month context starts at 'format', but there is also has 'stand-alone'
- // I'm making the decision to use the 'stand-alone' if, and only if,
- // the value does not exist in the 'format' month context
- var abbrSet, narrSet, wideSet bool
- for _, monthctx := range calendar.Months.MonthContext {
- for _, months := range monthctx.MonthWidth {
- var monthData []string
- for _, m := range months.Month {
- if len(m.Data()) == 0 {
- continue
- }
- switch m.Type {
- case "1":
- monthData = append(monthData, m.Data())
- case "2":
- monthData = append(monthData, m.Data())
- case "3":
- monthData = append(monthData, m.Data())
- case "4":
- monthData = append(monthData, m.Data())
- case "5":
- monthData = append(monthData, m.Data())
- case "6":
- monthData = append(monthData, m.Data())
- case "7":
- monthData = append(monthData, m.Data())
- case "8":
- monthData = append(monthData, m.Data())
- case "9":
- monthData = append(monthData, m.Data())
- case "10":
- monthData = append(monthData, m.Data())
- case "11":
- monthData = append(monthData, m.Data())
- case "12":
- monthData = append(monthData, m.Data())
- }
- }
- if len(monthData) > 0 {
- // making array indexes line up with month values
- // so I'll have an extra empty value, it's way faster
- // than a switch over all type values...
- monthData = append(make([]string, 1, len(monthData)+1), monthData...)
- switch months.Type {
- case "abbreviated":
- if !abbrSet {
- abbrSet = true
- trans.FmtMonthsAbbreviated = fmt.Sprintf("%#v", monthData)
- }
- case "narrow":
- if !narrSet {
- narrSet = true
- trans.FmtMonthsNarrow = fmt.Sprintf("%#v", monthData)
- }
- case "wide":
- if !wideSet {
- wideSet = true
- trans.FmtMonthsWide = fmt.Sprintf("%#v", monthData)
- }
- }
- }
- }
- }
- }
- if calendar.Days != nil {
- // day context starts at 'format', but there is also has 'stand-alone'
- // I'm making the decision to use the 'stand-alone' if, and only if,
- // the value does not exist in the 'format' day context
- var abbrSet, narrSet, shortSet, wideSet bool
- for _, dayctx := range calendar.Days.DayContext {
- for _, days := range dayctx.DayWidth {
- var dayData []string
- for _, d := range days.Day {
- switch d.Type {
- case "sun":
- dayData = append(dayData, d.Data())
- case "mon":
- dayData = append(dayData, d.Data())
- case "tue":
- dayData = append(dayData, d.Data())
- case "wed":
- dayData = append(dayData, d.Data())
- case "thu":
- dayData = append(dayData, d.Data())
- case "fri":
- dayData = append(dayData, d.Data())
- case "sat":
- dayData = append(dayData, d.Data())
- }
- }
- if len(dayData) > 0 {
- switch days.Type {
- case "abbreviated":
- if !abbrSet {
- abbrSet = true
- trans.FmtDaysAbbreviated = fmt.Sprintf("%#v", dayData)
- }
- case "narrow":
- if !narrSet {
- narrSet = true
- trans.FmtDaysNarrow = fmt.Sprintf("%#v", dayData)
- }
- case "short":
- if !shortSet {
- shortSet = true
- trans.FmtDaysShort = fmt.Sprintf("%#v", dayData)
- }
- case "wide":
- if !wideSet {
- wideSet = true
- trans.FmtDaysWide = fmt.Sprintf("%#v", dayData)
- }
- }
- }
- }
- }
- }
- if calendar.DayPeriods != nil {
- // day periods context starts at 'format', but there is also has 'stand-alone'
- // I'm making the decision to use the 'stand-alone' if, and only if,
- // the value does not exist in the 'format' day period context
- var abbrSet, narrSet, shortSet, wideSet bool
- for _, ctx := range calendar.DayPeriods.DayPeriodContext {
- for _, width := range ctx.DayPeriodWidth {
- // [0] = AM
- // [0] = PM
- ampm := make([]string, 2, 2)
- for _, d := range width.DayPeriod {
- if d.Type == "am" {
- ampm[0] = d.Data()
- continue
- }
- if d.Type == "pm" {
- ampm[1] = d.Data()
- }
- }
- switch width.Type {
- case "abbreviated":
- if !abbrSet {
- abbrSet = true
- trans.FmtPeriodsAbbreviated = fmt.Sprintf("%#v", ampm)
- }
- case "narrow":
- if !narrSet {
- narrSet = true
- trans.FmtPeriodsNarrow = fmt.Sprintf("%#v", ampm)
- }
- case "short":
- if !shortSet {
- shortSet = true
- trans.FmtPeriodsShort = fmt.Sprintf("%#v", ampm)
- }
- case "wide":
- if !wideSet {
- wideSet = true
- trans.FmtPeriodsWide = fmt.Sprintf("%#v", ampm)
- }
- }
- }
- }
- }
- if calendar.Eras != nil {
- // [0] = BC
- // [0] = AD
- abbrev := make([]string, 2, 2)
- narr := make([]string, 2, 2)
- wide := make([]string, 2, 2)
- if calendar.Eras.EraAbbr != nil {
- if len(calendar.Eras.EraAbbr.Era) == 4 {
- abbrev[0] = calendar.Eras.EraAbbr.Era[0].Data()
- abbrev[1] = calendar.Eras.EraAbbr.Era[2].Data()
- } else if len(calendar.Eras.EraAbbr.Era) == 2 {
- abbrev[0] = calendar.Eras.EraAbbr.Era[0].Data()
- abbrev[1] = calendar.Eras.EraAbbr.Era[1].Data()
- }
- }
- if calendar.Eras.EraNarrow != nil {
- if len(calendar.Eras.EraNarrow.Era) == 4 {
- narr[0] = calendar.Eras.EraNarrow.Era[0].Data()
- narr[1] = calendar.Eras.EraNarrow.Era[2].Data()
- } else if len(calendar.Eras.EraNarrow.Era) == 2 {
- narr[0] = calendar.Eras.EraNarrow.Era[0].Data()
- narr[1] = calendar.Eras.EraNarrow.Era[1].Data()
- }
- }
- if calendar.Eras.EraNames != nil {
- if len(calendar.Eras.EraNames.Era) == 4 {
- wide[0] = calendar.Eras.EraNames.Era[0].Data()
- wide[1] = calendar.Eras.EraNames.Era[2].Data()
- } else if len(calendar.Eras.EraNames.Era) == 2 {
- wide[0] = calendar.Eras.EraNames.Era[0].Data()
- wide[1] = calendar.Eras.EraNames.Era[1].Data()
- }
- }
- trans.FmtErasAbbreviated = fmt.Sprintf("%#v", abbrev)
- trans.FmtErasNarrow = fmt.Sprintf("%#v", narr)
- trans.FmtErasWide = fmt.Sprintf("%#v", wide)
- }
- }
- }
- }
- }
- for k := range globalCurrenciesMap {
- globalCurrencies = append(globalCurrencies, k)
- }
- sort.Strings(globalCurrencies)
- for i, loc := range globalCurrencies {
- globCurrencyIdxMap[loc] = i
- }
- }
- func parseDateFormats(trans *translator, shortFormat, mediumFormat, longFormat, fullFormat string) (short, medium, long, full string) {
- // Short Date Parsing
- short = parseDateTimeFormat(trans.BaseLocale, shortFormat, 2)
- medium = parseDateTimeFormat(trans.BaseLocale, mediumFormat, 2)
- long = parseDateTimeFormat(trans.BaseLocale, longFormat, 1)
- full = parseDateTimeFormat(trans.BaseLocale, fullFormat, 0)
- // End Short Data Parsing
- return
- }
- func parseDateTimeFormat(baseLocale, format string, eraScore uint8) (results string) {
- // rules:
- // y = four digit year
- // yy = two digit year
- // var b []byte
- var inConstantText bool
- var start int
- for i := 0; i < len(format); i++ {
- switch format[i] {
- // time separator
- case ':':
- if inConstantText {
- inConstantText = false
- results += "b = append(b, " + fmt.Sprintf("%#v", []byte(format[start:i])) + "...)\n"
- }
- results += "b = append(b, " + baseLocale + ".timeSeparator...)"
- case '\'':
- i++
- // peek to see if ''
- if len(format) != i && format[i] == '\'' {
- if inConstantText {
- inConstantText = false
- results += "b = append(b, " + fmt.Sprintf("%#v", []byte(format[start:i-1])) + "...)\n"
- } else {
- inConstantText = true
- start = i - 1
- }
- continue
- }
- // not '' so whatever comes between '' is constant
- if len(format) != i {
- if inConstantText {
- // inContantText = false // gonna put us right back in so not setting...
- results += "b = append(b, " + fmt.Sprintf("%#v", []byte(format[start:i-2])) + "...)\n"
- start = i - 1
- }
- inConstantText = true
- // advance i to the next single quote + 1
- for ; i < len(format); i++ {
- if format[i] == '\'' {
- // i++
- break
- }
- }
- }
- // 24 hour
- case 'H':
- if inConstantText {
- inConstantText = false
- results += "b = append(b, " + fmt.Sprintf("%#v", []byte(format[start:i])) + "...)\n"
- }
- // peek
- // two digit hour required?
- if len(format) != i+1 && format[i+1] == 'H' {
- i++
- results += `
- if t.Hour() < 10 {
- b = append(b, '0')
- }
- `
- }
- results += "b = strconv.AppendInt(b, int64(t.Hour()), 10)\n"
- // hour
- case 'h':
- if inConstantText {
- inConstantText = false
- results += "b = append(b, " + fmt.Sprintf("%#v", []byte(format[start:i])) + "...)\n"
- }
- results += `
- h := t.Hour()
- if h > 12 {
- h -= 12
- }
- `
- // peek
- // two digit hour required?
- if len(format) != i+1 && format[i+1] == 'h' {
- i++
- results += `
- if h < 10 {
- b = append(b, '0')
- }
- `
- }
- results += "b = strconv.AppendInt(b, int64(h), 10)\n"
- // minute
- case 'm':
- if inConstantText {
- inConstantText = false
- results += "b = append(b, " + fmt.Sprintf("%#v", []byte(format[start:i])) + "...)\n"
- }
- // peek
- // two digit minute required?
- if len(format) != i+1 && format[i+1] == 'm' {
- i++
- results += `
- if t.Minute() < 10 {
- b = append(b, '0')
- }
- `
- }
- results += "b = strconv.AppendInt(b, int64(t.Minute()), 10)\n"
- // second
- case 's':
- if inConstantText {
- inConstantText = false
- results += "b = append(b, " + fmt.Sprintf("%#v", []byte(format[start:i])) + "...)\n"
- }
- // peek
- // two digit minute required?
- if len(format) != i+1 && format[i+1] == 's' {
- i++
- results += `
- if t.Second() < 10 {
- b = append(b, '0')
- }
- `
- }
- results += "b = strconv.AppendInt(b, int64(t.Second()), 10)\n"
- // day period
- case 'a':
- if inConstantText {
- inConstantText = false
- results += "b = append(b, " + fmt.Sprintf("%#v", []byte(format[start:i])) + "...)\n"
- }
- // only used with 'h', patterns should not contains 'a' without 'h' so not checking
- // choosing to use abbreviated, didn't see any rules about which should be used with which
- // date format....
- results += `
- if t.Hour() < 12 {
- b = append(b, ` + baseLocale + `.periodsAbbreviated[0]...)
- } else {
- b = append(b, ` + baseLocale + `.periodsAbbreviated[1]...)
- }
- `
- // timezone
- case 'z', 'v':
- if inConstantText {
- inConstantText = false
- results += "b = append(b, " + fmt.Sprintf("%#v", []byte(format[start:i])) + "...)\n"
- }
- // consume multiple, only handling Abbrev tz from time.Time for the moment...
- var count int
- if format[i] == 'z' {
- for j := i; j < len(format); j++ {
- if format[j] == 'z' {
- count++
- } else {
- break
- }
- }
- }
- if format[i] == 'v' {
- for j := i; j < len(format); j++ {
- if format[j] == 'v' {
- count++
- } else {
- break
- }
- }
- }
- i += count - 1
- // using the timezone on the Go time object, eg. EST, EDT, MST.....
- if count < 4 {
- results += `
- tz, _ := t.Zone()
- b = append(b, tz...)
- `
- } else {
- results += `
- tz, _ := t.Zone()
- if btz, ok := ` + baseLocale + `.timezones[tz]; ok {
- b = append(b, btz...)
- } else {
- b = append(b, tz...)
- }
- `
- }
- // day
- case 'd':
- if inConstantText {
- inConstantText = false
- results += "b = append(b, " + fmt.Sprintf("%#v", []byte(format[start:i])) + "...)\n"
- }
- // peek
- // two digit day required?
- if len(format) != i+1 && format[i+1] == 'd' {
- i++
- results += `
- if t.Day() < 10 {
- b = append(b, '0')
- }
- `
- }
- results += "b = strconv.AppendInt(b, int64(t.Day()), 10)\n"
- // month
- case 'M':
- if inConstantText {
- inConstantText = false
- results += "b = append(b, " + fmt.Sprintf("%#v", []byte(format[start:i])) + "...)\n"
- }
- var count int
- // count # of M's
- for j := i; j < len(format); j++ {
- if format[j] == 'M' {
- count++
- } else {
- break
- }
- }
- switch count {
- // Numeric form, at least 1 digit
- case 1:
- results += "b = strconv.AppendInt(b, int64(t.Month()), 10)\n"
- // Number form, at least 2 digits (padding with 0)
- case 2:
- results += `
- if t.Month() < 10 {
- b = append(b, '0')
- }
-
- b = strconv.AppendInt(b, int64(t.Month()), 10)
- `
- // Abbreviated form
- case 3:
- results += "b = append(b, " + baseLocale + ".monthsAbbreviated[t.Month()]...)\n"
- // Full/Wide form
- case 4:
- results += "b = append(b, " + baseLocale + ".monthsWide[t.Month()]...)\n"
- // Narrow form - only used in where context makes it clear, such as headers in a calendar.
- // Should be one character wherever possible.
- case 5:
- results += "b = append(b, " + baseLocale + ".monthsNarrow[t.Month()]...)\n"
- }
- // skip over M's
- i += count - 1
- // year
- case 'y':
- if inConstantText {
- inConstantText = false
- results += "b = append(b, " + fmt.Sprintf("%#v", []byte(format[start:i])) + "...)\n"
- }
- // peek
- // two digit year
- if len(format) != i+1 && format[i+1] == 'y' {
- i++
- results += `
- if t.Year() > 9 {
- b = append(b, strconv.Itoa(t.Year())[2:]...)
- } else {
- b = append(b, strconv.Itoa(t.Year())[1:]...)
- }
- `
- } else {
- // four digit year
- results += "b = strconv.AppendInt(b, int64(t.Year()), 10)\n"
- }
- // weekday
- // I know I only see 'EEEE' in the xml, but just in case handled all posibilities
- case 'E':
- if inConstantText {
- inConstantText = false
- results += "b = append(b, " + fmt.Sprintf("%#v", []byte(format[start:i])) + "...)\n"
- }
- var count int
- // count # of E's
- for j := i; j < len(format); j++ {
- if format[j] == 'E' {
- count++
- } else {
- break
- }
- }
- switch count {
- // Narrow
- case 1:
- results += "b = append(b, " + baseLocale + ".daysNarrow[t.Weekday()]...)\n"
- // Short
- case 2:
- results += "b = append(b, " + baseLocale + ".daysShort[t.Weekday()]...)\n"
- // Abbreviated
- case 3:
- results += "b = append(b, " + baseLocale + ".daysAbbreviated[t.Weekday()]...)\n"
- // Full/Wide
- case 4:
- results += "b = append(b, " + baseLocale + ".daysWide[t.Weekday()]...)\n"
- }
- // skip over E's
- i += count - 1
- // era eg. AD, BC
- case 'G':
- if inConstantText {
- inConstantText = false
- results += "b = append(b, " + fmt.Sprintf("%#v", []byte(format[start:i])) + "...)\n"
- }
- switch eraScore {
- case 0:
- results += `
- if t.Year() < 0 {
- b = append(b, ` + baseLocale + `.erasWide[0]...)
- } else {
- b = append(b, ` + baseLocale + `.erasWide[1]...)
- }
- `
- case 1, 2:
- results += `
- if t.Year() < 0 {
- b = append(b, ` + baseLocale + `.erasAbbreviated[0]...)
- } else {
- b = append(b, ` + baseLocale + `.erasAbbreviated[1]...)
- }
- `
- }
- default:
- // append all non matched text as they are constants
- if !inConstantText {
- inConstantText = true
- start = i
- }
- }
- }
- // if we were inConstantText when the string ended, add what's left.
- if inConstantText {
- // inContantText = false
- results += "b = append(b, " + fmt.Sprintf("%#v", []byte(format[start:])) + "...)\n"
- }
- return
- }
- func parseCurrencyNumberFormat(trans *translator) {
- if len(trans.CurrencyNumberFormat) == 0 {
- return
- }
- trans.FmtCurrencyExists = true
- negativeEqual := trans.CurrencyNumberFormat == trans.NegativeCurrencyNumberFormat
- match := groupLenRegex.FindString(trans.CurrencyNumberFormat)
- if len(match) > 0 {
- trans.FmtCurrencyGroupLen = len(match) - 2
- }
- match = requiredDecimalRegex.FindString(trans.CurrencyNumberFormat)
- if len(match) > 0 {
- trans.FmtCurrencyMinDecimalLen = len(match) - 1
- }
- match = secondaryGroupLenRegex.FindString(trans.CurrencyNumberFormat)
- if len(match) > 0 {
- trans.FmtCurrencySecondaryGroupLen = len(match) - 2
- }
- idx := 0
- for idx = 0; idx < len(trans.CurrencyNumberFormat); idx++ {
- if trans.CurrencyNumberFormat[idx] == '#' || trans.CurrencyNumberFormat[idx] == '0' {
- trans.FmtCurrencyPrefix = trans.CurrencyNumberFormat[:idx]
- break
- }
- }
- for idx = len(trans.CurrencyNumberFormat) - 1; idx >= 0; idx-- {
- if trans.CurrencyNumberFormat[idx] == '#' || trans.CurrencyNumberFormat[idx] == '0' {
- idx++
- trans.FmtCurrencySuffix = trans.CurrencyNumberFormat[idx:]
- break
- }
- }
- for idx = 0; idx < len(trans.FmtCurrencyPrefix); idx++ {
- if trans.FmtCurrencyPrefix[idx] == '¤' {
- trans.FmtCurrencyInPrefix = true
- trans.FmtCurrencyPrefix = strings.Replace(trans.FmtCurrencyPrefix, string(trans.FmtCurrencyPrefix[idx]), "", 1)
- if idx == 0 {
- trans.FmtCurrencyLeft = true
- } else {
- trans.FmtCurrencyLeft = false
- }
- break
- }
- }
- for idx = 0; idx < len(trans.FmtCurrencySuffix); idx++ {
- if trans.FmtCurrencySuffix[idx] == '¤' {
- trans.FmtCurrencyInPrefix = false
- trans.FmtCurrencySuffix = strings.Replace(trans.FmtCurrencySuffix, string(trans.FmtCurrencySuffix[idx]), "", 1)
- if idx == 0 {
- trans.FmtCurrencyLeft = true
- } else {
- trans.FmtCurrencyLeft = false
- }
- break
- }
- }
- // if len(trans.FmtCurrencyPrefix) > 0 {
- // trans.FmtCurrencyPrefix = fmt.Sprintf("%#v", []byte(trans.FmtCurrencyPrefix))
- // }
- // if len(trans.FmtCurrencySuffix) > 0 {
- // trans.FmtCurrencySuffix = fmt.Sprintf("%#v", []byte(trans.FmtCurrencySuffix))
- // }
- // no need to parse again if true....
- if negativeEqual {
- trans.FmtCurrencyNegativePrefix = trans.FmtCurrencyPrefix
- trans.FmtCurrencyNegativeSuffix = trans.FmtCurrencySuffix
- trans.FmtCurrencyNegativeInPrefix = trans.FmtCurrencyInPrefix
- trans.FmtCurrencyNegativeLeft = trans.FmtCurrencyLeft
- return
- }
- trans.FmtCurrencyNegativeExists = true
- for idx = 0; idx < len(trans.NegativeCurrencyNumberFormat); idx++ {
- if trans.NegativeCurrencyNumberFormat[idx] == '#' || trans.NegativeCurrencyNumberFormat[idx] == '0' {
- trans.FmtCurrencyNegativePrefix = trans.NegativeCurrencyNumberFormat[:idx]
- break
- }
- }
- for idx = len(trans.NegativeCurrencyNumberFormat) - 1; idx >= 0; idx-- {
- if trans.NegativeCurrencyNumberFormat[idx] == '#' || trans.NegativeCurrencyNumberFormat[idx] == '0' {
- idx++
- trans.FmtCurrencyNegativeSuffix = trans.NegativeCurrencyNumberFormat[idx:]
- break
- }
- }
- for idx = 0; idx < len(trans.FmtCurrencyNegativePrefix); idx++ {
- if trans.FmtCurrencyNegativePrefix[idx] == '¤' {
- trans.FmtCurrencyNegativeInPrefix = true
- trans.FmtCurrencyNegativePrefix = strings.Replace(trans.FmtCurrencyNegativePrefix, string(trans.FmtCurrencyNegativePrefix[idx]), "", 1)
- if idx == 0 {
- trans.FmtCurrencyNegativeLeft = true
- } else {
- trans.FmtCurrencyNegativeLeft = false
- }
- break
- }
- }
- for idx = 0; idx < len(trans.FmtCurrencyNegativeSuffix); idx++ {
- if trans.FmtCurrencyNegativeSuffix[idx] == '¤' {
- trans.FmtCurrencyNegativeInPrefix = false
- trans.FmtCurrencyNegativeSuffix = strings.Replace(trans.FmtCurrencyNegativeSuffix, string(trans.FmtCurrencyNegativeSuffix[idx]), "", 1)
- if idx == 0 {
- trans.FmtCurrencyNegativeLeft = true
- } else {
- trans.FmtCurrencyNegativeLeft = false
- }
- break
- }
- }
- // if len(trans.FmtCurrencyNegativePrefix) > 0 {
- // trans.FmtCurrencyNegativePrefix = fmt.Sprintf("%#v", []byte(trans.FmtCurrencyNegativePrefix))
- // }
- // if len(trans.FmtCurrencyNegativeSuffix) > 0 {
- // trans.FmtCurrencyNegativeSuffix = fmt.Sprintf("%#v", []byte(trans.FmtCurrencyNegativeSuffix))
- // }
- return
- }
- func parsePercentNumberFormat(trans *translator) {
- if len(trans.PercentNumberFormat) == 0 {
- return
- }
- trans.FmtPercentExists = true
- match := groupLenPercentRegex.FindString(trans.PercentNumberFormat)
- if len(match) > 0 {
- trans.FmtPercentGroupLen = len(match) - 1
- }
- match = requiredDecimalRegex.FindString(trans.PercentNumberFormat)
- if len(match) > 0 {
- trans.FmtPercentMinDecimalLen = len(match) - 1
- }
- match = secondaryGroupLenRegex.FindString(trans.PercentNumberFormat)
- if len(match) > 0 {
- trans.FmtPercentSecondaryGroupLen = len(match) - 2
- }
- idx := 0
- for idx = 0; idx < len(trans.PercentNumberFormat); idx++ {
- if trans.PercentNumberFormat[idx] == '#' || trans.PercentNumberFormat[idx] == '0' {
- trans.FmtPercentPrefix = trans.PercentNumberFormat[:idx]
- break
- }
- }
- for idx = len(trans.PercentNumberFormat) - 1; idx >= 0; idx-- {
- if trans.PercentNumberFormat[idx] == '#' || trans.PercentNumberFormat[idx] == '0' {
- idx++
- trans.FmtPercentSuffix = trans.PercentNumberFormat[idx:]
- break
- }
- }
- for idx = 0; idx < len(trans.FmtPercentPrefix); idx++ {
- if trans.FmtPercentPrefix[idx] == '%' {
- trans.FmtPercentInPrefix = true
- trans.FmtPercentPrefix = strings.Replace(trans.FmtPercentPrefix, string(trans.FmtPercentPrefix[idx]), "", 1)
- if idx == 0 {
- trans.FmtPercentLeft = true
- } else {
- trans.FmtPercentLeft = false
- }
- break
- }
- }
- for idx = 0; idx < len(trans.FmtPercentSuffix); idx++ {
- if trans.FmtPercentSuffix[idx] == '%' {
- trans.FmtPercentInPrefix = false
- trans.FmtPercentSuffix = strings.Replace(trans.FmtPercentSuffix, string(trans.FmtPercentSuffix[idx]), "", 1)
- if idx == 0 {
- trans.FmtPercentLeft = true
- } else {
- trans.FmtPercentLeft = false
- }
- break
- }
- }
- // if len(trans.FmtPercentPrefix) > 0 {
- // trans.FmtPercentPrefix = fmt.Sprintf("%#v", []byte(trans.FmtPercentPrefix))
- // }
- // if len(trans.FmtPercentSuffix) > 0 {
- // trans.FmtPercentSuffix = fmt.Sprintf("%#v", []byte(trans.FmtPercentSuffix))
- // }
- return
- }
- func parseDecimalNumberFormat(trans *translator) {
- if len(trans.DecimalNumberFormat) == 0 {
- return
- }
- trans.FmtNumberExists = true
- formats := strings.SplitN(trans.DecimalNumberFormat, ";", 2)
- match := groupLenRegex.FindString(formats[0])
- if len(match) > 0 {
- trans.FmtNumberGroupLen = len(match) - 2
- }
- match = requiredDecimalRegex.FindString(formats[0])
- if len(match) > 0 {
- trans.FmtNumberMinDecimalLen = len(match) - 1
- }
- match = secondaryGroupLenRegex.FindString(formats[0])
- if len(match) > 0 {
- trans.FmtNumberSecondaryGroupLen = len(match) - 2
- }
- return
- }
- type sortRank struct {
- Rank uint8
- Value string
- }
- type ByRank []sortRank
- func (a ByRank) Len() int { return len(a) }
- func (a ByRank) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
- func (a ByRank) Less(i, j int) bool { return a[i].Rank < a[j].Rank }
- // TODO: refine generated code a bit, some combinations end up with same plural rule,
- // could check all at once; but it works and that's step 1 complete
- func parseRangePluralRuleFunc(current *cldr.CLDR, baseLocale string) (results string, plurals string) {
- var pluralRange *struct {
- cldr.Common
- Locales string `xml:"locales,attr"`
- PluralRange []*struct {
- cldr.Common
- Start string `xml:"start,attr"`
- End string `xml:"end,attr"`
- Result string `xml:"result,attr"`
- } `xml:"pluralRange"`
- }
- var pluralArr []locales.PluralRule
- for _, pr := range current.Supplemental().Plurals[1].PluralRanges {
- locs := strings.Split(pr.Locales, " ")
- for _, loc := range locs {
- if loc == baseLocale {
- pluralRange = pr
- }
- }
- }
- // no range plural rules for locale
- if pluralRange == nil {
- plurals = "nil"
- results = "return locales.PluralRuleUnknown"
- return
- }
- mp := make(map[string]struct{})
- // pre-process if all the same
- for _, rule := range pluralRange.PluralRange {
- mp[rule.Result] = struct{}{}
- }
- for k := range mp {
- psI := pluralStringToInt(k)
- pluralArr = append(pluralArr, psI)
- }
- if len(mp) == 1 {
- results += "return locales." + pluralStringToString(pluralRange.PluralRange[0].Result)
- plurals = fmt.Sprintf("%#v", pluralArr)
- return
- }
- multiple := len(pluralRange.PluralRange) > 1
- if multiple {
- results += "start := " + baseLocale + ".CardinalPluralRule(num1, v1)\n"
- results += "end := " + baseLocale + ".CardinalPluralRule(num2, v2)\n\n"
- }
- first := true
- // pre parse for variables
- for i, rule := range pluralRange.PluralRange {
- if i == len(pluralRange.PluralRange)-1 {
- if multiple {
- results += "\n\n"
- }
- results += "return locales." + pluralStringToString(rule.Result)
- continue
- }
- if first {
- results += "if"
- first = false
- } else {
- results += "else if"
- }
- results += " start == locales." + pluralStringToString(rule.Start) + " && end == locales." + pluralStringToString(rule.End) + " {\n return locales." + pluralStringToString(rule.Result) + "\n} "
- }
- if multiple {
- results = "\n" + results + "\n"
- }
- if len(pluralArr) == 0 {
- plurals = "nil"
- } else {
- plurals = fmt.Sprintf("%#v", pluralArr)
- }
- return
- }
- // TODO: cleanup function logic perhaps write a lexer... but it's working right now, and
- // I'm already farther down the rabbit hole than I'd like and so pulling the chute here.
- func parseOrdinalPluralRuleFunc(current *cldr.CLDR, baseLocale string) (results string, plurals string) {
- var prOrdinal *struct {
- cldr.Common
- Locales string "xml:\"locales,attr\""
- PluralRule []*struct {
- cldr.Common
- Count string "xml:\"count,attr\""
- } "xml:\"pluralRule\""
- }
- var pluralArr []locales.PluralRule
- // idx 0 is ordinal rules
- for _, pr := range current.Supplemental().Plurals[0].PluralRules {
- locs := strings.Split(pr.Locales, " ")
- for _, loc := range locs {
- if loc == baseLocale {
- prOrdinal = pr
- // for _, pl := range pr.PluralRule {
- // fmt.Println(pl.Count, pl.Common.Data())
- // }
- }
- }
- }
- // no plural rules for locale
- if prOrdinal == nil {
- plurals = "nil"
- results = "return locales.PluralRuleUnknown"
- return
- }
- vals := make(map[string]struct{})
- first := true
- // pre parse for variables
- for _, rule := range prOrdinal.PluralRule {
- ps1 := pluralStringToString(rule.Count)
- psI := pluralStringToInt(rule.Count)
- pluralArr = append(pluralArr, psI)
- data := strings.Replace(strings.Replace(strings.Replace(strings.TrimSpace(strings.SplitN(rule.Common.Data(), "@", 2)[0]), " = ", " == ", -1), " or ", " || ", -1), " and ", " && ", -1)
- if len(data) == 0 {
- if len(prOrdinal.PluralRule) == 1 {
- results = "return locales." + ps1
- } else {
- results += "\n\nreturn locales." + ps1
- // results += "else {\nreturn locales." + locales.PluralStringToString(rule.Count) + ", nil\n}"
- }
- continue
- }
- // // All need n, so always add
- // if strings.Contains(data, "n") {
- // vals[prVarFuncs["n"]] = struct{}{}
- // }
- if strings.Contains(data, "i") {
- vals[prVarFuncs["i"]] = struct{}{}
- }
- // v is inherently avaialable as an argument
- // if strings.Contains(data, "v") {
- // vals[prVarFuncs["v"]] = struct{}{}
- // }
- if strings.Contains(data, "w") {
- vals[prVarFuncs["w"]] = struct{}{}
- }
- if strings.Contains(data, "f") {
- vals[prVarFuncs["f"]] = struct{}{}
- }
- if strings.Contains(data, "t") {
- vals[prVarFuncs["t"]] = struct{}{}
- }
- if first {
- results += "if "
- first = false
- } else {
- results += "else if "
- }
- stmt := ""
- // real work here
- //
- // split by 'or' then by 'and' allowing to better
- // determine bracketing for formula
- ors := strings.Split(data, "||")
- for _, or := range ors {
- stmt += "("
- ands := strings.Split(strings.TrimSpace(or), "&&")
- for _, and := range ands {
- inArg := false
- pre := ""
- lft := ""
- preOperator := ""
- args := strings.Split(strings.TrimSpace(and), " ")
- for _, a := range args {
- if inArg {
- // check to see if is a value range 2..9
- multiRange := strings.Count(a, "..") > 1
- cargs := strings.Split(strings.TrimSpace(a), ",")
- hasBracket := len(cargs) > 1
- bracketAdded := false
- lastWasRange := false
- for _, carg := range cargs {
- if rng := strings.Split(carg, ".."); len(rng) > 1 {
- if multiRange {
- pre += " ("
- } else {
- pre += " "
- }
- switch preOperator {
- case "==":
- pre += lft + " >= " + rng[0] + " && " + lft + "<=" + rng[1]
- case "!=":
- pre += lft + " < " + rng[0] + " && " + lft + " > " + rng[1]
- }
- if multiRange {
- pre += ") || "
- } else {
- pre += " || "
- }
- lastWasRange = true
- continue
- }
- if lastWasRange {
- pre = strings.TrimRight(pre, " || ") + " && "
- }
- lastWasRange = false
- if hasBracket && !bracketAdded {
- pre += "("
- bracketAdded = true
- }
- // single comma separated values
- switch preOperator {
- case "==":
- pre += " " + lft + preOperator + carg + " || "
- case "!=":
- pre += " " + lft + preOperator + carg + " && "
- }
- }
- pre = strings.TrimRight(pre, " || ")
- pre = strings.TrimRight(pre, " && ")
- pre = strings.TrimRight(pre, " || ")
- if hasBracket && bracketAdded {
- pre += ")"
- }
- continue
- }
- if strings.Contains(a, "=") || a == ">" || a == "<" {
- inArg = true
- preOperator = a
- continue
- }
- lft += a
- }
- stmt += pre + " && "
- }
- stmt = strings.TrimRight(stmt, " && ") + ") || "
- }
- stmt = strings.TrimRight(stmt, " || ")
- results += stmt
- results += " {\n"
- // return plural rule here
- results += "return locales." + ps1 + "\n"
- results += "}"
- }
- pre := "\n"
- // always needed
- vals[prVarFuncs["n"]] = struct{}{}
- sorted := make([]sortRank, 0, len(vals))
- for k := range vals {
- switch k[:1] {
- case "n":
- sorted = append(sorted, sortRank{
- Value: prVarFuncs["n"],
- Rank: 1,
- })
- case "i":
- sorted = append(sorted, sortRank{
- Value: prVarFuncs["i"],
- Rank: 2,
- })
- case "w":
- sorted = append(sorted, sortRank{
- Value: prVarFuncs["w"],
- Rank: 3,
- })
- case "f":
- sorted = append(sorted, sortRank{
- Value: prVarFuncs["f"],
- Rank: 4,
- })
- case "t":
- sorted = append(sorted, sortRank{
- Value: prVarFuncs["t"],
- Rank: 5,
- })
- }
- }
- sort.Sort(ByRank(sorted))
- for _, k := range sorted {
- pre += k.Value
- }
- if len(results) == 0 {
- results = "return locales.PluralRuleUnknown"
- } else {
- if !strings.HasPrefix(results, "return") {
- results = manyToSingleVars(results)
- // pre += "\n"
- results = pre + results
- }
- }
- if len(pluralArr) == 0 {
- plurals = "nil"
- } else {
- plurals = fmt.Sprintf("%#v", pluralArr)
- }
- return
- }
- // TODO: cleanup function logic perhaps write a lexer... but it's working right now, and
- // I'm already farther down the rabbit hole than I'd like and so pulling the chute here.
- func parseCardinalPluralRuleFunc(current *cldr.CLDR, baseLocale string) (results string, plurals string) {
- var prCardinal *struct {
- cldr.Common
- Locales string "xml:\"locales,attr\""
- PluralRule []*struct {
- cldr.Common
- Count string "xml:\"count,attr\""
- } "xml:\"pluralRule\""
- }
- var pluralArr []locales.PluralRule
- // idx 2 is cardinal rules
- for _, pr := range current.Supplemental().Plurals[2].PluralRules {
- locs := strings.Split(pr.Locales, " ")
- for _, loc := range locs {
- if loc == baseLocale {
- prCardinal = pr
- }
- }
- }
- // no plural rules for locale
- if prCardinal == nil {
- plurals = "nil"
- results = "return locales.PluralRuleUnknown"
- return
- }
- vals := make(map[string]struct{})
- first := true
- // pre parse for variables
- for _, rule := range prCardinal.PluralRule {
- ps1 := pluralStringToString(rule.Count)
- psI := pluralStringToInt(rule.Count)
- pluralArr = append(pluralArr, psI)
- data := strings.Replace(strings.Replace(strings.Replace(strings.TrimSpace(strings.SplitN(rule.Common.Data(), "@", 2)[0]), " = ", " == ", -1), " or ", " || ", -1), " and ", " && ", -1)
- if len(data) == 0 {
- if len(prCardinal.PluralRule) == 1 {
- results = "return locales." + ps1
- } else {
- results += "\n\nreturn locales." + ps1
- // results += "else {\nreturn locales." + locales.PluralStringToString(rule.Count) + ", nil\n}"
- }
- continue
- }
- // // All need n, so always add
- // if strings.Contains(data, "n") {
- // vals[prVarFuncs["n"]] = struct{}{}
- // }
- if strings.Contains(data, "i") {
- vals[prVarFuncs["i"]] = struct{}{}
- }
- // v is inherently avaialable as an argument
- // if strings.Contains(data, "v") {
- // vals[prVarFuncs["v"]] = struct{}{}
- // }
- if strings.Contains(data, "w") {
- vals[prVarFuncs["w"]] = struct{}{}
- }
- if strings.Contains(data, "f") {
- vals[prVarFuncs["f"]] = struct{}{}
- }
- if strings.Contains(data, "t") {
- vals[prVarFuncs["t"]] = struct{}{}
- }
- if first {
- results += "if "
- first = false
- } else {
- results += "else if "
- }
- stmt := ""
- // real work here
- //
- // split by 'or' then by 'and' allowing to better
- // determine bracketing for formula
- ors := strings.Split(data, "||")
- for _, or := range ors {
- stmt += "("
- ands := strings.Split(strings.TrimSpace(or), "&&")
- for _, and := range ands {
- inArg := false
- pre := ""
- lft := ""
- preOperator := ""
- args := strings.Split(strings.TrimSpace(and), " ")
- for _, a := range args {
- if inArg {
- // check to see if is a value range 2..9
- multiRange := strings.Count(a, "..") > 1
- cargs := strings.Split(strings.TrimSpace(a), ",")
- hasBracket := len(cargs) > 1
- bracketAdded := false
- lastWasRange := false
- for _, carg := range cargs {
- if rng := strings.Split(carg, ".."); len(rng) > 1 {
- if multiRange {
- pre += " ("
- } else {
- pre += " "
- }
- switch preOperator {
- case "==":
- pre += lft + " >= " + rng[0] + " && " + lft + "<=" + rng[1]
- case "!=":
- pre += lft + " < " + rng[0] + " && " + lft + " > " + rng[1]
- }
- if multiRange {
- pre += ") || "
- } else {
- pre += " || "
- }
- lastWasRange = true
- continue
- }
- if lastWasRange {
- pre = strings.TrimRight(pre, " || ") + " && "
- }
- lastWasRange = false
- if hasBracket && !bracketAdded {
- pre += "("
- bracketAdded = true
- }
- // single comma separated values
- switch preOperator {
- case "==":
- pre += " " + lft + preOperator + carg + " || "
- case "!=":
- pre += " " + lft + preOperator + carg + " && "
- }
- }
- pre = strings.TrimRight(pre, " || ")
- pre = strings.TrimRight(pre, " && ")
- pre = strings.TrimRight(pre, " || ")
- if hasBracket && bracketAdded {
- pre += ")"
- }
- continue
- }
- if strings.Contains(a, "=") || a == ">" || a == "<" {
- inArg = true
- preOperator = a
- continue
- }
- lft += a
- }
- stmt += pre + " && "
- }
- stmt = strings.TrimRight(stmt, " && ") + ") || "
- }
- stmt = strings.TrimRight(stmt, " || ")
- results += stmt
- results += " {\n"
- // return plural rule here
- results += "return locales." + ps1 + "\n"
- results += "}"
- }
- pre := "\n"
- // always needed
- vals[prVarFuncs["n"]] = struct{}{}
- sorted := make([]sortRank, 0, len(vals))
- for k := range vals {
- switch k[:1] {
- case "n":
- sorted = append(sorted, sortRank{
- Value: prVarFuncs["n"],
- Rank: 1,
- })
- case "i":
- sorted = append(sorted, sortRank{
- Value: prVarFuncs["i"],
- Rank: 2,
- })
- case "w":
- sorted = append(sorted, sortRank{
- Value: prVarFuncs["w"],
- Rank: 3,
- })
- case "f":
- sorted = append(sorted, sortRank{
- Value: prVarFuncs["f"],
- Rank: 4,
- })
- case "t":
- sorted = append(sorted, sortRank{
- Value: prVarFuncs["t"],
- Rank: 5,
- })
- }
- }
- sort.Sort(ByRank(sorted))
- for _, k := range sorted {
- pre += k.Value
- }
- if len(results) == 0 {
- results = "return locales.PluralRuleUnknown"
- } else {
- if !strings.HasPrefix(results, "return") {
- results = manyToSingleVars(results)
- // pre += "\n"
- results = pre + results
- }
- }
- if len(pluralArr) == 0 {
- plurals = "nil"
- } else {
- plurals = fmt.Sprintf("%#v", pluralArr)
- }
- return
- }
- func manyToSingleVars(input string) (results string) {
- matches := nModRegex.FindAllString(input, -1)
- mp := make(map[string][]string) // map of formula to variable
- var found bool
- var split []string
- var variable string
- for _, formula := range matches {
- if _, found = mp[formula]; found {
- continue
- }
- split = strings.SplitN(formula, "%", 2)
- mp[formula] = []string{split[1], "math.Mod(" + split[0] + ", " + split[1] + ")"}
- }
- for k, v := range mp {
- variable = "nMod" + v[0]
- results += variable + " := " + v[1] + "\n"
- input = strings.Replace(input, k, variable, -1)
- }
- matches = iModRegex.FindAllString(input, -1)
- mp = make(map[string][]string) // map of formula to variable
- for _, formula := range matches {
- if _, found = mp[formula]; found {
- continue
- }
- split = strings.SplitN(formula, "%", 2)
- mp[formula] = []string{split[1], formula}
- }
- for k, v := range mp {
- variable = "iMod" + v[0]
- results += variable + " := " + v[1] + "\n"
- input = strings.Replace(input, k, variable, -1)
- }
- matches = wModRegex.FindAllString(input, -1)
- mp = make(map[string][]string) // map of formula to variable
- for _, formula := range matches {
- if _, found = mp[formula]; found {
- continue
- }
- split = strings.SplitN(formula, "%", 2)
- mp[formula] = []string{split[1], formula}
- }
- for k, v := range mp {
- variable = "wMod" + v[0]
- results += variable + " := " + v[1] + "\n"
- input = strings.Replace(input, k, variable, -1)
- }
- matches = fModRegex.FindAllString(input, -1)
- mp = make(map[string][]string) // map of formula to variable
- for _, formula := range matches {
- if _, found = mp[formula]; found {
- continue
- }
- split = strings.SplitN(formula, "%", 2)
- mp[formula] = []string{split[1], formula}
- }
- for k, v := range mp {
- variable = "fMod" + v[0]
- results += variable + " := " + v[1] + "\n"
- input = strings.Replace(input, k, variable, -1)
- }
- matches = tModRegex.FindAllString(input, -1)
- mp = make(map[string][]string) // map of formula to variable
- for _, formula := range matches {
- if _, found = mp[formula]; found {
- continue
- }
- split = strings.SplitN(formula, "%", 2)
- mp[formula] = []string{split[1], formula}
- }
- for k, v := range mp {
- variable = "tMod" + v[0]
- results += variable + " := " + v[1] + "\n"
- input = strings.Replace(input, k, variable, -1)
- }
- results = results + "\n" + input
- return
- }
- // pluralStringToInt returns the enum value of 'plural' provided
- func pluralStringToInt(plural string) locales.PluralRule {
- switch plural {
- case "zero":
- return locales.PluralRuleZero
- case "one":
- return locales.PluralRuleOne
- case "two":
- return locales.PluralRuleTwo
- case "few":
- return locales.PluralRuleFew
- case "many":
- return locales.PluralRuleMany
- case "other":
- return locales.PluralRuleOther
- default:
- return locales.PluralRuleUnknown
- }
- }
- func pluralStringToString(pr string) string {
- pr = strings.TrimSpace(pr)
- switch pr {
- case "zero":
- return "PluralRuleZero"
- case "one":
- return "PluralRuleOne"
- case "two":
- return "PluralRuleTwo"
- case "few":
- return "PluralRuleFew"
- case "many":
- return "PluralRuleMany"
- case "other":
- return "PluralRuleOther"
- default:
- return "PluralRuleUnknown"
- }
- }
|