generate_resources.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422
  1. package main
  2. import (
  3. "bytes"
  4. "fmt"
  5. "go/format"
  6. "os"
  7. "strings"
  8. "sync"
  9. "text/template"
  10. "golang.org/x/text/unicode/cldr"
  11. i18n "github.com/go-playground/universal-translator"
  12. "github.com/kr/pretty"
  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. if len(ldml.Numbers.Symbols) > 0 {
  47. symbol := ldml.Numbers.Symbols[0]
  48. if len(symbol.Decimal) > 0 {
  49. number.Symbols.Decimal = symbol.Decimal[0].Data()
  50. }
  51. if len(symbol.Group) > 0 {
  52. number.Symbols.Group = symbol.Group[0].Data()
  53. }
  54. if len(symbol.MinusSign) > 0 {
  55. number.Symbols.Negative = symbol.MinusSign[0].Data()
  56. }
  57. if len(symbol.PercentSign) > 0 {
  58. number.Symbols.Percent = symbol.PercentSign[0].Data()
  59. }
  60. if len(symbol.PerMille) > 0 {
  61. number.Symbols.PerMille = symbol.PerMille[0].Data()
  62. }
  63. }
  64. if len(ldml.Numbers.DecimalFormats) > 0 && len(ldml.Numbers.DecimalFormats[0].DecimalFormatLength) > 0 {
  65. number.Formats.Decimal = ldml.Numbers.DecimalFormats[0].DecimalFormatLength[0].DecimalFormat[0].Pattern[0].Data()
  66. }
  67. if len(ldml.Numbers.CurrencyFormats) > 0 && len(ldml.Numbers.CurrencyFormats[0].CurrencyFormatLength) > 0 {
  68. number.Formats.Currency = ldml.Numbers.CurrencyFormats[0].CurrencyFormatLength[0].CurrencyFormat[0].Pattern[0].Data()
  69. }
  70. if len(ldml.Numbers.PercentFormats) > 0 && len(ldml.Numbers.PercentFormats[0].PercentFormatLength) > 0 {
  71. number.Formats.Percent = ldml.Numbers.PercentFormats[0].PercentFormatLength[0].PercentFormat[0].Pattern[0].Data()
  72. }
  73. if ldml.Numbers.Currencies != nil {
  74. for _, currency := range ldml.Numbers.Currencies.Currency {
  75. var c i18n.Currency
  76. c.Currency = currency.Type
  77. if len(currency.DisplayName) > 0 {
  78. c.DisplayName = currency.DisplayName[0].Data()
  79. }
  80. if len(currency.Symbol) > 0 {
  81. c.Symbol = currency.Symbol[0].Data()
  82. }
  83. number.Currencies = append(number.Currencies, c)
  84. }
  85. }
  86. numbers[loc] = number
  87. locs[loc] = strings.Replace(loc, "_", "", -1)
  88. if ldml.Dates != nil && ldml.Dates.Calendars != nil {
  89. var calendar i18n.Calendar
  90. ldmlCar := ldml.Dates.Calendars.Calendar[0]
  91. for _, cal := range ldml.Dates.Calendars.Calendar {
  92. if cal.Type == "gregorian" {
  93. ldmlCar = cal
  94. }
  95. }
  96. if ldmlCar.DateFormats != nil {
  97. for _, datefmt := range ldmlCar.DateFormats.DateFormatLength {
  98. switch datefmt.Type {
  99. case "full":
  100. calendar.Formats.Date.Full = datefmt.DateFormat[0].Pattern[0].Data()
  101. case "long":
  102. calendar.Formats.Date.Long = datefmt.DateFormat[0].Pattern[0].Data()
  103. case "medium":
  104. calendar.Formats.Date.Medium = datefmt.DateFormat[0].Pattern[0].Data()
  105. case "short":
  106. calendar.Formats.Date.Short = datefmt.DateFormat[0].Pattern[0].Data()
  107. }
  108. }
  109. }
  110. if ldmlCar.TimeFormats != nil {
  111. for _, datefmt := range ldmlCar.TimeFormats.TimeFormatLength {
  112. switch datefmt.Type {
  113. case "full":
  114. calendar.Formats.Time.Full = datefmt.TimeFormat[0].Pattern[0].Data()
  115. case "long":
  116. calendar.Formats.Time.Long = datefmt.TimeFormat[0].Pattern[0].Data()
  117. case "medium":
  118. calendar.Formats.Time.Medium = datefmt.TimeFormat[0].Pattern[0].Data()
  119. case "short":
  120. calendar.Formats.Time.Short = datefmt.TimeFormat[0].Pattern[0].Data()
  121. }
  122. }
  123. }
  124. if ldmlCar.DateTimeFormats != nil {
  125. for _, datefmt := range ldmlCar.DateTimeFormats.DateTimeFormatLength {
  126. switch datefmt.Type {
  127. case "full":
  128. calendar.Formats.DateTime.Full = datefmt.DateTimeFormat[0].Pattern[0].Data()
  129. case "long":
  130. calendar.Formats.DateTime.Long = datefmt.DateTimeFormat[0].Pattern[0].Data()
  131. case "medium":
  132. calendar.Formats.DateTime.Medium = datefmt.DateTimeFormat[0].Pattern[0].Data()
  133. case "short":
  134. calendar.Formats.DateTime.Short = datefmt.DateTimeFormat[0].Pattern[0].Data()
  135. }
  136. }
  137. }
  138. if ldmlCar.Months != nil {
  139. for _, monthctx := range ldmlCar.Months.MonthContext {
  140. for _, months := range monthctx.MonthWidth {
  141. var i18nMonth i18n.CalendarMonthFormatNameValue
  142. for _, m := range months.Month {
  143. switch m.Type {
  144. case "1":
  145. i18nMonth.Jan = m.Data()
  146. case "2":
  147. i18nMonth.Feb = m.Data()
  148. case "3":
  149. i18nMonth.Mar = m.Data()
  150. case "4":
  151. i18nMonth.Apr = m.Data()
  152. case "5":
  153. i18nMonth.May = m.Data()
  154. case "6":
  155. i18nMonth.Jun = m.Data()
  156. case "7":
  157. i18nMonth.Jul = m.Data()
  158. case "8":
  159. i18nMonth.Aug = m.Data()
  160. case "9":
  161. i18nMonth.Sep = m.Data()
  162. case "10":
  163. i18nMonth.Oct = m.Data()
  164. case "11":
  165. i18nMonth.Nov = m.Data()
  166. case "12":
  167. i18nMonth.Dec = m.Data()
  168. }
  169. }
  170. switch months.Type {
  171. case "abbreviated":
  172. calendar.FormatNames.Months.Abbreviated = i18nMonth
  173. case "narrow":
  174. calendar.FormatNames.Months.Narrow = i18nMonth
  175. case "short":
  176. calendar.FormatNames.Months.Short = i18nMonth
  177. case "wide":
  178. calendar.FormatNames.Months.Wide = i18nMonth
  179. }
  180. }
  181. }
  182. }
  183. if ldmlCar.Days != nil {
  184. for _, dayctx := range ldmlCar.Days.DayContext {
  185. for _, days := range dayctx.DayWidth {
  186. var i18nDay i18n.CalendarDayFormatNameValue
  187. for _, d := range days.Day {
  188. switch d.Type {
  189. case "sun":
  190. i18nDay.Sun = d.Data()
  191. case "mon":
  192. i18nDay.Mon = d.Data()
  193. case "tue":
  194. i18nDay.Tue = d.Data()
  195. case "wed":
  196. i18nDay.Wed = d.Data()
  197. case "thu":
  198. i18nDay.Thu = d.Data()
  199. case "fri":
  200. i18nDay.Fri = d.Data()
  201. case "sat":
  202. i18nDay.Sat = d.Data()
  203. }
  204. }
  205. switch days.Type {
  206. case "abbreviated":
  207. calendar.FormatNames.Days.Abbreviated = i18nDay
  208. case "narrow":
  209. calendar.FormatNames.Days.Narrow = i18nDay
  210. case "short":
  211. calendar.FormatNames.Days.Short = i18nDay
  212. case "wide":
  213. calendar.FormatNames.Days.Wide = i18nDay
  214. }
  215. }
  216. }
  217. }
  218. if ldmlCar.DayPeriods != nil {
  219. for _, ctx := range ldmlCar.DayPeriods.DayPeriodContext {
  220. for _, width := range ctx.DayPeriodWidth {
  221. var i18nPeriod i18n.CalendarPeriodFormatNameValue
  222. for _, d := range width.DayPeriod {
  223. switch d.Type {
  224. case "am":
  225. if i18nPeriod.AM == "" {
  226. i18nPeriod.AM = d.Data()
  227. }
  228. case "pm":
  229. if i18nPeriod.PM == "" {
  230. i18nPeriod.PM = d.Data()
  231. }
  232. }
  233. }
  234. switch width.Type {
  235. case "abbreviated":
  236. calendar.FormatNames.Periods.Abbreviated = i18nPeriod
  237. case "narrow":
  238. calendar.FormatNames.Periods.Narrow = i18nPeriod
  239. case "short":
  240. calendar.FormatNames.Periods.Short = i18nPeriod
  241. case "wide":
  242. calendar.FormatNames.Periods.Wide = i18nPeriod
  243. }
  244. }
  245. }
  246. // var empty i18n.CalendarPeriodFormatNameValue
  247. // if calendar.FormatNames.Periods.Abbreviated == empty {
  248. // calendar.FormatNames.Periods.Abbreviated = calendar.FormatNames.Periods.Wide
  249. // }
  250. }
  251. calendars[loc] = calendar
  252. }
  253. }
  254. var wg sync.WaitGroup
  255. wg.Add(len(numbers))
  256. for locale, number := range numbers {
  257. go func(locale string, number i18n.Number) {
  258. localeNoUnderscore := strings.Replace(locale, "_", "", -1)
  259. defer func() { wg.Done() }()
  260. path := "../../zzz_generated_" + locale + "_"
  261. // if _, err := os.Stat(path); err != nil {
  262. // if err = os.MkdirAll(path, 0777); err != nil {
  263. // panic(err)
  264. // }
  265. // }
  266. numberFile, err := os.Create(path + "number.go")
  267. if err != nil {
  268. panic(err)
  269. }
  270. defer func() { numberFile.Close() }()
  271. mainFile, err := os.Create(path + "main.go")
  272. if err != nil {
  273. panic(err)
  274. }
  275. defer func() { mainFile.Close() }()
  276. currencyFile, err := os.Create(path + "currency.go")
  277. if err != nil {
  278. panic(err)
  279. }
  280. defer func() { currencyFile.Close() }()
  281. mainCodes, err := format.Source([]byte(fmt.Sprintf(`package ut
  282. // new returns a new instance of the locale
  283. func new%s() *Locale {
  284. return &Locale {
  285. Locale: %q,
  286. Number: Number {
  287. Symbols: new%sSymbols(),
  288. Formats: new%sFormats(),
  289. Currencies: new%sCurrencies(),
  290. },
  291. Calendar: new%sCalendar(),
  292. PluralRule: %spluralRule,
  293. }
  294. }
  295. `, localeNoUnderscore, locale, localeNoUnderscore, localeNoUnderscore, localeNoUnderscore, localeNoUnderscore, localeNoUnderscore)))
  296. if err != nil {
  297. panic(err)
  298. }
  299. fmt.Fprintf(mainFile, "%s", mainCodes)
  300. numberCodes, err := format.Source([]byte(fmt.Sprintf(`package ut
  301. func new%sSymbols() Symbols {
  302. return %#v
  303. }
  304. func new%sFormats() NumberFormats {
  305. return %#v
  306. }
  307. `, localeNoUnderscore, pretty.Formatter(number.Symbols), localeNoUnderscore, pretty.Formatter(number.Formats))))
  308. if err != nil {
  309. panic(err)
  310. }
  311. numberCodes = []byte(strings.Replace(string(numberCodes), "ut.", "", -1))
  312. fmt.Fprintf(numberFile, "%s", numberCodes)
  313. currencyCodes, err := format.Source([]byte(fmt.Sprintf(`package ut
  314. func new%sCurrencies() []Currency {
  315. return %#v
  316. }
  317. `, localeNoUnderscore, pretty.Formatter(number.Currencies))))
  318. if err != nil {
  319. panic(err)
  320. }
  321. currencyCodes = []byte(strings.Replace(string(currencyCodes), "ut.", "", -1))
  322. fmt.Fprintf(currencyFile, "%s", currencyCodes)
  323. calendar := calendars[locale]
  324. calendarFile, err := os.Create(path + "calendar.go")
  325. if err != nil {
  326. panic(err)
  327. }
  328. defer func() { calendarFile.Close() }()
  329. calendarCodes, err := format.Source([]byte(fmt.Sprintf(`package ut
  330. func new%sCalendar() Calendar {
  331. return %#v
  332. }
  333. `, localeNoUnderscore, pretty.Formatter(calendar))))
  334. if err != nil {
  335. panic(err)
  336. }
  337. calendarCodes = []byte(strings.Replace(string(calendarCodes), "ut.", "", -1))
  338. fmt.Fprintf(calendarFile, "%s", calendarCodes)
  339. pluralFile, err := os.Create(path + "plural.go")
  340. if err != nil {
  341. panic(err)
  342. }
  343. defer func() { pluralFile.Close() }()
  344. pluralCodes, err := format.Source([]byte(fmt.Sprintf(`package ut
  345. var %spluralRule = "1"
  346. `, localeNoUnderscore)))
  347. if err != nil {
  348. panic(err)
  349. }
  350. fmt.Fprintf(pluralFile, "%s", pluralCodes)
  351. }(locale, number)
  352. }
  353. wg.Wait()
  354. // TODO: make switch with all of the locales + function to return new!
  355. localesFile, err := os.Create("../../zzz_generated_locales.go")
  356. if err != nil {
  357. panic(err)
  358. }
  359. defer localesFile.Close()
  360. tmpl, err := template.New("").Parse(`package ut
  361. import "errors"
  362. // GetLocale returns the Locale instance associated with
  363. // the provided locale string, or returns an error when not found.
  364. func GetLocale(locale string) (*Locale, error) {
  365. switch locale {
  366. {{range $locale, $val := .}}case "{{$locale}}":
  367. return new{{$val}}(), nil
  368. {{end}}
  369. default:
  370. return nil, errors.New("Unknown locale '" + locale + "'")
  371. }
  372. }
  373. `)
  374. if err != nil {
  375. panic(err)
  376. }
  377. var buf bytes.Buffer
  378. if err := tmpl.Execute(&buf, locs); err != nil {
  379. panic(err)
  380. }
  381. allCodes, err := format.Source(buf.Bytes())
  382. if err != nil {
  383. panic(err)
  384. }
  385. _, err = localesFile.Write(allCodes)
  386. if err != nil {
  387. panic(err)
  388. }
  389. }