generate_resources.go 58 KB

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