generate_resources.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400
  1. package main
  2. import (
  3. "bytes"
  4. "fmt"
  5. "go/format"
  6. "os"
  7. "strings"
  8. "sync"
  9. "text/template"
  10. "time"
  11. "golang.org/x/text/unicode/cldr"
  12. i18n "github.com/go-playground/universal-translator"
  13. )
  14. // numbers:
  15. // symbols:
  16. // decimal: .
  17. // group: ','
  18. // negative: '-'
  19. // percent: '%'
  20. // permille: "\u2030"
  21. // formats:
  22. // decimal: '#,##0.###'
  23. // currency: "\xA4#,##0.00;(\xA4#,##0.00)"
  24. // percent: '#,##0%'
  25. // currencies:
  26. // JPY:
  27. // symbol: "\xA5"
  28. // USD:
  29. // symbol: $
  30. func main() {
  31. var decoder cldr.Decoder
  32. cldr, err := decoder.DecodePath("data/core")
  33. if err != nil {
  34. panic(err)
  35. }
  36. locs := map[string]string{}
  37. numbers := map[string]i18n.Number{}
  38. calendars := map[string]i18n.Calendar{}
  39. locales := cldr.Locales()
  40. for _, loc := range locales {
  41. ldml := cldr.RawLDML(loc)
  42. if ldml.Numbers == nil {
  43. continue
  44. }
  45. var number i18n.Number
  46. number.Currencies = make(i18n.CurrencyFormatValue)
  47. if len(ldml.Numbers.Symbols) > 0 {
  48. symbol := ldml.Numbers.Symbols[0]
  49. if len(symbol.Decimal) > 0 {
  50. number.Symbols.Decimal = symbol.Decimal[0].Data()
  51. }
  52. if len(symbol.Group) > 0 {
  53. number.Symbols.Group = symbol.Group[0].Data()
  54. }
  55. if len(symbol.MinusSign) > 0 {
  56. number.Symbols.Negative = symbol.MinusSign[0].Data()
  57. }
  58. if len(symbol.PercentSign) > 0 {
  59. number.Symbols.Percent = symbol.PercentSign[0].Data()
  60. }
  61. if len(symbol.PerMille) > 0 {
  62. number.Symbols.PerMille = symbol.PerMille[0].Data()
  63. }
  64. }
  65. if len(ldml.Numbers.DecimalFormats) > 0 && len(ldml.Numbers.DecimalFormats[0].DecimalFormatLength) > 0 {
  66. number.Formats.Decimal = ldml.Numbers.DecimalFormats[0].DecimalFormatLength[0].DecimalFormat[0].Pattern[0].Data()
  67. }
  68. if len(ldml.Numbers.CurrencyFormats) > 0 && len(ldml.Numbers.CurrencyFormats[0].CurrencyFormatLength) > 0 {
  69. number.Formats.Currency = ldml.Numbers.CurrencyFormats[0].CurrencyFormatLength[0].CurrencyFormat[0].Pattern[0].Data()
  70. }
  71. if len(ldml.Numbers.PercentFormats) > 0 && len(ldml.Numbers.PercentFormats[0].PercentFormatLength) > 0 {
  72. number.Formats.Percent = ldml.Numbers.PercentFormats[0].PercentFormatLength[0].PercentFormat[0].Pattern[0].Data()
  73. }
  74. if ldml.Numbers.Currencies != nil {
  75. for _, currency := range ldml.Numbers.Currencies.Currency {
  76. var c i18n.Currency
  77. c.Currency = currency.Type
  78. if len(currency.DisplayName) > 0 {
  79. c.DisplayName = currency.DisplayName[0].Data()
  80. }
  81. if len(currency.Symbol) > 0 {
  82. c.Symbol = currency.Symbol[0].Data()
  83. }
  84. number.Currencies[c.Currency] = c
  85. }
  86. }
  87. numbers[loc] = number
  88. locs[loc] = strings.ToLower(strings.Replace(loc, "_", "", -1))
  89. if ldml.Dates != nil && ldml.Dates.Calendars != nil {
  90. var calendar i18n.Calendar
  91. ldmlCar := ldml.Dates.Calendars.Calendar[0]
  92. for _, cal := range ldml.Dates.Calendars.Calendar {
  93. if cal.Type == "gregorian" {
  94. ldmlCar = cal
  95. }
  96. }
  97. if ldmlCar.DateFormats != nil {
  98. for _, datefmt := range ldmlCar.DateFormats.DateFormatLength {
  99. switch datefmt.Type {
  100. case "full":
  101. calendar.Formats.Date.Full = datefmt.DateFormat[0].Pattern[0].Data()
  102. case "long":
  103. calendar.Formats.Date.Long = datefmt.DateFormat[0].Pattern[0].Data()
  104. case "medium":
  105. calendar.Formats.Date.Medium = datefmt.DateFormat[0].Pattern[0].Data()
  106. case "short":
  107. calendar.Formats.Date.Short = datefmt.DateFormat[0].Pattern[0].Data()
  108. }
  109. }
  110. }
  111. if ldmlCar.TimeFormats != nil {
  112. for _, datefmt := range ldmlCar.TimeFormats.TimeFormatLength {
  113. switch datefmt.Type {
  114. case "full":
  115. calendar.Formats.Time.Full = datefmt.TimeFormat[0].Pattern[0].Data()
  116. case "long":
  117. calendar.Formats.Time.Long = datefmt.TimeFormat[0].Pattern[0].Data()
  118. case "medium":
  119. calendar.Formats.Time.Medium = datefmt.TimeFormat[0].Pattern[0].Data()
  120. case "short":
  121. calendar.Formats.Time.Short = datefmt.TimeFormat[0].Pattern[0].Data()
  122. }
  123. }
  124. }
  125. if ldmlCar.DateTimeFormats != nil {
  126. for _, datefmt := range ldmlCar.DateTimeFormats.DateTimeFormatLength {
  127. switch datefmt.Type {
  128. case "full":
  129. calendar.Formats.DateTime.Full = datefmt.DateTimeFormat[0].Pattern[0].Data()
  130. case "long":
  131. calendar.Formats.DateTime.Long = datefmt.DateTimeFormat[0].Pattern[0].Data()
  132. case "medium":
  133. calendar.Formats.DateTime.Medium = datefmt.DateTimeFormat[0].Pattern[0].Data()
  134. case "short":
  135. calendar.Formats.DateTime.Short = datefmt.DateTimeFormat[0].Pattern[0].Data()
  136. }
  137. }
  138. }
  139. if ldmlCar.Months != nil {
  140. for _, monthctx := range ldmlCar.Months.MonthContext {
  141. for _, months := range monthctx.MonthWidth {
  142. i18nMonth := make(i18n.CalendarMonthFormatNameValue)
  143. for _, m := range months.Month {
  144. switch m.Type {
  145. case "1":
  146. i18nMonth[time.January] = m.Data()
  147. case "2":
  148. i18nMonth[time.February] = m.Data()
  149. case "3":
  150. i18nMonth[time.March] = m.Data()
  151. case "4":
  152. i18nMonth[time.April] = m.Data()
  153. case "5":
  154. i18nMonth[time.May] = m.Data()
  155. case "6":
  156. i18nMonth[time.June] = m.Data()
  157. case "7":
  158. i18nMonth[time.July] = m.Data()
  159. case "8":
  160. i18nMonth[time.August] = m.Data()
  161. case "9":
  162. i18nMonth[time.September] = m.Data()
  163. case "10":
  164. i18nMonth[time.October] = m.Data()
  165. case "11":
  166. i18nMonth[time.November] = m.Data()
  167. case "12":
  168. i18nMonth[time.December] = m.Data()
  169. }
  170. }
  171. switch months.Type {
  172. case "abbreviated":
  173. calendar.FormatNames.Months.Abbreviated = i18nMonth
  174. case "narrow":
  175. calendar.FormatNames.Months.Narrow = i18nMonth
  176. case "short":
  177. calendar.FormatNames.Months.Short = i18nMonth
  178. case "wide":
  179. calendar.FormatNames.Months.Wide = i18nMonth
  180. }
  181. }
  182. }
  183. }
  184. if ldmlCar.Days != nil {
  185. for _, dayctx := range ldmlCar.Days.DayContext {
  186. for _, days := range dayctx.DayWidth {
  187. i18nDay := make(i18n.CalendarDayFormatNameValue)
  188. for _, d := range days.Day {
  189. switch d.Type {
  190. case "sun":
  191. i18nDay[time.Sunday] = d.Data()
  192. case "mon":
  193. i18nDay[time.Monday] = d.Data()
  194. case "tue":
  195. i18nDay[time.Tuesday] = d.Data()
  196. case "wed":
  197. i18nDay[time.Wednesday] = d.Data()
  198. case "thu":
  199. i18nDay[time.Thursday] = d.Data()
  200. case "fri":
  201. i18nDay[time.Friday] = d.Data()
  202. case "sat":
  203. i18nDay[time.Saturday] = d.Data()
  204. }
  205. }
  206. switch days.Type {
  207. case "abbreviated":
  208. calendar.FormatNames.Days.Abbreviated = i18nDay
  209. case "narrow":
  210. calendar.FormatNames.Days.Narrow = i18nDay
  211. case "short":
  212. calendar.FormatNames.Days.Short = i18nDay
  213. case "wide":
  214. calendar.FormatNames.Days.Wide = i18nDay
  215. }
  216. }
  217. }
  218. }
  219. if ldmlCar.DayPeriods != nil {
  220. for _, ctx := range ldmlCar.DayPeriods.DayPeriodContext {
  221. for _, width := range ctx.DayPeriodWidth {
  222. // var i18nPeriod i18n.CalendarPeriodFormatNameValue
  223. i18nPeriod := make(i18n.CalendarPeriodFormatNameValue)
  224. for _, d := range width.DayPeriod {
  225. if _, ok := i18nPeriod[d.Type]; !ok {
  226. i18nPeriod[d.Type] = d.Data()
  227. }
  228. }
  229. switch width.Type {
  230. case "abbreviated":
  231. calendar.FormatNames.Periods.Abbreviated = i18nPeriod
  232. case "narrow":
  233. calendar.FormatNames.Periods.Narrow = i18nPeriod
  234. case "short":
  235. calendar.FormatNames.Periods.Short = i18nPeriod
  236. case "wide":
  237. calendar.FormatNames.Periods.Wide = i18nPeriod
  238. }
  239. }
  240. }
  241. // var empty i18n.CalendarPeriodFormatNameValue
  242. // if calendar.FormatNames.Periods.Abbreviated == empty {
  243. // calendar.FormatNames.Periods.Abbreviated = calendar.FormatNames.Periods.Wide
  244. // }
  245. }
  246. calendars[loc] = calendar
  247. }
  248. }
  249. var wg sync.WaitGroup
  250. wg.Add(len(numbers))
  251. for locale, number := range numbers {
  252. go func(locale string, number i18n.Number) {
  253. localeNoUnderscore := strings.ToLower(strings.Replace(locale, "_", "", -1))
  254. defer func() { wg.Done() }()
  255. path := "../../zzz_generated_" + locale
  256. mainFile, err := os.Create(path + ".go")
  257. if err != nil {
  258. panic(err)
  259. }
  260. defer mainFile.Close()
  261. calendar := calendars[locale]
  262. mainCodes, err := format.Source([]byte(fmt.Sprintf(`package ut
  263. // new returns a new instance of the locale
  264. func new%s() *Locale {
  265. return &Locale {
  266. Locale: %q,
  267. Number: Number {
  268. Symbols: new%sSymbols(),
  269. Formats: new%sFormats(),
  270. Currencies: new%sCurrencies(),
  271. },
  272. Calendar: new%sCalendar(),
  273. PluralRule: %spluralRule,
  274. }
  275. }
  276. func new%sSymbols() Symbols {
  277. return %# v
  278. }
  279. func new%sFormats() NumberFormats {
  280. return %# v
  281. }
  282. func new%sCurrencies() CurrencyFormatValue {
  283. return %# v
  284. }
  285. func new%sCalendar() Calendar {
  286. return %#v
  287. }
  288. `, localeNoUnderscore, locale, localeNoUnderscore, localeNoUnderscore, localeNoUnderscore, localeNoUnderscore, localeNoUnderscore,
  289. localeNoUnderscore, number.Symbols, localeNoUnderscore, number.Formats, localeNoUnderscore,
  290. number.Currencies, localeNoUnderscore, calendar)))
  291. if err != nil {
  292. panic(err)
  293. }
  294. mainCodes = []byte(strings.Replace(string(mainCodes), "ut.", "", -1))
  295. fmt.Fprintf(mainFile, "%s", mainCodes)
  296. pluralFile, err := os.Create(path + "_plural.go")
  297. if err != nil {
  298. panic(err)
  299. }
  300. defer pluralFile.Close()
  301. pluralCodes, err := format.Source([]byte(fmt.Sprintf(`package ut
  302. var %spluralRule = "1"
  303. `, localeNoUnderscore)))
  304. if err != nil {
  305. panic(err)
  306. }
  307. fmt.Fprintf(pluralFile, "%s", pluralCodes)
  308. }(locale, number)
  309. }
  310. wg.Wait()
  311. // TODO: make switch with all of the locales + function to return new!
  312. localesFile, err := os.Create("../../zzz_generated_locales.go")
  313. if err != nil {
  314. panic(err)
  315. }
  316. defer localesFile.Close()
  317. tmpl, err := template.New("").Parse(`package ut
  318. import "errors"
  319. // GetLocale returns the Locale instance associated with
  320. // the provided locale string, or returns an error when not found.
  321. func GetLocale(locale string) (*Locale, error) {
  322. switch locale {
  323. {{range $locale, $val := .}}case "{{$locale}}":
  324. return new{{$val}}(), nil
  325. {{end}}
  326. default:
  327. return nil, errors.New("Unknown locale '" + locale + "'")
  328. }
  329. }
  330. `)
  331. if err != nil {
  332. panic(err)
  333. }
  334. var buf bytes.Buffer
  335. if err := tmpl.Execute(&buf, locs); err != nil {
  336. panic(err)
  337. }
  338. allCodes, err := format.Source(buf.Bytes())
  339. if err != nil {
  340. panic(err)
  341. }
  342. _, err = localesFile.Write(allCodes)
  343. if err != nil {
  344. panic(err)
  345. }
  346. }