generate_resources.go 60 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698
  1. package main
  2. import (
  3. "fmt"
  4. "log"
  5. "os"
  6. "os/exec"
  7. "regexp"
  8. "sort"
  9. "strconv"
  10. "strings"
  11. "github.com/go-playground/locales"
  12. "golang.org/x/text/unicode/cldr"
  13. "text/template"
  14. )
  15. const (
  16. locDir = "../%s"
  17. locFilename = locDir + "/%s.go"
  18. )
  19. var (
  20. tfuncs = template.FuncMap{
  21. "is_multibyte": func(s string) bool {
  22. return len([]byte(s)) > 1
  23. },
  24. "reverse_bytes": func(s string) string {
  25. b := make([]byte, 0, 8)
  26. for j := len(s) - 1; j >= 0; j-- {
  27. b = append(b, s[j])
  28. }
  29. return fmt.Sprintf("%#v", b)
  30. },
  31. "byte_count": func(s ...string) string {
  32. var count int
  33. for i := 0; i < len(s); i++ {
  34. count += len([]byte(s[i]))
  35. }
  36. return strconv.Itoa(count)
  37. },
  38. }
  39. prVarFuncs = map[string]string{
  40. "n": "n := math.Abs(num)\n",
  41. "i": "i := int64(n)\n",
  42. // "v": "v := ...", // inherently available as argument
  43. "w": "w := locales.W(n, v)\n",
  44. "f": "f := locales.F(n, v)\n",
  45. "t": "t := locales.T(n, v)\n",
  46. }
  47. translators = make(map[string]*translator)
  48. baseTranslators = make(map[string]*translator)
  49. globalCurrenciesMap = make(map[string]struct{}) // ["USD"] = "$" currency code, just all currencies for mapping to enum
  50. globCurrencyIdxMap = make(map[string]int) // ["USD"] = 0
  51. globalCurrencies = make([]string, 0, 100) // array of currency codes index maps to enum
  52. tmpl *template.Template
  53. nModRegex = regexp.MustCompile("(n%[0-9]+)")
  54. iModRegex = regexp.MustCompile("(i%[0-9]+)")
  55. wModRegex = regexp.MustCompile("(w%[0-9]+)")
  56. fModRegex = regexp.MustCompile("(f%[0-9]+)")
  57. tModRegex = regexp.MustCompile("(t%[0-9]+)")
  58. groupLenRegex = regexp.MustCompile(",([0-9#]+)\\.")
  59. groupLenPercentRegex = regexp.MustCompile(",([0-9#]+)$")
  60. secondaryGroupLenRegex = regexp.MustCompile(",([0-9#]+),")
  61. requiredNumRegex = regexp.MustCompile("([0-9]+)\\.")
  62. requiredDecimalRegex = regexp.MustCompile("\\.([0-9]+)")
  63. )
  64. type translator struct {
  65. Locale string
  66. BaseLocale string
  67. Plurals string
  68. CardinalFunc string
  69. PluralsOrdinal string
  70. OrdinalFunc string
  71. PluralsRange string
  72. RangeFunc string
  73. Decimal string
  74. Group string
  75. Minus string
  76. Percent string
  77. PerMille string
  78. TimeSeparator string
  79. Infinity string
  80. Currencies string
  81. // FmtNumber vars
  82. FmtNumberExists bool
  83. FmtNumberGroupLen int
  84. FmtNumberSecondaryGroupLen int
  85. FmtNumberMinDecimalLen int
  86. // FmtPercent vars
  87. FmtPercentExists bool
  88. FmtPercentGroupLen int
  89. FmtPercentSecondaryGroupLen int
  90. FmtPercentMinDecimalLen int
  91. FmtPercentPrefix string
  92. FmtPercentSuffix string
  93. FmtPercentInPrefix bool
  94. FmtPercentLeft bool
  95. // FmtCurrency vars
  96. FmtCurrencyExists bool
  97. FmtCurrencyGroupLen int
  98. FmtCurrencySecondaryGroupLen int
  99. FmtCurrencyMinDecimalLen int
  100. FmtCurrencyPrefix string
  101. FmtCurrencySuffix string
  102. FmtCurrencyInPrefix bool
  103. FmtCurrencyLeft bool
  104. FmtCurrencyNegativeExists bool
  105. FmtCurrencyNegativePrefix string
  106. FmtCurrencyNegativeSuffix string
  107. FmtCurrencyNegativeInPrefix bool
  108. FmtCurrencyNegativeLeft bool
  109. // Date & Time
  110. FmtCalendarExists bool
  111. FmtMonthsAbbreviated string
  112. FmtMonthsNarrow string
  113. FmtMonthsWide string
  114. FmtDaysAbbreviated string
  115. FmtDaysNarrow string
  116. FmtDaysShort string
  117. FmtDaysWide string
  118. FmtPeriodsAbbreviated string
  119. FmtPeriodsNarrow string
  120. FmtPeriodsShort string
  121. FmtPeriodsWide string
  122. FmtErasAbbreviated string
  123. FmtErasNarrow string
  124. FmtErasWide string
  125. FmtTimezones string
  126. // calculation only fields below this point...
  127. DecimalNumberFormat string
  128. PercentNumberFormat string
  129. CurrencyNumberFormat string
  130. NegativeCurrencyNumberFormat string
  131. // Dates
  132. FmtDateFull string
  133. FmtDateLong string
  134. FmtDateMedium string
  135. FmtDateShort string
  136. // Times
  137. FmtTimeFull string
  138. FmtTimeLong string
  139. FmtTimeMedium string
  140. FmtTimeShort string
  141. // timezones per locale by type
  142. timezones map[string]*zoneAbbrev // key = type eg. America_Eastern zone Abbrev will be long form eg. Eastern Standard Time, Pacific Standard Time.....
  143. }
  144. type zoneAbbrev struct {
  145. standard string
  146. daylight string
  147. }
  148. var timezones = map[string]*zoneAbbrev{} // key = type eg. America_Eastern zone Abbrev eg. EST & EDT
  149. func main() {
  150. var err error
  151. // load template
  152. tmpl, err = template.New("all").Funcs(tfuncs).ParseGlob("*.tmpl")
  153. if err != nil {
  154. log.Fatal(err)
  155. }
  156. // load CLDR recourses
  157. var decoder cldr.Decoder
  158. cldr, err := decoder.DecodePath("data/core")
  159. if err != nil {
  160. panic(err)
  161. }
  162. preProcess(cldr)
  163. postProcess(cldr)
  164. var currencies string
  165. for i, curr := range globalCurrencies {
  166. if i == 0 {
  167. currencies = curr + " Type = iota\n"
  168. continue
  169. }
  170. currencies += curr + "\n"
  171. }
  172. if err = os.MkdirAll(fmt.Sprintf(locDir, "currency"), 0777); err != nil {
  173. log.Fatal(err)
  174. }
  175. filename := fmt.Sprintf(locFilename, "currency", "currency")
  176. output, err := os.Create(filename)
  177. if err != nil {
  178. log.Fatal(err)
  179. }
  180. defer output.Close()
  181. if err := tmpl.ExecuteTemplate(output, "currencies", currencies); err != nil {
  182. log.Fatal(err)
  183. }
  184. output.Close()
  185. // after file written run gofmt on file to ensure best formatting
  186. cmd := exec.Command("goimports", "-w", filename)
  187. if err = cmd.Run(); err != nil {
  188. log.Panic(err)
  189. }
  190. cmd = exec.Command("gofmt", "-s", "-w", filename)
  191. if err = cmd.Run(); err != nil {
  192. log.Panic(err)
  193. }
  194. var locMap string
  195. for _, trans := range translators {
  196. locMap += `"` + trans.Locale + `" : ` + trans.Locale + `.New,
  197. `
  198. fmt.Println("Writing Data:", trans.Locale)
  199. if err = os.MkdirAll(fmt.Sprintf(locDir, trans.Locale), 0777); err != nil {
  200. log.Fatal(err)
  201. }
  202. filename = fmt.Sprintf(locFilename, trans.Locale, trans.Locale)
  203. output, err := os.Create(filename)
  204. if err != nil {
  205. log.Fatal(err)
  206. }
  207. defer output.Close()
  208. if err := tmpl.ExecuteTemplate(output, "translator", trans); err != nil {
  209. log.Fatal(err)
  210. }
  211. output.Close()
  212. // after file written run gofmt on file to ensure best formatting
  213. cmd := exec.Command("goimports", "-w", filename)
  214. if err = cmd.Run(); err != nil {
  215. log.Panic(err)
  216. }
  217. // this simplifies some syntax that I can;t find an option for in goimports, namely '-s'
  218. cmd = exec.Command("gofmt", "-s", "-w", filename)
  219. if err = cmd.Run(); err != nil {
  220. log.Panic(err)
  221. }
  222. filename = fmt.Sprintf(locFilename, trans.Locale, trans.Locale+"_test")
  223. if _, err := os.Stat(filename); err == nil {
  224. fmt.Println("*************** test file exists, skipping:", filename)
  225. continue
  226. }
  227. output, err = os.Create(filename)
  228. if err != nil {
  229. log.Fatal(err)
  230. }
  231. defer output.Close()
  232. if err := tmpl.ExecuteTemplate(output, "tests", trans); err != nil {
  233. log.Fatal(err)
  234. }
  235. output.Close()
  236. // after file written run gofmt on file to ensure best formatting
  237. cmd = exec.Command("goimports", "-w", filename)
  238. if err = cmd.Run(); err != nil {
  239. log.Panic(err)
  240. }
  241. // this simplifies some syntax that I can;t find an option for in goimports, namely '-s'
  242. cmd = exec.Command("gofmt", "-s", "-w", filename)
  243. if err = cmd.Run(); err != nil {
  244. log.Panic(err)
  245. }
  246. }
  247. }
  248. func ApplyOverrides(trans *translator) {
  249. if trans.BaseLocale == "ru" {
  250. trans.PercentNumberFormat = "#,##0%"
  251. }
  252. }
  253. func postProcess(cldr *cldr.CLDR) {
  254. for _, v := range timezones {
  255. // no DST
  256. if len(v.daylight) == 0 {
  257. v.daylight = v.standard
  258. }
  259. }
  260. var base *translator
  261. var found bool
  262. for _, trans := range translators {
  263. fmt.Println("Post Processing:", trans.Locale)
  264. // cardinal plural rules
  265. trans.CardinalFunc, trans.Plurals = parseCardinalPluralRuleFunc(cldr, trans.BaseLocale)
  266. //ordinal plural rules
  267. trans.OrdinalFunc, trans.PluralsOrdinal = parseOrdinalPluralRuleFunc(cldr, trans.BaseLocale)
  268. // range plural rules
  269. trans.RangeFunc, trans.PluralsRange = parseRangePluralRuleFunc(cldr, trans.BaseLocale)
  270. // ignore base locales
  271. if trans.BaseLocale == trans.Locale {
  272. found = false
  273. } else {
  274. base, found = baseTranslators[trans.BaseLocale]
  275. }
  276. // Numbers
  277. if len(trans.Decimal) == 0 {
  278. if found {
  279. trans.Decimal = base.Decimal
  280. }
  281. if len(trans.Decimal) == 0 {
  282. trans.Decimal = ""
  283. }
  284. }
  285. if len(trans.Group) == 0 {
  286. if found {
  287. trans.Group = base.Group
  288. }
  289. if len(trans.Group) == 0 {
  290. trans.Group = ""
  291. }
  292. }
  293. if len(trans.Minus) == 0 {
  294. if found {
  295. trans.Minus = base.Minus
  296. }
  297. if len(trans.Minus) == 0 {
  298. trans.Minus = ""
  299. }
  300. }
  301. if len(trans.Percent) == 0 {
  302. if found {
  303. trans.Percent = base.Percent
  304. }
  305. if len(trans.Percent) == 0 {
  306. trans.Percent = ""
  307. }
  308. }
  309. if len(trans.PerMille) == 0 {
  310. if found {
  311. trans.PerMille = base.PerMille
  312. }
  313. if len(trans.PerMille) == 0 {
  314. trans.PerMille = ""
  315. }
  316. }
  317. if len(trans.TimeSeparator) == 0 && found {
  318. trans.TimeSeparator = base.TimeSeparator
  319. }
  320. if len(trans.Infinity) == 0 && found {
  321. trans.Infinity = base.Infinity
  322. }
  323. // Currency
  324. // number values
  325. if len(trans.DecimalNumberFormat) == 0 && found {
  326. trans.DecimalNumberFormat = base.DecimalNumberFormat
  327. }
  328. if len(trans.PercentNumberFormat) == 0 && found {
  329. trans.PercentNumberFormat = base.PercentNumberFormat
  330. }
  331. if len(trans.CurrencyNumberFormat) == 0 && found {
  332. trans.CurrencyNumberFormat = base.CurrencyNumberFormat
  333. }
  334. if len(trans.NegativeCurrencyNumberFormat) == 0 && found {
  335. trans.NegativeCurrencyNumberFormat = base.NegativeCurrencyNumberFormat
  336. }
  337. // date values
  338. if len(trans.FmtDateFull) == 0 && found {
  339. trans.FmtDateFull = base.FmtDateFull
  340. }
  341. if len(trans.FmtDateLong) == 0 && found {
  342. trans.FmtDateLong = base.FmtDateLong
  343. }
  344. if len(trans.FmtDateMedium) == 0 && found {
  345. trans.FmtDateMedium = base.FmtDateMedium
  346. }
  347. if len(trans.FmtDateShort) == 0 && found {
  348. trans.FmtDateShort = base.FmtDateShort
  349. }
  350. // time values
  351. if len(trans.FmtTimeFull) == 0 && found {
  352. trans.FmtTimeFull = base.FmtTimeFull
  353. }
  354. if len(trans.FmtTimeLong) == 0 && found {
  355. trans.FmtTimeLong = base.FmtTimeLong
  356. }
  357. if len(trans.FmtTimeMedium) == 0 && found {
  358. trans.FmtTimeMedium = base.FmtTimeMedium
  359. }
  360. if len(trans.FmtTimeShort) == 0 && found {
  361. trans.FmtTimeShort = base.FmtTimeShort
  362. }
  363. // month values
  364. if len(trans.FmtMonthsAbbreviated) == 0 && found {
  365. trans.FmtMonthsAbbreviated = base.FmtMonthsAbbreviated
  366. }
  367. if len(trans.FmtMonthsNarrow) == 0 && found {
  368. trans.FmtMonthsNarrow = base.FmtMonthsNarrow
  369. }
  370. if len(trans.FmtMonthsWide) == 0 && found {
  371. trans.FmtMonthsWide = base.FmtMonthsWide
  372. }
  373. // day values
  374. if len(trans.FmtDaysAbbreviated) == 0 && found {
  375. trans.FmtDaysAbbreviated = base.FmtDaysAbbreviated
  376. }
  377. if len(trans.FmtDaysNarrow) == 0 && found {
  378. trans.FmtDaysNarrow = base.FmtDaysNarrow
  379. }
  380. if len(trans.FmtDaysShort) == 0 && found {
  381. trans.FmtDaysShort = base.FmtDaysShort
  382. }
  383. if len(trans.FmtDaysWide) == 0 && found {
  384. trans.FmtDaysWide = base.FmtDaysWide
  385. }
  386. // period values
  387. if len(trans.FmtPeriodsAbbreviated) == 0 && found {
  388. trans.FmtPeriodsAbbreviated = base.FmtPeriodsAbbreviated
  389. }
  390. if len(trans.FmtPeriodsNarrow) == 0 && found {
  391. trans.FmtPeriodsNarrow = base.FmtPeriodsNarrow
  392. }
  393. if len(trans.FmtPeriodsShort) == 0 && found {
  394. trans.FmtPeriodsShort = base.FmtPeriodsShort
  395. }
  396. if len(trans.FmtPeriodsWide) == 0 && found {
  397. trans.FmtPeriodsWide = base.FmtPeriodsWide
  398. }
  399. // era values
  400. if len(trans.FmtErasAbbreviated) == 0 && found {
  401. trans.FmtErasAbbreviated = base.FmtErasAbbreviated
  402. }
  403. if len(trans.FmtErasNarrow) == 0 && found {
  404. trans.FmtErasNarrow = base.FmtErasNarrow
  405. }
  406. if len(trans.FmtErasWide) == 0 && found {
  407. trans.FmtErasWide = base.FmtErasWide
  408. }
  409. ldml := cldr.RawLDML(trans.Locale)
  410. currencies := make([]string, len(globalCurrencies), len(globalCurrencies))
  411. var kval string
  412. for k, v := range globCurrencyIdxMap {
  413. kval = k
  414. // if kval[:len(kval)-1] != " " {
  415. // kval += " "
  416. // }
  417. currencies[v] = kval
  418. }
  419. // some just have no data...
  420. if ldml.Numbers != nil {
  421. if ldml.Numbers.Currencies != nil {
  422. for _, currency := range ldml.Numbers.Currencies.Currency {
  423. if len(currency.Symbol) == 0 {
  424. continue
  425. }
  426. if len(currency.Symbol[0].Data()) == 0 {
  427. continue
  428. }
  429. if len(currency.Type) == 0 {
  430. continue
  431. }
  432. currencies[globCurrencyIdxMap[currency.Type]] = currency.Symbol[0].Data()
  433. }
  434. }
  435. }
  436. trans.Currencies = fmt.Sprintf("%#v", currencies)
  437. // timezones
  438. if (trans.timezones == nil || len(trans.timezones) == 0) && found {
  439. trans.timezones = base.timezones
  440. }
  441. // make sure all base timezones are part of sub locale timezones
  442. if found {
  443. var ok bool
  444. for k, v := range base.timezones {
  445. if _, ok = trans.timezones[k]; ok {
  446. continue
  447. }
  448. trans.timezones[k] = v
  449. }
  450. }
  451. ApplyOverrides(trans)
  452. parseDecimalNumberFormat(trans)
  453. parsePercentNumberFormat(trans)
  454. parseCurrencyNumberFormat(trans)
  455. }
  456. for _, trans := range translators {
  457. fmt.Println("Final Processing:", trans.Locale)
  458. // if it's still nill.....
  459. if trans.timezones == nil {
  460. trans.timezones = make(map[string]*zoneAbbrev)
  461. }
  462. tz := make(map[string]string) // key = abbrev locale eg. EST, EDT, MST, PST... value = long locale eg. Eastern Standard Time, Pacific Time.....
  463. for k, v := range timezones {
  464. ttz, ok := trans.timezones[k]
  465. if !ok {
  466. ttz = v
  467. trans.timezones[k] = v
  468. }
  469. tz[v.standard] = ttz.standard
  470. tz[v.daylight] = ttz.daylight
  471. }
  472. trans.FmtTimezones = fmt.Sprintf("%#v", tz)
  473. if len(trans.TimeSeparator) == 0 {
  474. trans.TimeSeparator = ":"
  475. }
  476. trans.FmtDateShort, trans.FmtDateMedium, trans.FmtDateLong, trans.FmtDateFull = parseDateFormats(trans, trans.FmtDateShort, trans.FmtDateMedium, trans.FmtDateLong, trans.FmtDateFull)
  477. trans.FmtTimeShort, trans.FmtTimeMedium, trans.FmtTimeLong, trans.FmtTimeFull = parseDateFormats(trans, trans.FmtTimeShort, trans.FmtTimeMedium, trans.FmtTimeLong, trans.FmtTimeFull)
  478. }
  479. }
  480. // preprocesses maps, array etc... just requires multiple passes no choice....
  481. func preProcess(cldrVar *cldr.CLDR) {
  482. for _, l := range cldrVar.Locales() {
  483. fmt.Println("Pre Processing:", l)
  484. split := strings.SplitN(l, "_", 2)
  485. baseLocale := split[0]
  486. trans := &translator{
  487. Locale: l,
  488. BaseLocale: baseLocale,
  489. }
  490. // if is a base locale
  491. if len(split) == 1 {
  492. baseTranslators[baseLocale] = trans
  493. }
  494. translators[l] = trans
  495. // get number, currency and datetime symbols
  496. // number values
  497. ldml := cldrVar.RawLDML(l)
  498. // some just have no data...
  499. if ldml.Numbers != nil {
  500. if len(ldml.Numbers.Symbols) > 0 {
  501. symbol := ldml.Numbers.Symbols[0]
  502. // Try to get the default numbering system instead of the first one
  503. systems := ldml.Numbers.DefaultNumberingSystem
  504. // There shouldn't really be more than one DefaultNumberingSystem
  505. if len(systems) > 0 {
  506. if dns := systems[0].Data(); dns != "" {
  507. for k := range ldml.Numbers.Symbols {
  508. if ldml.Numbers.Symbols[k].NumberSystem == dns {
  509. symbol = ldml.Numbers.Symbols[k]
  510. break
  511. }
  512. }
  513. }
  514. }
  515. if len(symbol.Decimal) > 0 {
  516. trans.Decimal = symbol.Decimal[0].Data()
  517. }
  518. if len(symbol.Group) > 0 {
  519. trans.Group = symbol.Group[0].Data()
  520. }
  521. if len(symbol.MinusSign) > 0 {
  522. trans.Minus = symbol.MinusSign[0].Data()
  523. }
  524. if len(symbol.PercentSign) > 0 {
  525. trans.Percent = symbol.PercentSign[0].Data()
  526. }
  527. if len(symbol.PerMille) > 0 {
  528. trans.PerMille = symbol.PerMille[0].Data()
  529. }
  530. if len(symbol.TimeSeparator) > 0 {
  531. trans.TimeSeparator = symbol.TimeSeparator[0].Data()
  532. }
  533. if len(symbol.Infinity) > 0 {
  534. trans.Infinity = symbol.Infinity[0].Data()
  535. }
  536. }
  537. if ldml.Numbers.Currencies != nil {
  538. for _, currency := range ldml.Numbers.Currencies.Currency {
  539. if len(strings.TrimSpace(currency.Type)) == 0 {
  540. continue
  541. }
  542. globalCurrenciesMap[currency.Type] = struct{}{}
  543. }
  544. }
  545. if len(ldml.Numbers.DecimalFormats) > 0 && len(ldml.Numbers.DecimalFormats[0].DecimalFormatLength) > 0 {
  546. for _, dfl := range ldml.Numbers.DecimalFormats[0].DecimalFormatLength {
  547. if len(dfl.Type) == 0 {
  548. trans.DecimalNumberFormat = dfl.DecimalFormat[0].Pattern[0].Data()
  549. break
  550. }
  551. }
  552. }
  553. if len(ldml.Numbers.PercentFormats) > 0 && len(ldml.Numbers.PercentFormats[0].PercentFormatLength) > 0 {
  554. for _, dfl := range ldml.Numbers.PercentFormats[0].PercentFormatLength {
  555. if len(dfl.Type) == 0 {
  556. trans.PercentNumberFormat = dfl.PercentFormat[0].Pattern[0].Data()
  557. break
  558. }
  559. }
  560. }
  561. if len(ldml.Numbers.CurrencyFormats) > 0 && len(ldml.Numbers.CurrencyFormats[0].CurrencyFormatLength) > 0 {
  562. if len(ldml.Numbers.CurrencyFormats[0].CurrencyFormatLength[0].CurrencyFormat) > 1 {
  563. split := strings.SplitN(ldml.Numbers.CurrencyFormats[0].CurrencyFormatLength[0].CurrencyFormat[1].Pattern[0].Data(), ";", 2)
  564. trans.CurrencyNumberFormat = split[0]
  565. if len(split) > 1 && len(split[1]) > 0 {
  566. trans.NegativeCurrencyNumberFormat = split[1]
  567. } else {
  568. trans.NegativeCurrencyNumberFormat = trans.CurrencyNumberFormat
  569. }
  570. } else {
  571. trans.CurrencyNumberFormat = ldml.Numbers.CurrencyFormats[0].CurrencyFormatLength[0].CurrencyFormat[0].Pattern[0].Data()
  572. trans.NegativeCurrencyNumberFormat = trans.CurrencyNumberFormat
  573. }
  574. }
  575. }
  576. if ldml.Dates != nil {
  577. if ldml.Dates.TimeZoneNames != nil {
  578. for _, zone := range ldml.Dates.TimeZoneNames.Metazone {
  579. for _, short := range zone.Short {
  580. if len(short.Standard) > 0 {
  581. za, ok := timezones[zone.Type]
  582. if !ok {
  583. za = new(zoneAbbrev)
  584. timezones[zone.Type] = za
  585. }
  586. za.standard = short.Standard[0].Data()
  587. }
  588. if len(short.Daylight) > 0 {
  589. za, ok := timezones[zone.Type]
  590. if !ok {
  591. za = new(zoneAbbrev)
  592. timezones[zone.Type] = za
  593. }
  594. za.daylight = short.Daylight[0].Data()
  595. }
  596. }
  597. for _, long := range zone.Long {
  598. if trans.timezones == nil {
  599. trans.timezones = make(map[string]*zoneAbbrev)
  600. }
  601. if len(long.Standard) > 0 {
  602. za, ok := trans.timezones[zone.Type]
  603. if !ok {
  604. za = new(zoneAbbrev)
  605. trans.timezones[zone.Type] = za
  606. }
  607. za.standard = long.Standard[0].Data()
  608. }
  609. za, ok := trans.timezones[zone.Type]
  610. if !ok {
  611. za = new(zoneAbbrev)
  612. trans.timezones[zone.Type] = za
  613. }
  614. if len(long.Daylight) > 0 {
  615. za.daylight = long.Daylight[0].Data()
  616. } else {
  617. za.daylight = za.standard
  618. }
  619. }
  620. }
  621. }
  622. if ldml.Dates.Calendars != nil {
  623. var calendar *cldr.Calendar
  624. for _, cal := range ldml.Dates.Calendars.Calendar {
  625. if cal.Type == "gregorian" {
  626. calendar = cal
  627. }
  628. }
  629. if calendar != nil {
  630. if calendar.DateFormats != nil {
  631. for _, datefmt := range calendar.DateFormats.DateFormatLength {
  632. switch datefmt.Type {
  633. case "full":
  634. trans.FmtDateFull = datefmt.DateFormat[0].Pattern[0].Data()
  635. case "long":
  636. trans.FmtDateLong = datefmt.DateFormat[0].Pattern[0].Data()
  637. case "medium":
  638. trans.FmtDateMedium = datefmt.DateFormat[0].Pattern[0].Data()
  639. case "short":
  640. trans.FmtDateShort = datefmt.DateFormat[0].Pattern[0].Data()
  641. }
  642. }
  643. }
  644. if calendar.TimeFormats != nil {
  645. for _, datefmt := range calendar.TimeFormats.TimeFormatLength {
  646. switch datefmt.Type {
  647. case "full":
  648. trans.FmtTimeFull = datefmt.TimeFormat[0].Pattern[0].Data()
  649. case "long":
  650. trans.FmtTimeLong = datefmt.TimeFormat[0].Pattern[0].Data()
  651. case "medium":
  652. trans.FmtTimeMedium = datefmt.TimeFormat[0].Pattern[0].Data()
  653. case "short":
  654. trans.FmtTimeShort = datefmt.TimeFormat[0].Pattern[0].Data()
  655. }
  656. }
  657. }
  658. if calendar.Months != nil {
  659. // month context starts at 'format', but there is also has 'stand-alone'
  660. // I'm making the decision to use the 'stand-alone' if, and only if,
  661. // the value does not exist in the 'format' month context
  662. var abbrSet, narrSet, wideSet bool
  663. for _, monthctx := range calendar.Months.MonthContext {
  664. for _, months := range monthctx.MonthWidth {
  665. var monthData []string
  666. for _, m := range months.Month {
  667. if len(m.Data()) == 0 {
  668. continue
  669. }
  670. switch m.Type {
  671. case "1":
  672. monthData = append(monthData, m.Data())
  673. case "2":
  674. monthData = append(monthData, m.Data())
  675. case "3":
  676. monthData = append(monthData, m.Data())
  677. case "4":
  678. monthData = append(monthData, m.Data())
  679. case "5":
  680. monthData = append(monthData, m.Data())
  681. case "6":
  682. monthData = append(monthData, m.Data())
  683. case "7":
  684. monthData = append(monthData, m.Data())
  685. case "8":
  686. monthData = append(monthData, m.Data())
  687. case "9":
  688. monthData = append(monthData, m.Data())
  689. case "10":
  690. monthData = append(monthData, m.Data())
  691. case "11":
  692. monthData = append(monthData, m.Data())
  693. case "12":
  694. monthData = append(monthData, m.Data())
  695. }
  696. }
  697. if len(monthData) > 0 {
  698. // making array indexes line up with month values
  699. // so I'll have an extra empty value, it's way faster
  700. // than a switch over all type values...
  701. monthData = append(make([]string, 1, len(monthData)+1), monthData...)
  702. switch months.Type {
  703. case "abbreviated":
  704. if !abbrSet {
  705. abbrSet = true
  706. trans.FmtMonthsAbbreviated = fmt.Sprintf("%#v", monthData)
  707. }
  708. case "narrow":
  709. if !narrSet {
  710. narrSet = true
  711. trans.FmtMonthsNarrow = fmt.Sprintf("%#v", monthData)
  712. }
  713. case "wide":
  714. if !wideSet {
  715. wideSet = true
  716. trans.FmtMonthsWide = fmt.Sprintf("%#v", monthData)
  717. }
  718. }
  719. }
  720. }
  721. }
  722. }
  723. if calendar.Days != nil {
  724. // day context starts at 'format', but there is also has 'stand-alone'
  725. // I'm making the decision to use the 'stand-alone' if, and only if,
  726. // the value does not exist in the 'format' day context
  727. var abbrSet, narrSet, shortSet, wideSet bool
  728. for _, dayctx := range calendar.Days.DayContext {
  729. for _, days := range dayctx.DayWidth {
  730. var dayData []string
  731. for _, d := range days.Day {
  732. switch d.Type {
  733. case "sun":
  734. dayData = append(dayData, d.Data())
  735. case "mon":
  736. dayData = append(dayData, d.Data())
  737. case "tue":
  738. dayData = append(dayData, d.Data())
  739. case "wed":
  740. dayData = append(dayData, d.Data())
  741. case "thu":
  742. dayData = append(dayData, d.Data())
  743. case "fri":
  744. dayData = append(dayData, d.Data())
  745. case "sat":
  746. dayData = append(dayData, d.Data())
  747. }
  748. }
  749. if len(dayData) > 0 {
  750. switch days.Type {
  751. case "abbreviated":
  752. if !abbrSet {
  753. abbrSet = true
  754. trans.FmtDaysAbbreviated = fmt.Sprintf("%#v", dayData)
  755. }
  756. case "narrow":
  757. if !narrSet {
  758. narrSet = true
  759. trans.FmtDaysNarrow = fmt.Sprintf("%#v", dayData)
  760. }
  761. case "short":
  762. if !shortSet {
  763. shortSet = true
  764. trans.FmtDaysShort = fmt.Sprintf("%#v", dayData)
  765. }
  766. case "wide":
  767. if !wideSet {
  768. wideSet = true
  769. trans.FmtDaysWide = fmt.Sprintf("%#v", dayData)
  770. }
  771. }
  772. }
  773. }
  774. }
  775. }
  776. if calendar.DayPeriods != nil {
  777. // day periods context starts at 'format', but there is also has 'stand-alone'
  778. // I'm making the decision to use the 'stand-alone' if, and only if,
  779. // the value does not exist in the 'format' day period context
  780. var abbrSet, narrSet, shortSet, wideSet bool
  781. for _, ctx := range calendar.DayPeriods.DayPeriodContext {
  782. for _, width := range ctx.DayPeriodWidth {
  783. // [0] = AM
  784. // [0] = PM
  785. ampm := make([]string, 2, 2)
  786. for _, d := range width.DayPeriod {
  787. if d.Type == "am" {
  788. ampm[0] = d.Data()
  789. continue
  790. }
  791. if d.Type == "pm" {
  792. ampm[1] = d.Data()
  793. }
  794. }
  795. switch width.Type {
  796. case "abbreviated":
  797. if !abbrSet {
  798. abbrSet = true
  799. trans.FmtPeriodsAbbreviated = fmt.Sprintf("%#v", ampm)
  800. }
  801. case "narrow":
  802. if !narrSet {
  803. narrSet = true
  804. trans.FmtPeriodsNarrow = fmt.Sprintf("%#v", ampm)
  805. }
  806. case "short":
  807. if !shortSet {
  808. shortSet = true
  809. trans.FmtPeriodsShort = fmt.Sprintf("%#v", ampm)
  810. }
  811. case "wide":
  812. if !wideSet {
  813. wideSet = true
  814. trans.FmtPeriodsWide = fmt.Sprintf("%#v", ampm)
  815. }
  816. }
  817. }
  818. }
  819. }
  820. if calendar.Eras != nil {
  821. // [0] = BC
  822. // [0] = AD
  823. abbrev := make([]string, 2, 2)
  824. narr := make([]string, 2, 2)
  825. wide := make([]string, 2, 2)
  826. if calendar.Eras.EraAbbr != nil {
  827. if len(calendar.Eras.EraAbbr.Era) == 4 {
  828. abbrev[0] = calendar.Eras.EraAbbr.Era[0].Data()
  829. abbrev[1] = calendar.Eras.EraAbbr.Era[2].Data()
  830. } else if len(calendar.Eras.EraAbbr.Era) == 2 {
  831. abbrev[0] = calendar.Eras.EraAbbr.Era[0].Data()
  832. abbrev[1] = calendar.Eras.EraAbbr.Era[1].Data()
  833. }
  834. }
  835. if calendar.Eras.EraNarrow != nil {
  836. if len(calendar.Eras.EraNarrow.Era) == 4 {
  837. narr[0] = calendar.Eras.EraNarrow.Era[0].Data()
  838. narr[1] = calendar.Eras.EraNarrow.Era[2].Data()
  839. } else if len(calendar.Eras.EraNarrow.Era) == 2 {
  840. narr[0] = calendar.Eras.EraNarrow.Era[0].Data()
  841. narr[1] = calendar.Eras.EraNarrow.Era[1].Data()
  842. }
  843. }
  844. if calendar.Eras.EraNames != nil {
  845. if len(calendar.Eras.EraNames.Era) == 4 {
  846. wide[0] = calendar.Eras.EraNames.Era[0].Data()
  847. wide[1] = calendar.Eras.EraNames.Era[2].Data()
  848. } else if len(calendar.Eras.EraNames.Era) == 2 {
  849. wide[0] = calendar.Eras.EraNames.Era[0].Data()
  850. wide[1] = calendar.Eras.EraNames.Era[1].Data()
  851. }
  852. }
  853. trans.FmtErasAbbreviated = fmt.Sprintf("%#v", abbrev)
  854. trans.FmtErasNarrow = fmt.Sprintf("%#v", narr)
  855. trans.FmtErasWide = fmt.Sprintf("%#v", wide)
  856. }
  857. }
  858. }
  859. }
  860. }
  861. for k := range globalCurrenciesMap {
  862. globalCurrencies = append(globalCurrencies, k)
  863. }
  864. sort.Strings(globalCurrencies)
  865. for i, loc := range globalCurrencies {
  866. globCurrencyIdxMap[loc] = i
  867. }
  868. }
  869. func parseDateFormats(trans *translator, shortFormat, mediumFormat, longFormat, fullFormat string) (short, medium, long, full string) {
  870. // Short Date Parsing
  871. short = parseDateTimeFormat(trans.BaseLocale, shortFormat, 2)
  872. medium = parseDateTimeFormat(trans.BaseLocale, mediumFormat, 2)
  873. long = parseDateTimeFormat(trans.BaseLocale, longFormat, 1)
  874. full = parseDateTimeFormat(trans.BaseLocale, fullFormat, 0)
  875. // End Short Data Parsing
  876. return
  877. }
  878. func parseDateTimeFormat(baseLocale, format string, eraScore uint8) (results string) {
  879. // rules:
  880. // y = four digit year
  881. // yy = two digit year
  882. // var b []byte
  883. var inConstantText bool
  884. var start int
  885. for i := 0; i < len(format); i++ {
  886. switch format[i] {
  887. // time separator
  888. case ':':
  889. if inConstantText {
  890. inConstantText = false
  891. results += "b = append(b, " + fmt.Sprintf("%#v", []byte(format[start:i])) + "...)\n"
  892. }
  893. results += "b = append(b, " + baseLocale + ".timeSeparator...)"
  894. case '\'':
  895. i++
  896. startI := i
  897. // peek to see if ''
  898. if len(format) != i && format[i] == '\'' {
  899. if inConstantText {
  900. inConstantText = false
  901. results += "b = append(b, " + fmt.Sprintf("%#v", []byte(format[start:i-1])) + "...)\n"
  902. } else {
  903. inConstantText = true
  904. start = i
  905. }
  906. continue
  907. }
  908. // not '' so whatever comes between '' is constant
  909. if len(format) != i {
  910. // advance i to the next single quote + 1
  911. for ; i < len(format); i++ {
  912. if format[i] == '\'' {
  913. if inConstantText {
  914. inConstantText = false
  915. b := []byte(format[start : startI-1])
  916. b = append(b, []byte(format[startI:i])...)
  917. results += "b = append(b, " + fmt.Sprintf("%#v", b) + "...)\n"
  918. } else {
  919. results += "b = append(b, " + fmt.Sprintf("%#v", []byte(format[startI:i])) + "...)\n"
  920. }
  921. break
  922. }
  923. }
  924. }
  925. // 24 hour
  926. case 'H':
  927. if inConstantText {
  928. inConstantText = false
  929. results += "b = append(b, " + fmt.Sprintf("%#v", []byte(format[start:i])) + "...)\n"
  930. }
  931. // peek
  932. // two digit hour required?
  933. if len(format) != i+1 && format[i+1] == 'H' {
  934. i++
  935. results += `
  936. if t.Hour() < 10 {
  937. b = append(b, '0')
  938. }
  939. `
  940. }
  941. results += "b = strconv.AppendInt(b, int64(t.Hour()), 10)\n"
  942. // hour
  943. case 'h':
  944. if inConstantText {
  945. inConstantText = false
  946. results += "b = append(b, " + fmt.Sprintf("%#v", []byte(format[start:i])) + "...)\n"
  947. }
  948. results += `
  949. h := t.Hour()
  950. if h > 12 {
  951. h -= 12
  952. }
  953. `
  954. // peek
  955. // two digit hour required?
  956. if len(format) != i+1 && format[i+1] == 'h' {
  957. i++
  958. results += `
  959. if h < 10 {
  960. b = append(b, '0')
  961. }
  962. `
  963. }
  964. results += "b = strconv.AppendInt(b, int64(h), 10)\n"
  965. // minute
  966. case 'm':
  967. if inConstantText {
  968. inConstantText = false
  969. results += "b = append(b, " + fmt.Sprintf("%#v", []byte(format[start:i])) + "...)\n"
  970. }
  971. // peek
  972. // two digit minute required?
  973. if len(format) != i+1 && format[i+1] == 'm' {
  974. i++
  975. results += `
  976. if t.Minute() < 10 {
  977. b = append(b, '0')
  978. }
  979. `
  980. }
  981. results += "b = strconv.AppendInt(b, int64(t.Minute()), 10)\n"
  982. // second
  983. case 's':
  984. if inConstantText {
  985. inConstantText = false
  986. results += "b = append(b, " + fmt.Sprintf("%#v", []byte(format[start:i])) + "...)\n"
  987. }
  988. // peek
  989. // two digit minute required?
  990. if len(format) != i+1 && format[i+1] == 's' {
  991. i++
  992. results += `
  993. if t.Second() < 10 {
  994. b = append(b, '0')
  995. }
  996. `
  997. }
  998. results += "b = strconv.AppendInt(b, int64(t.Second()), 10)\n"
  999. // day period
  1000. case 'a':
  1001. if inConstantText {
  1002. inConstantText = false
  1003. results += "b = append(b, " + fmt.Sprintf("%#v", []byte(format[start:i])) + "...)\n"
  1004. }
  1005. // only used with 'h', patterns should not contains 'a' without 'h' so not checking
  1006. // choosing to use abbreviated, didn't see any rules about which should be used with which
  1007. // date format....
  1008. results += `
  1009. if t.Hour() < 12 {
  1010. b = append(b, ` + baseLocale + `.periodsAbbreviated[0]...)
  1011. } else {
  1012. b = append(b, ` + baseLocale + `.periodsAbbreviated[1]...)
  1013. }
  1014. `
  1015. // timezone
  1016. case 'z', 'v':
  1017. if inConstantText {
  1018. inConstantText = false
  1019. results += "b = append(b, " + fmt.Sprintf("%#v", []byte(format[start:i])) + "...)\n"
  1020. }
  1021. // consume multiple, only handling Abbrev tz from time.Time for the moment...
  1022. var count int
  1023. if format[i] == 'z' {
  1024. for j := i; j < len(format); j++ {
  1025. if format[j] == 'z' {
  1026. count++
  1027. } else {
  1028. break
  1029. }
  1030. }
  1031. }
  1032. if format[i] == 'v' {
  1033. for j := i; j < len(format); j++ {
  1034. if format[j] == 'v' {
  1035. count++
  1036. } else {
  1037. break
  1038. }
  1039. }
  1040. }
  1041. i += count - 1
  1042. // using the timezone on the Go time object, eg. EST, EDT, MST.....
  1043. if count < 4 {
  1044. results += `
  1045. tz, _ := t.Zone()
  1046. b = append(b, tz...)
  1047. `
  1048. } else {
  1049. results += `
  1050. tz, _ := t.Zone()
  1051. if btz, ok := ` + baseLocale + `.timezones[tz]; ok {
  1052. b = append(b, btz...)
  1053. } else {
  1054. b = append(b, tz...)
  1055. }
  1056. `
  1057. }
  1058. // day
  1059. case 'd':
  1060. if inConstantText {
  1061. inConstantText = false
  1062. results += "b = append(b, " + fmt.Sprintf("%#v", []byte(format[start:i])) + "...)\n"
  1063. }
  1064. // peek
  1065. // two digit day required?
  1066. if len(format) != i+1 && format[i+1] == 'd' {
  1067. i++
  1068. results += `
  1069. if t.Day() < 10 {
  1070. b = append(b, '0')
  1071. }
  1072. `
  1073. }
  1074. results += "b = strconv.AppendInt(b, int64(t.Day()), 10)\n"
  1075. // month
  1076. case 'M':
  1077. if inConstantText {
  1078. inConstantText = false
  1079. results += "b = append(b, " + fmt.Sprintf("%#v", []byte(format[start:i])) + "...)\n"
  1080. }
  1081. var count int
  1082. // count # of M's
  1083. for j := i; j < len(format); j++ {
  1084. if format[j] == 'M' {
  1085. count++
  1086. } else {
  1087. break
  1088. }
  1089. }
  1090. switch count {
  1091. // Numeric form, at least 1 digit
  1092. case 1:
  1093. results += "b = strconv.AppendInt(b, int64(t.Month()), 10)\n"
  1094. // Number form, at least 2 digits (padding with 0)
  1095. case 2:
  1096. results += `
  1097. if t.Month() < 10 {
  1098. b = append(b, '0')
  1099. }
  1100. b = strconv.AppendInt(b, int64(t.Month()), 10)
  1101. `
  1102. // Abbreviated form
  1103. case 3:
  1104. results += "b = append(b, " + baseLocale + ".monthsAbbreviated[t.Month()]...)\n"
  1105. // Full/Wide form
  1106. case 4:
  1107. results += "b = append(b, " + baseLocale + ".monthsWide[t.Month()]...)\n"
  1108. // Narrow form - only used in where context makes it clear, such as headers in a calendar.
  1109. // Should be one character wherever possible.
  1110. case 5:
  1111. results += "b = append(b, " + baseLocale + ".monthsNarrow[t.Month()]...)\n"
  1112. }
  1113. // skip over M's
  1114. i += count - 1
  1115. // year
  1116. case 'y':
  1117. if inConstantText {
  1118. inConstantText = false
  1119. results += "b = append(b, " + fmt.Sprintf("%#v", []byte(format[start:i])) + "...)\n"
  1120. }
  1121. // peek
  1122. // two digit year
  1123. if len(format) != i+1 && format[i+1] == 'y' {
  1124. i++
  1125. results += `
  1126. if t.Year() > 9 {
  1127. b = append(b, strconv.Itoa(t.Year())[2:]...)
  1128. } else {
  1129. b = append(b, strconv.Itoa(t.Year())[1:]...)
  1130. }
  1131. `
  1132. } else {
  1133. // four digit year
  1134. results += `
  1135. if t.Year() > 0 {
  1136. b = strconv.AppendInt(b, int64(t.Year()), 10)
  1137. } else {
  1138. b = strconv.AppendInt(b, int64(t.Year()*-1), 10)
  1139. }
  1140. `
  1141. }
  1142. // weekday
  1143. // I know I only see 'EEEE' in the xml, but just in case handled all posibilities
  1144. case 'E':
  1145. if inConstantText {
  1146. inConstantText = false
  1147. results += "b = append(b, " + fmt.Sprintf("%#v", []byte(format[start:i])) + "...)\n"
  1148. }
  1149. var count int
  1150. // count # of E's
  1151. for j := i; j < len(format); j++ {
  1152. if format[j] == 'E' {
  1153. count++
  1154. } else {
  1155. break
  1156. }
  1157. }
  1158. switch count {
  1159. // Narrow
  1160. case 1:
  1161. results += "b = append(b, " + baseLocale + ".daysNarrow[t.Weekday()]...)\n"
  1162. // Short
  1163. case 2:
  1164. results += "b = append(b, " + baseLocale + ".daysShort[t.Weekday()]...)\n"
  1165. // Abbreviated
  1166. case 3:
  1167. results += "b = append(b, " + baseLocale + ".daysAbbreviated[t.Weekday()]...)\n"
  1168. // Full/Wide
  1169. case 4:
  1170. results += "b = append(b, " + baseLocale + ".daysWide[t.Weekday()]...)\n"
  1171. }
  1172. // skip over E's
  1173. i += count - 1
  1174. // era eg. AD, BC
  1175. case 'G':
  1176. if inConstantText {
  1177. inConstantText = false
  1178. results += "b = append(b, " + fmt.Sprintf("%#v", []byte(format[start:i])) + "...)\n"
  1179. }
  1180. switch eraScore {
  1181. case 0:
  1182. results += `
  1183. if t.Year() < 0 {
  1184. b = append(b, ` + baseLocale + `.erasWide[0]...)
  1185. } else {
  1186. b = append(b, ` + baseLocale + `.erasWide[1]...)
  1187. }
  1188. `
  1189. case 1, 2:
  1190. results += `
  1191. if t.Year() < 0 {
  1192. b = append(b, ` + baseLocale + `.erasAbbreviated[0]...)
  1193. } else {
  1194. b = append(b, ` + baseLocale + `.erasAbbreviated[1]...)
  1195. }
  1196. `
  1197. }
  1198. default:
  1199. // append all non matched text as they are constants
  1200. if !inConstantText {
  1201. inConstantText = true
  1202. start = i
  1203. }
  1204. }
  1205. }
  1206. // if we were inConstantText when the string ended, add what's left.
  1207. if inConstantText {
  1208. // inContantText = false
  1209. results += "b = append(b, " + fmt.Sprintf("%#v", []byte(format[start:])) + "...)\n"
  1210. }
  1211. return
  1212. }
  1213. func parseCurrencyNumberFormat(trans *translator) {
  1214. if len(trans.CurrencyNumberFormat) == 0 {
  1215. return
  1216. }
  1217. trans.FmtCurrencyExists = true
  1218. negativeEqual := trans.CurrencyNumberFormat == trans.NegativeCurrencyNumberFormat
  1219. match := groupLenRegex.FindString(trans.CurrencyNumberFormat)
  1220. if len(match) > 0 {
  1221. trans.FmtCurrencyGroupLen = len(match) - 2
  1222. }
  1223. match = requiredDecimalRegex.FindString(trans.CurrencyNumberFormat)
  1224. if len(match) > 0 {
  1225. trans.FmtCurrencyMinDecimalLen = len(match) - 1
  1226. }
  1227. match = secondaryGroupLenRegex.FindString(trans.CurrencyNumberFormat)
  1228. if len(match) > 0 {
  1229. trans.FmtCurrencySecondaryGroupLen = len(match) - 2
  1230. }
  1231. idx := 0
  1232. for idx = 0; idx < len(trans.CurrencyNumberFormat); idx++ {
  1233. if trans.CurrencyNumberFormat[idx] == '#' || trans.CurrencyNumberFormat[idx] == '0' {
  1234. trans.FmtCurrencyPrefix = trans.CurrencyNumberFormat[:idx]
  1235. break
  1236. }
  1237. }
  1238. for idx = len(trans.CurrencyNumberFormat) - 1; idx >= 0; idx-- {
  1239. if trans.CurrencyNumberFormat[idx] == '#' || trans.CurrencyNumberFormat[idx] == '0' {
  1240. idx++
  1241. trans.FmtCurrencySuffix = trans.CurrencyNumberFormat[idx:]
  1242. break
  1243. }
  1244. }
  1245. for idx = 0; idx < len(trans.FmtCurrencyPrefix); idx++ {
  1246. if trans.FmtCurrencyPrefix[idx] == '¤' {
  1247. trans.FmtCurrencyInPrefix = true
  1248. trans.FmtCurrencyPrefix = strings.Replace(trans.FmtCurrencyPrefix, string(trans.FmtCurrencyPrefix[idx]), "", 1)
  1249. if idx == 0 {
  1250. trans.FmtCurrencyLeft = true
  1251. } else {
  1252. trans.FmtCurrencyLeft = false
  1253. }
  1254. break
  1255. }
  1256. }
  1257. for idx = 0; idx < len(trans.FmtCurrencySuffix); idx++ {
  1258. if trans.FmtCurrencySuffix[idx] == '¤' {
  1259. trans.FmtCurrencyInPrefix = false
  1260. trans.FmtCurrencySuffix = strings.Replace(trans.FmtCurrencySuffix, string(trans.FmtCurrencySuffix[idx]), "", 1)
  1261. if idx == 0 {
  1262. trans.FmtCurrencyLeft = true
  1263. } else {
  1264. trans.FmtCurrencyLeft = false
  1265. }
  1266. break
  1267. }
  1268. }
  1269. // if len(trans.FmtCurrencyPrefix) > 0 {
  1270. // trans.FmtCurrencyPrefix = fmt.Sprintf("%#v", []byte(trans.FmtCurrencyPrefix))
  1271. // }
  1272. // if len(trans.FmtCurrencySuffix) > 0 {
  1273. // trans.FmtCurrencySuffix = fmt.Sprintf("%#v", []byte(trans.FmtCurrencySuffix))
  1274. // }
  1275. // no need to parse again if true....
  1276. if negativeEqual {
  1277. trans.FmtCurrencyNegativePrefix = trans.FmtCurrencyPrefix
  1278. trans.FmtCurrencyNegativeSuffix = trans.FmtCurrencySuffix
  1279. trans.FmtCurrencyNegativeInPrefix = trans.FmtCurrencyInPrefix
  1280. trans.FmtCurrencyNegativeLeft = trans.FmtCurrencyLeft
  1281. return
  1282. }
  1283. trans.FmtCurrencyNegativeExists = true
  1284. for idx = 0; idx < len(trans.NegativeCurrencyNumberFormat); idx++ {
  1285. if trans.NegativeCurrencyNumberFormat[idx] == '#' || trans.NegativeCurrencyNumberFormat[idx] == '0' {
  1286. trans.FmtCurrencyNegativePrefix = trans.NegativeCurrencyNumberFormat[:idx]
  1287. break
  1288. }
  1289. }
  1290. for idx = len(trans.NegativeCurrencyNumberFormat) - 1; idx >= 0; idx-- {
  1291. if trans.NegativeCurrencyNumberFormat[idx] == '#' || trans.NegativeCurrencyNumberFormat[idx] == '0' {
  1292. idx++
  1293. trans.FmtCurrencyNegativeSuffix = trans.NegativeCurrencyNumberFormat[idx:]
  1294. break
  1295. }
  1296. }
  1297. for idx = 0; idx < len(trans.FmtCurrencyNegativePrefix); idx++ {
  1298. if trans.FmtCurrencyNegativePrefix[idx] == '¤' {
  1299. trans.FmtCurrencyNegativeInPrefix = true
  1300. trans.FmtCurrencyNegativePrefix = strings.Replace(trans.FmtCurrencyNegativePrefix, string(trans.FmtCurrencyNegativePrefix[idx]), "", 1)
  1301. if idx == 0 {
  1302. trans.FmtCurrencyNegativeLeft = true
  1303. } else {
  1304. trans.FmtCurrencyNegativeLeft = false
  1305. }
  1306. break
  1307. }
  1308. }
  1309. for idx = 0; idx < len(trans.FmtCurrencyNegativeSuffix); idx++ {
  1310. if trans.FmtCurrencyNegativeSuffix[idx] == '¤' {
  1311. trans.FmtCurrencyNegativeInPrefix = false
  1312. trans.FmtCurrencyNegativeSuffix = strings.Replace(trans.FmtCurrencyNegativeSuffix, string(trans.FmtCurrencyNegativeSuffix[idx]), "", 1)
  1313. if idx == 0 {
  1314. trans.FmtCurrencyNegativeLeft = true
  1315. } else {
  1316. trans.FmtCurrencyNegativeLeft = false
  1317. }
  1318. break
  1319. }
  1320. }
  1321. // if len(trans.FmtCurrencyNegativePrefix) > 0 {
  1322. // trans.FmtCurrencyNegativePrefix = fmt.Sprintf("%#v", []byte(trans.FmtCurrencyNegativePrefix))
  1323. // }
  1324. // if len(trans.FmtCurrencyNegativeSuffix) > 0 {
  1325. // trans.FmtCurrencyNegativeSuffix = fmt.Sprintf("%#v", []byte(trans.FmtCurrencyNegativeSuffix))
  1326. // }
  1327. return
  1328. }
  1329. func parsePercentNumberFormat(trans *translator) {
  1330. if len(trans.PercentNumberFormat) == 0 {
  1331. return
  1332. }
  1333. trans.FmtPercentExists = true
  1334. match := groupLenPercentRegex.FindString(trans.PercentNumberFormat)
  1335. if len(match) > 0 {
  1336. trans.FmtPercentGroupLen = len(match) - 1
  1337. }
  1338. match = requiredDecimalRegex.FindString(trans.PercentNumberFormat)
  1339. if len(match) > 0 {
  1340. trans.FmtPercentMinDecimalLen = len(match) - 1
  1341. }
  1342. match = secondaryGroupLenRegex.FindString(trans.PercentNumberFormat)
  1343. if len(match) > 0 {
  1344. trans.FmtPercentSecondaryGroupLen = len(match) - 2
  1345. }
  1346. idx := 0
  1347. for idx = 0; idx < len(trans.PercentNumberFormat); idx++ {
  1348. if trans.PercentNumberFormat[idx] == '#' || trans.PercentNumberFormat[idx] == '0' {
  1349. trans.FmtPercentPrefix = trans.PercentNumberFormat[:idx]
  1350. break
  1351. }
  1352. }
  1353. for idx = len(trans.PercentNumberFormat) - 1; idx >= 0; idx-- {
  1354. if trans.PercentNumberFormat[idx] == '#' || trans.PercentNumberFormat[idx] == '0' {
  1355. idx++
  1356. trans.FmtPercentSuffix = trans.PercentNumberFormat[idx:]
  1357. break
  1358. }
  1359. }
  1360. for idx = 0; idx < len(trans.FmtPercentPrefix); idx++ {
  1361. if trans.FmtPercentPrefix[idx] == '%' {
  1362. trans.FmtPercentInPrefix = true
  1363. trans.FmtPercentPrefix = strings.Replace(trans.FmtPercentPrefix, string(trans.FmtPercentPrefix[idx]), "", 1)
  1364. if idx == 0 {
  1365. trans.FmtPercentLeft = true
  1366. } else {
  1367. trans.FmtPercentLeft = false
  1368. }
  1369. break
  1370. }
  1371. }
  1372. for idx = 0; idx < len(trans.FmtPercentSuffix); idx++ {
  1373. if trans.FmtPercentSuffix[idx] == '%' {
  1374. trans.FmtPercentInPrefix = false
  1375. trans.FmtPercentSuffix = strings.Replace(trans.FmtPercentSuffix, string(trans.FmtPercentSuffix[idx]), "", 1)
  1376. if idx == 0 {
  1377. trans.FmtPercentLeft = true
  1378. } else {
  1379. trans.FmtPercentLeft = false
  1380. }
  1381. break
  1382. }
  1383. }
  1384. // if len(trans.FmtPercentPrefix) > 0 {
  1385. // trans.FmtPercentPrefix = fmt.Sprintf("%#v", []byte(trans.FmtPercentPrefix))
  1386. // }
  1387. // if len(trans.FmtPercentSuffix) > 0 {
  1388. // trans.FmtPercentSuffix = fmt.Sprintf("%#v", []byte(trans.FmtPercentSuffix))
  1389. // }
  1390. return
  1391. }
  1392. func parseDecimalNumberFormat(trans *translator) {
  1393. if len(trans.DecimalNumberFormat) == 0 {
  1394. return
  1395. }
  1396. trans.FmtNumberExists = true
  1397. formats := strings.SplitN(trans.DecimalNumberFormat, ";", 2)
  1398. match := groupLenRegex.FindString(formats[0])
  1399. if len(match) > 0 {
  1400. trans.FmtNumberGroupLen = len(match) - 2
  1401. }
  1402. match = requiredDecimalRegex.FindString(formats[0])
  1403. if len(match) > 0 {
  1404. trans.FmtNumberMinDecimalLen = len(match) - 1
  1405. }
  1406. match = secondaryGroupLenRegex.FindString(formats[0])
  1407. if len(match) > 0 {
  1408. trans.FmtNumberSecondaryGroupLen = len(match) - 2
  1409. }
  1410. return
  1411. }
  1412. type sortRank struct {
  1413. Rank uint8
  1414. Value string
  1415. }
  1416. type ByRank []sortRank
  1417. func (a ByRank) Len() int { return len(a) }
  1418. func (a ByRank) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
  1419. func (a ByRank) Less(i, j int) bool { return a[i].Rank < a[j].Rank }
  1420. type ByPluralRule []locales.PluralRule
  1421. func (a ByPluralRule) Len() int { return len(a) }
  1422. func (a ByPluralRule) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
  1423. func (a ByPluralRule) Less(i, j int) bool { return a[i] < a[j] }
  1424. // TODO: refine generated code a bit, some combinations end up with same plural rule,
  1425. // could check all at once; but it works and that's step 1 complete
  1426. func parseRangePluralRuleFunc(current *cldr.CLDR, baseLocale string) (results string, plurals string) {
  1427. var pluralRange *struct {
  1428. cldr.Common
  1429. Locales string `xml:"locales,attr"`
  1430. PluralRange []*struct {
  1431. cldr.Common
  1432. Start string `xml:"start,attr"`
  1433. End string `xml:"end,attr"`
  1434. Result string `xml:"result,attr"`
  1435. } `xml:"pluralRange"`
  1436. }
  1437. var pluralArr []locales.PluralRule
  1438. for _, pr := range current.Supplemental().Plurals[1].PluralRanges {
  1439. locs := strings.Split(pr.Locales, " ")
  1440. for _, loc := range locs {
  1441. if loc == baseLocale {
  1442. pluralRange = pr
  1443. }
  1444. }
  1445. }
  1446. // no range plural rules for locale
  1447. if pluralRange == nil {
  1448. plurals = "nil"
  1449. results = "return locales.PluralRuleUnknown"
  1450. return
  1451. }
  1452. mp := make(map[string]struct{})
  1453. // pre-process if all the same
  1454. for _, rule := range pluralRange.PluralRange {
  1455. mp[rule.Result] = struct{}{}
  1456. }
  1457. for k := range mp {
  1458. psI := pluralStringToInt(k)
  1459. pluralArr = append(pluralArr, psI)
  1460. }
  1461. if len(mp) == 1 {
  1462. results += "return locales." + pluralStringToString(pluralRange.PluralRange[0].Result)
  1463. plurals = fmt.Sprintf("%#v", pluralArr)
  1464. return
  1465. }
  1466. multiple := len(pluralRange.PluralRange) > 1
  1467. if multiple {
  1468. results += "start := " + baseLocale + ".CardinalPluralRule(num1, v1)\n"
  1469. results += "end := " + baseLocale + ".CardinalPluralRule(num2, v2)\n\n"
  1470. }
  1471. first := true
  1472. // pre parse for variables
  1473. for i, rule := range pluralRange.PluralRange {
  1474. if i == len(pluralRange.PluralRange)-1 {
  1475. if multiple {
  1476. results += "\n\n"
  1477. }
  1478. results += "return locales." + pluralStringToString(rule.Result)
  1479. continue
  1480. }
  1481. if first {
  1482. results += "if"
  1483. first = false
  1484. } else {
  1485. results += "else if"
  1486. }
  1487. results += " start == locales." + pluralStringToString(rule.Start) + " && end == locales." + pluralStringToString(rule.End) + " {\n return locales." + pluralStringToString(rule.Result) + "\n} "
  1488. }
  1489. if multiple {
  1490. results = "\n" + results + "\n"
  1491. }
  1492. if len(pluralArr) == 0 {
  1493. plurals = "nil"
  1494. } else {
  1495. ints := make([]int, len(pluralArr))
  1496. for i := 0; i < len(pluralArr); i++ {
  1497. ints[i] = int(pluralArr[i])
  1498. }
  1499. sort.Ints(ints)
  1500. for i := 0; i < len(ints); i++ {
  1501. pluralArr[i] = locales.PluralRule(ints[i])
  1502. }
  1503. plurals = fmt.Sprintf("%#v", pluralArr)
  1504. }
  1505. return
  1506. }
  1507. // TODO: cleanup function logic perhaps write a lexer... but it's working right now, and
  1508. // I'm already farther down the rabbit hole than I'd like and so pulling the chute here.
  1509. func parseOrdinalPluralRuleFunc(current *cldr.CLDR, baseLocale string) (results string, plurals string) {
  1510. var prOrdinal *struct {
  1511. cldr.Common
  1512. Locales string "xml:\"locales,attr\""
  1513. PluralRule []*struct {
  1514. cldr.Common
  1515. Count string "xml:\"count,attr\""
  1516. } "xml:\"pluralRule\""
  1517. }
  1518. var pluralArr []locales.PluralRule
  1519. // idx 0 is ordinal rules
  1520. for _, pr := range current.Supplemental().Plurals[0].PluralRules {
  1521. locs := strings.Split(pr.Locales, " ")
  1522. for _, loc := range locs {
  1523. if loc == baseLocale {
  1524. prOrdinal = pr
  1525. // for _, pl := range pr.PluralRule {
  1526. // fmt.Println(pl.Count, pl.Common.Data())
  1527. // }
  1528. }
  1529. }
  1530. }
  1531. // no plural rules for locale
  1532. if prOrdinal == nil {
  1533. plurals = "nil"
  1534. results = "return locales.PluralRuleUnknown"
  1535. return
  1536. }
  1537. vals := make(map[string]struct{})
  1538. first := true
  1539. // pre parse for variables
  1540. for _, rule := range prOrdinal.PluralRule {
  1541. ps1 := pluralStringToString(rule.Count)
  1542. psI := pluralStringToInt(rule.Count)
  1543. pluralArr = append(pluralArr, psI)
  1544. data := strings.Replace(strings.Replace(strings.Replace(strings.TrimSpace(strings.SplitN(rule.Common.Data(), "@", 2)[0]), " = ", " == ", -1), " or ", " || ", -1), " and ", " && ", -1)
  1545. if len(data) == 0 {
  1546. if len(prOrdinal.PluralRule) == 1 {
  1547. results = "return locales." + ps1
  1548. } else {
  1549. results += "\n\nreturn locales." + ps1
  1550. // results += "else {\nreturn locales." + locales.PluralStringToString(rule.Count) + ", nil\n}"
  1551. }
  1552. continue
  1553. }
  1554. // // All need n, so always add
  1555. // if strings.Contains(data, "n") {
  1556. // vals[prVarFuncs["n"]] = struct{}{}
  1557. // }
  1558. if strings.Contains(data, "i") {
  1559. vals[prVarFuncs["i"]] = struct{}{}
  1560. }
  1561. // v is inherently avaialable as an argument
  1562. // if strings.Contains(data, "v") {
  1563. // vals[prVarFuncs["v"]] = struct{}{}
  1564. // }
  1565. if strings.Contains(data, "w") {
  1566. vals[prVarFuncs["w"]] = struct{}{}
  1567. }
  1568. if strings.Contains(data, "f") {
  1569. vals[prVarFuncs["f"]] = struct{}{}
  1570. }
  1571. if strings.Contains(data, "t") {
  1572. vals[prVarFuncs["t"]] = struct{}{}
  1573. }
  1574. if first {
  1575. results += "if "
  1576. first = false
  1577. } else {
  1578. results += "else if "
  1579. }
  1580. stmt := ""
  1581. // real work here
  1582. //
  1583. // split by 'or' then by 'and' allowing to better
  1584. // determine bracketing for formula
  1585. ors := strings.Split(data, "||")
  1586. for _, or := range ors {
  1587. stmt += "("
  1588. ands := strings.Split(strings.TrimSpace(or), "&&")
  1589. for _, and := range ands {
  1590. inArg := false
  1591. pre := ""
  1592. lft := ""
  1593. preOperator := ""
  1594. args := strings.Split(strings.TrimSpace(and), " ")
  1595. for _, a := range args {
  1596. if inArg {
  1597. // check to see if is a value range 2..9
  1598. multiRange := strings.Count(a, "..") > 1
  1599. cargs := strings.Split(strings.TrimSpace(a), ",")
  1600. hasBracket := len(cargs) > 1
  1601. bracketAdded := false
  1602. lastWasRange := false
  1603. for _, carg := range cargs {
  1604. if rng := strings.Split(carg, ".."); len(rng) > 1 {
  1605. if multiRange {
  1606. pre += " ("
  1607. } else {
  1608. pre += " "
  1609. }
  1610. switch preOperator {
  1611. case "==":
  1612. pre += lft + " >= " + rng[0] + " && " + lft + "<=" + rng[1]
  1613. case "!=":
  1614. pre += "(" + lft + " < " + rng[0] + " || " + lft + " > " + rng[1] + ")"
  1615. }
  1616. if multiRange {
  1617. pre += ") || "
  1618. } else {
  1619. pre += " || "
  1620. }
  1621. lastWasRange = true
  1622. continue
  1623. }
  1624. if lastWasRange {
  1625. pre = strings.TrimRight(pre, " || ") + " && "
  1626. }
  1627. lastWasRange = false
  1628. if hasBracket && !bracketAdded {
  1629. pre += "("
  1630. bracketAdded = true
  1631. }
  1632. // single comma separated values
  1633. switch preOperator {
  1634. case "==":
  1635. pre += " " + lft + preOperator + carg + " || "
  1636. case "!=":
  1637. pre += " " + lft + preOperator + carg + " && "
  1638. }
  1639. }
  1640. pre = strings.TrimRight(pre, " || ")
  1641. pre = strings.TrimRight(pre, " && ")
  1642. pre = strings.TrimRight(pre, " || ")
  1643. if hasBracket && bracketAdded {
  1644. pre += ")"
  1645. }
  1646. continue
  1647. }
  1648. if strings.Contains(a, "=") || a == ">" || a == "<" {
  1649. inArg = true
  1650. preOperator = a
  1651. continue
  1652. }
  1653. lft += a
  1654. }
  1655. stmt += pre + " && "
  1656. }
  1657. stmt = strings.TrimRight(stmt, " && ") + ") || "
  1658. }
  1659. stmt = strings.TrimRight(stmt, " || ")
  1660. results += stmt
  1661. results += " {\n"
  1662. // return plural rule here
  1663. results += "return locales." + ps1 + "\n"
  1664. results += "}"
  1665. }
  1666. pre := "\n"
  1667. // always needed
  1668. vals[prVarFuncs["n"]] = struct{}{}
  1669. sorted := make([]sortRank, 0, len(vals))
  1670. for k := range vals {
  1671. switch k[:1] {
  1672. case "n":
  1673. sorted = append(sorted, sortRank{
  1674. Value: prVarFuncs["n"],
  1675. Rank: 1,
  1676. })
  1677. case "i":
  1678. sorted = append(sorted, sortRank{
  1679. Value: prVarFuncs["i"],
  1680. Rank: 2,
  1681. })
  1682. case "w":
  1683. sorted = append(sorted, sortRank{
  1684. Value: prVarFuncs["w"],
  1685. Rank: 3,
  1686. })
  1687. case "f":
  1688. sorted = append(sorted, sortRank{
  1689. Value: prVarFuncs["f"],
  1690. Rank: 4,
  1691. })
  1692. case "t":
  1693. sorted = append(sorted, sortRank{
  1694. Value: prVarFuncs["t"],
  1695. Rank: 5,
  1696. })
  1697. }
  1698. }
  1699. sort.Sort(ByRank(sorted))
  1700. for _, k := range sorted {
  1701. pre += k.Value
  1702. }
  1703. if len(results) == 0 {
  1704. results = "return locales.PluralRuleUnknown"
  1705. } else {
  1706. if !strings.HasPrefix(results, "return") {
  1707. results = manyToSingleVars(results)
  1708. // pre += "\n"
  1709. results = pre + results
  1710. }
  1711. }
  1712. if len(pluralArr) == 0 {
  1713. plurals = "nil"
  1714. } else {
  1715. plurals = fmt.Sprintf("%#v", pluralArr)
  1716. }
  1717. return
  1718. }
  1719. // TODO: cleanup function logic perhaps write a lexer... but it's working right now, and
  1720. // I'm already farther down the rabbit hole than I'd like and so pulling the chute here.
  1721. func parseCardinalPluralRuleFunc(current *cldr.CLDR, baseLocale string) (results string, plurals string) {
  1722. var prCardinal *struct {
  1723. cldr.Common
  1724. Locales string "xml:\"locales,attr\""
  1725. PluralRule []*struct {
  1726. cldr.Common
  1727. Count string "xml:\"count,attr\""
  1728. } "xml:\"pluralRule\""
  1729. }
  1730. var pluralArr []locales.PluralRule
  1731. // idx 2 is cardinal rules
  1732. for _, pr := range current.Supplemental().Plurals[2].PluralRules {
  1733. locs := strings.Split(pr.Locales, " ")
  1734. for _, loc := range locs {
  1735. if loc == baseLocale {
  1736. prCardinal = pr
  1737. }
  1738. }
  1739. }
  1740. // no plural rules for locale
  1741. if prCardinal == nil {
  1742. plurals = "nil"
  1743. results = "return locales.PluralRuleUnknown"
  1744. return
  1745. }
  1746. vals := make(map[string]struct{})
  1747. first := true
  1748. // pre parse for variables
  1749. for _, rule := range prCardinal.PluralRule {
  1750. ps1 := pluralStringToString(rule.Count)
  1751. psI := pluralStringToInt(rule.Count)
  1752. pluralArr = append(pluralArr, psI)
  1753. data := strings.Replace(strings.Replace(strings.Replace(strings.TrimSpace(strings.SplitN(rule.Common.Data(), "@", 2)[0]), " = ", " == ", -1), " or ", " || ", -1), " and ", " && ", -1)
  1754. if len(data) == 0 {
  1755. if len(prCardinal.PluralRule) == 1 {
  1756. results = "return locales." + ps1
  1757. } else {
  1758. results += "\n\nreturn locales." + ps1
  1759. // results += "else {\nreturn locales." + locales.PluralStringToString(rule.Count) + ", nil\n}"
  1760. }
  1761. continue
  1762. }
  1763. // // All need n, so always add
  1764. // if strings.Contains(data, "n") {
  1765. // vals[prVarFuncs["n"]] = struct{}{}
  1766. // }
  1767. if strings.Contains(data, "i") {
  1768. vals[prVarFuncs["i"]] = struct{}{}
  1769. }
  1770. // v is inherently avaialable as an argument
  1771. // if strings.Contains(data, "v") {
  1772. // vals[prVarFuncs["v"]] = struct{}{}
  1773. // }
  1774. if strings.Contains(data, "w") {
  1775. vals[prVarFuncs["w"]] = struct{}{}
  1776. }
  1777. if strings.Contains(data, "f") {
  1778. vals[prVarFuncs["f"]] = struct{}{}
  1779. }
  1780. if strings.Contains(data, "t") {
  1781. vals[prVarFuncs["t"]] = struct{}{}
  1782. }
  1783. if first {
  1784. results += "if "
  1785. first = false
  1786. } else {
  1787. results += "else if "
  1788. }
  1789. stmt := ""
  1790. // real work here
  1791. //
  1792. // split by 'or' then by 'and' allowing to better
  1793. // determine bracketing for formula
  1794. ors := strings.Split(data, "||")
  1795. for _, or := range ors {
  1796. stmt += "("
  1797. ands := strings.Split(strings.TrimSpace(or), "&&")
  1798. for _, and := range ands {
  1799. inArg := false
  1800. pre := ""
  1801. lft := ""
  1802. preOperator := ""
  1803. args := strings.Split(strings.TrimSpace(and), " ")
  1804. for _, a := range args {
  1805. if inArg {
  1806. // check to see if is a value range 2..9
  1807. multiRange := strings.Count(a, "..") > 1
  1808. cargs := strings.Split(strings.TrimSpace(a), ",")
  1809. hasBracket := len(cargs) > 1
  1810. bracketAdded := false
  1811. lastWasRange := false
  1812. for _, carg := range cargs {
  1813. if rng := strings.Split(carg, ".."); len(rng) > 1 {
  1814. if multiRange {
  1815. pre += " ("
  1816. } else {
  1817. pre += " "
  1818. }
  1819. switch preOperator {
  1820. case "==":
  1821. pre += lft + " >= " + rng[0] + " && " + lft + "<=" + rng[1]
  1822. case "!=":
  1823. pre += "(" + lft + " < " + rng[0] + " || " + lft + " > " + rng[1] + ")"
  1824. }
  1825. if multiRange {
  1826. pre += ") || "
  1827. } else {
  1828. pre += " || "
  1829. }
  1830. lastWasRange = true
  1831. continue
  1832. }
  1833. if lastWasRange {
  1834. pre = strings.TrimRight(pre, " || ") + " && "
  1835. }
  1836. lastWasRange = false
  1837. if hasBracket && !bracketAdded {
  1838. pre += "("
  1839. bracketAdded = true
  1840. }
  1841. // single comma separated values
  1842. switch preOperator {
  1843. case "==":
  1844. pre += " " + lft + preOperator + carg + " || "
  1845. case "!=":
  1846. pre += " " + lft + preOperator + carg + " && "
  1847. }
  1848. }
  1849. pre = strings.TrimRight(pre, " || ")
  1850. pre = strings.TrimRight(pre, " && ")
  1851. pre = strings.TrimRight(pre, " || ")
  1852. if hasBracket && bracketAdded {
  1853. pre += ")"
  1854. }
  1855. continue
  1856. }
  1857. if strings.Contains(a, "=") || a == ">" || a == "<" {
  1858. inArg = true
  1859. preOperator = a
  1860. continue
  1861. }
  1862. lft += a
  1863. }
  1864. stmt += pre + " && "
  1865. }
  1866. stmt = strings.TrimRight(stmt, " && ") + ") || "
  1867. }
  1868. stmt = strings.TrimRight(stmt, " || ")
  1869. results += stmt
  1870. results += " {\n"
  1871. // return plural rule here
  1872. results += "return locales." + ps1 + "\n"
  1873. results += "}"
  1874. }
  1875. pre := "\n"
  1876. // always needed
  1877. vals[prVarFuncs["n"]] = struct{}{}
  1878. sorted := make([]sortRank, 0, len(vals))
  1879. for k := range vals {
  1880. switch k[:1] {
  1881. case "n":
  1882. sorted = append(sorted, sortRank{
  1883. Value: prVarFuncs["n"],
  1884. Rank: 1,
  1885. })
  1886. case "i":
  1887. sorted = append(sorted, sortRank{
  1888. Value: prVarFuncs["i"],
  1889. Rank: 2,
  1890. })
  1891. case "w":
  1892. sorted = append(sorted, sortRank{
  1893. Value: prVarFuncs["w"],
  1894. Rank: 3,
  1895. })
  1896. case "f":
  1897. sorted = append(sorted, sortRank{
  1898. Value: prVarFuncs["f"],
  1899. Rank: 4,
  1900. })
  1901. case "t":
  1902. sorted = append(sorted, sortRank{
  1903. Value: prVarFuncs["t"],
  1904. Rank: 5,
  1905. })
  1906. }
  1907. }
  1908. sort.Sort(ByRank(sorted))
  1909. for _, k := range sorted {
  1910. pre += k.Value
  1911. }
  1912. if len(results) == 0 {
  1913. results = "return locales.PluralRuleUnknown"
  1914. } else {
  1915. if !strings.HasPrefix(results, "return") {
  1916. results = manyToSingleVars(results)
  1917. // pre += "\n"
  1918. results = pre + results
  1919. }
  1920. }
  1921. if len(pluralArr) == 0 {
  1922. plurals = "nil"
  1923. } else {
  1924. plurals = fmt.Sprintf("%#v", pluralArr)
  1925. }
  1926. return
  1927. }
  1928. func manyToSingleVars(input string) (results string) {
  1929. matches := nModRegex.FindAllString(input, -1)
  1930. mp := make(map[string][]string) // map of formula to variable
  1931. var found bool
  1932. var split []string
  1933. var variable string
  1934. for _, formula := range matches {
  1935. if _, found = mp[formula]; found {
  1936. continue
  1937. }
  1938. split = strings.SplitN(formula, "%", 2)
  1939. mp[formula] = []string{split[1], "math.Mod(" + split[0] + ", " + split[1] + ")"}
  1940. }
  1941. for k, v := range mp {
  1942. variable = "nMod" + v[0]
  1943. results += variable + " := " + v[1] + "\n"
  1944. input = strings.Replace(input, k, variable, -1)
  1945. }
  1946. matches = iModRegex.FindAllString(input, -1)
  1947. mp = make(map[string][]string) // map of formula to variable
  1948. for _, formula := range matches {
  1949. if _, found = mp[formula]; found {
  1950. continue
  1951. }
  1952. split = strings.SplitN(formula, "%", 2)
  1953. mp[formula] = []string{split[1], formula}
  1954. }
  1955. for k, v := range mp {
  1956. variable = "iMod" + v[0]
  1957. results += variable + " := " + v[1] + "\n"
  1958. input = strings.Replace(input, k, variable, -1)
  1959. }
  1960. matches = wModRegex.FindAllString(input, -1)
  1961. mp = make(map[string][]string) // map of formula to variable
  1962. for _, formula := range matches {
  1963. if _, found = mp[formula]; found {
  1964. continue
  1965. }
  1966. split = strings.SplitN(formula, "%", 2)
  1967. mp[formula] = []string{split[1], formula}
  1968. }
  1969. for k, v := range mp {
  1970. variable = "wMod" + v[0]
  1971. results += variable + " := " + v[1] + "\n"
  1972. input = strings.Replace(input, k, variable, -1)
  1973. }
  1974. matches = fModRegex.FindAllString(input, -1)
  1975. mp = make(map[string][]string) // map of formula to variable
  1976. for _, formula := range matches {
  1977. if _, found = mp[formula]; found {
  1978. continue
  1979. }
  1980. split = strings.SplitN(formula, "%", 2)
  1981. mp[formula] = []string{split[1], formula}
  1982. }
  1983. for k, v := range mp {
  1984. variable = "fMod" + v[0]
  1985. results += variable + " := " + v[1] + "\n"
  1986. input = strings.Replace(input, k, variable, -1)
  1987. }
  1988. matches = tModRegex.FindAllString(input, -1)
  1989. mp = make(map[string][]string) // map of formula to variable
  1990. for _, formula := range matches {
  1991. if _, found = mp[formula]; found {
  1992. continue
  1993. }
  1994. split = strings.SplitN(formula, "%", 2)
  1995. mp[formula] = []string{split[1], formula}
  1996. }
  1997. for k, v := range mp {
  1998. variable = "tMod" + v[0]
  1999. results += variable + " := " + v[1] + "\n"
  2000. input = strings.Replace(input, k, variable, -1)
  2001. }
  2002. results = results + "\n" + input
  2003. return
  2004. }
  2005. // pluralStringToInt returns the enum value of 'plural' provided
  2006. func pluralStringToInt(plural string) locales.PluralRule {
  2007. switch plural {
  2008. case "zero":
  2009. return locales.PluralRuleZero
  2010. case "one":
  2011. return locales.PluralRuleOne
  2012. case "two":
  2013. return locales.PluralRuleTwo
  2014. case "few":
  2015. return locales.PluralRuleFew
  2016. case "many":
  2017. return locales.PluralRuleMany
  2018. case "other":
  2019. return locales.PluralRuleOther
  2020. default:
  2021. return locales.PluralRuleUnknown
  2022. }
  2023. }
  2024. func pluralStringToString(pr string) string {
  2025. pr = strings.TrimSpace(pr)
  2026. switch pr {
  2027. case "zero":
  2028. return "PluralRuleZero"
  2029. case "one":
  2030. return "PluralRuleOne"
  2031. case "two":
  2032. return "PluralRuleTwo"
  2033. case "few":
  2034. return "PluralRuleFew"
  2035. case "many":
  2036. return "PluralRuleMany"
  2037. case "other":
  2038. return "PluralRuleOther"
  2039. default:
  2040. return "PluralRuleUnknown"
  2041. }
  2042. }