translator.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300
  1. package ut
  2. import (
  3. "errors"
  4. "fmt"
  5. "log"
  6. "time"
  7. )
  8. type translation struct {
  9. text string
  10. }
  11. // map[key]map[plural type other, many, few, single]*translation
  12. type translations map[PluralRule]map[string]*translation
  13. type groups map[string][]*translation
  14. // Translator holds the locale translation instance
  15. type Translator struct {
  16. locale *Locale
  17. ruler PluralRuler
  18. translations translations
  19. groups groups
  20. }
  21. func newTranslator(loc *Locale) *Translator {
  22. return &Translator{
  23. locale: loc,
  24. ruler: pluralRules[loc.PluralRule],
  25. translations: make(translations),
  26. groups: make(groups),
  27. }
  28. }
  29. // Add registers a new translation to the Translator using the
  30. // key for a unique translation and group allows for retrieving
  31. // translations for dev purposes later i.e. load all translations
  32. // for the "homepage" group
  33. // plural is optional, can be blank, but allows adding a plural translation
  34. // at the same time as a singular
  35. func (t *Translator) Add(rule PluralRule, group string, key string, text string) {
  36. trans := &translation{
  37. text: text,
  38. }
  39. if _, ok := t.translations[rule]; !ok {
  40. t.translations[rule] = make(map[string]*translation)
  41. }
  42. if _, ok := t.translations[rule][key]; ok {
  43. panic(fmt.Sprintf("Translation with key %q already exists", key))
  44. }
  45. t.translations[rule][key] = trans
  46. if _, ok := t.groups[group]; !ok {
  47. t.groups[group] = make([]*translation, 0)
  48. }
  49. t.groups[group] = append(t.groups[group], trans)
  50. }
  51. // PrintPluralRules prints the supported rules for the
  52. // given translators locale
  53. func (t *Translator) PrintPluralRules() {
  54. rules := pluralPluralRules[t.locale.PluralRule]
  55. fmt.Println("Translator locale '" + t.locale.Locale + "' supported rules:")
  56. for _, rule := range rules {
  57. fmt.Println("- " + rule.String())
  58. }
  59. fmt.Println("")
  60. }
  61. func (t *Translator) pluralRule(count NumberValue) (rule PluralRule) {
  62. return t.ruler.FindRule(count)
  63. }
  64. // T translates the text associated with the given key with the
  65. // arguments passed in
  66. func (t *Translator) T(key string, a ...interface{}) string {
  67. return t.P(key, 1, a...)
  68. }
  69. // TSafe translates the text associated with the given key with the
  70. // arguments passed in, if the key or rule cannot be found it returns an error
  71. func (t *Translator) TSafe(key string, a ...interface{}) (string, error) {
  72. return t.PSafe(key, 1, a...)
  73. }
  74. // P translates the plural text associated with the given key with the
  75. // arguments passed in
  76. func (t *Translator) P(key string, count interface{}, a ...interface{}) string {
  77. trans, err := t.PSafe(key, count, a...)
  78. if err != nil {
  79. log.Println(err.Error())
  80. return err.Error()
  81. }
  82. return trans
  83. }
  84. // PSafe translates the plural text associated with the given key with the
  85. // arguments passed in, if the key or rule cannot be found it returns an error
  86. func (t *Translator) PSafe(key string, count interface{}, a ...interface{}) (string, error) {
  87. rule := t.ruler.FindRule(count)
  88. trans, ok := t.translations[rule][key]
  89. if !ok {
  90. return "", errors.New("***** WARNING:***** Translation Key '" + key + "' Not Found")
  91. }
  92. return fmt.Sprintf(trans.text, a...), nil
  93. }
  94. // FmtDateFullSafe formats the time with the current locales full date format
  95. func (t *Translator) FmtDateFullSafe(dt time.Time) (string, error) {
  96. return t.locale.Calendar.FmtDateFullSafe(dt)
  97. }
  98. // FmtDateLongSafe formats the time with the current locales long date format
  99. func (t *Translator) FmtDateLongSafe(dt time.Time) (string, error) {
  100. return t.locale.Calendar.FmtDateLongSafe(dt)
  101. }
  102. // FmtDateMediumSafe formats the time with the current locales medium date format
  103. func (t *Translator) FmtDateMediumSafe(dt time.Time) (string, error) {
  104. return t.locale.Calendar.FmtDateMediumSafe(dt)
  105. }
  106. // FmtDateShortSafe formats the time with the current locales short date format
  107. func (t *Translator) FmtDateShortSafe(dt time.Time) (string, error) {
  108. return t.locale.Calendar.FmtDateShortSafe(dt)
  109. }
  110. // FmtDateTimeFullSafe formats the time with the current locales full data & time format
  111. func (t *Translator) FmtDateTimeFullSafe(dt time.Time) (string, error) {
  112. return t.locale.Calendar.FmtDateTimeFullSafe(dt)
  113. }
  114. // FmtDateTimeLongSafe formats the time with the current locales long data & time format
  115. func (t *Translator) FmtDateTimeLongSafe(dt time.Time) (string, error) {
  116. return t.locale.Calendar.FmtDateTimeLongSafe(dt)
  117. }
  118. // FmtDateTimeMediumSafe formats the time with the current locales medium data & time format
  119. func (t *Translator) FmtDateTimeMediumSafe(dt time.Time) (string, error) {
  120. return t.locale.Calendar.FmtDateTimeMediumSafe(dt)
  121. }
  122. // FmtDateTimeShortSafe formats the time with the current locales short data & time format
  123. func (t *Translator) FmtDateTimeShortSafe(dt time.Time) (string, error) {
  124. return t.locale.Calendar.FmtDateTimeShortSafe(dt)
  125. }
  126. // FmtTimeFullSafe formats the time with the current locales full time format
  127. func (t *Translator) FmtTimeFullSafe(dt time.Time) (string, error) {
  128. return t.locale.Calendar.FmtTimeFullSafe(dt)
  129. }
  130. // FmtTimeLongSafe formats the time with the current locales long time format
  131. func (t *Translator) FmtTimeLongSafe(dt time.Time) (string, error) {
  132. return t.locale.Calendar.FmtTimeLongSafe(dt)
  133. }
  134. // FmtTimeMediumSafe formats the time with the current locales medium time format
  135. func (t *Translator) FmtTimeMediumSafe(dt time.Time) (string, error) {
  136. return t.locale.Calendar.FmtTimeMediumSafe(dt)
  137. }
  138. // FmtTimeShortSafe formats the time with the current locales short time format
  139. func (t *Translator) FmtTimeShortSafe(dt time.Time) (string, error) {
  140. return t.locale.Calendar.FmtTimeShortSafe(dt)
  141. }
  142. // FmtDateFull formats the time with the current locales full date format
  143. func (t *Translator) FmtDateFull(dt time.Time) string {
  144. return t.locale.Calendar.FmtDateFull(dt)
  145. }
  146. // FmtDateLong formats the time with the current locales long date format
  147. func (t *Translator) FmtDateLong(dt time.Time) string {
  148. return t.locale.Calendar.FmtDateLong(dt)
  149. }
  150. // FmtDateMedium formats the time with the current locales medium date format
  151. func (t *Translator) FmtDateMedium(dt time.Time) string {
  152. return t.locale.Calendar.FmtDateMedium(dt)
  153. }
  154. // FmtDateShort formats the time with the current locales short date format
  155. func (t *Translator) FmtDateShort(dt time.Time) string {
  156. return t.locale.Calendar.FmtDateShort(dt)
  157. }
  158. // FmtDateTimeFull formats the time with the current locales full data & time format
  159. func (t *Translator) FmtDateTimeFull(dt time.Time) string {
  160. return t.locale.Calendar.FmtDateTimeFull(dt)
  161. }
  162. // FmtDateTimeLong formats the time with the current locales long data & time format
  163. func (t *Translator) FmtDateTimeLong(dt time.Time) string {
  164. return t.locale.Calendar.FmtDateTimeLong(dt)
  165. }
  166. // FmtDateTimeMedium formats the time with the current locales medium data & time format
  167. func (t *Translator) FmtDateTimeMedium(dt time.Time) string {
  168. return t.locale.Calendar.FmtDateTimeMedium(dt)
  169. }
  170. // FmtDateTimeShort formats the time with the current locales short data & time format
  171. func (t *Translator) FmtDateTimeShort(dt time.Time) string {
  172. return t.locale.Calendar.FmtDateTimeShort(dt)
  173. }
  174. // FmtTimeFull formats the time with the current locales full time format
  175. func (t *Translator) FmtTimeFull(dt time.Time) string {
  176. return t.locale.Calendar.FmtTimeFull(dt)
  177. }
  178. // FmtTimeLong formats the time with the current locales long time format
  179. func (t *Translator) FmtTimeLong(dt time.Time) string {
  180. return t.locale.Calendar.FmtTimeLong(dt)
  181. }
  182. // FmtTimeMedium formats the time with the current locales medium time format
  183. func (t *Translator) FmtTimeMedium(dt time.Time) string {
  184. return t.locale.Calendar.FmtTimeMedium(dt)
  185. }
  186. // FmtTimeShort formats the time with the current locales short time format
  187. func (t *Translator) FmtTimeShort(dt time.Time) string {
  188. return t.locale.Calendar.FmtTimeShort(dt)
  189. }
  190. // FmtDateTimeSafe formats the time with the current locales short time format
  191. func (t *Translator) FmtDateTimeSafe(dt time.Time, pattern string) (string, error) {
  192. return t.locale.Calendar.FormatSafe(dt, pattern)
  193. }
  194. // FmtDateTime formats the time with the current locales short time format
  195. func (t *Translator) FmtDateTime(dt time.Time, pattern string) string {
  196. return t.locale.Calendar.Format(dt, pattern)
  197. }
  198. // FmtCurrencySafe takes a float number and a currency key and returns a string
  199. // with a properly formatted currency amount with the correct currency symbol.
  200. // If a symbol cannot be found for the reqested currency, the the key is used
  201. // instead. If the currency key requested is not recognized, it is used as the
  202. // symbol, and an error is returned with the formatted string.
  203. func (t *Translator) FmtCurrencySafe(typ CurrencyType, currency string, number interface{}) (string, error) {
  204. return t.locale.Number.FmtCurrencySafe(typ, currency, toFloat64(number))
  205. }
  206. // FmtCurrencyWholeSafe does exactly what FormatCurrency does, but it leaves off
  207. // any decimal places. AKA, it would return $100 rather than $100.00.
  208. func (t *Translator) FmtCurrencyWholeSafe(typ CurrencyType, currency string, number interface{}) (string, error) {
  209. return t.locale.Number.FmtCurrencyWholeSafe(typ, currency, toFloat64(number))
  210. }
  211. // FmtCurrency takes a float number and a currency key and returns a string
  212. // with a properly formatted currency amount with the correct currency symbol.
  213. // If a symbol cannot be found for the reqested currency, this will panic, use
  214. // FmtCurrencySafe for non panicing variant.
  215. func (t *Translator) FmtCurrency(typ CurrencyType, currency string, number interface{}) string {
  216. return t.locale.Number.FmtCurrency(typ, currency, toFloat64(number))
  217. }
  218. // FmtCurrencyWhole does exactly what FormatCurrency does, but it leaves off
  219. // any decimal places. AKA, it would return $100 rather than $100.00.
  220. // If a symbol cannot be found for the reqested currency, this will panic, use
  221. // FmtCurrencyWholeSafe for non panicing variant.
  222. func (t *Translator) FmtCurrencyWhole(typ CurrencyType, currency string, number interface{}) string {
  223. return t.locale.Number.FmtCurrencyWhole(typ, currency, toFloat64(number))
  224. }
  225. // FmtNumber takes a float number and returns a properly formatted string
  226. // representation of that number according to the locale's number format.
  227. func (t *Translator) FmtNumber(number interface{}) string {
  228. return t.locale.Number.FmtNumber(toFloat64(number))
  229. }
  230. // FmtNumberWhole does exactly what FormatNumber does, but it leaves off any
  231. // decimal places. AKA, it would return 100 rather than 100.01.
  232. func (t *Translator) FmtNumberWhole(number interface{}) string {
  233. return t.locale.Number.FmtNumberWhole(toFloat64(number))
  234. }
  235. // FmtPercent takes a float number and returns a properly formatted string
  236. // representation of that number as a percentage according to the locale's
  237. // percentage format.
  238. func (t *Translator) FmtPercent(number interface{}) string {
  239. return t.locale.Number.FmtPercent(toFloat64(number))
  240. }