rules.go 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293
  1. package locales
  2. import (
  3. "strconv"
  4. "time"
  5. "github.com/go-playground/locales/currency"
  6. )
  7. // // ErrBadNumberValue is returned when the number passed for
  8. // // plural rule determination cannot be parsed
  9. // type ErrBadNumberValue struct {
  10. // NumberValue string
  11. // InnerError error
  12. // }
  13. // // Error returns ErrBadNumberValue error string
  14. // func (e *ErrBadNumberValue) Error() string {
  15. // return fmt.Sprintf("Invalid Number Value '%s' %s", e.NumberValue, e.InnerError)
  16. // }
  17. // var _ error = new(ErrBadNumberValue)
  18. // PluralRule denotes the type of plural rules
  19. type PluralRule int
  20. // PluralRule's
  21. const (
  22. PluralRuleUnknown PluralRule = iota
  23. PluralRuleZero // zero
  24. PluralRuleOne // one - singular
  25. PluralRuleTwo // two - dual
  26. PluralRuleFew // few - paucal
  27. PluralRuleMany // many - also used for fractions if they have a separate class
  28. PluralRuleOther // other - required—general plural form—also used if the language only has a single form
  29. )
  30. const (
  31. pluralsString = "UnknownZeroOneTwoFewManyOther"
  32. )
  33. // Translator encapsulates an instance of a locale
  34. // NOTE: some values are returned as a []byte just in case the caller
  35. // wishes to add more and can help avoid allocations; otherwise just cast as string
  36. type Translator interface {
  37. // The following Functions are for overriding, debugging or developing
  38. // with a Translator Locale
  39. // Locale returns the string value of the translator
  40. Locale() string
  41. // returns an array of cardinal plural rules associated
  42. // with this translator
  43. PluralsCardinal() []PluralRule
  44. // returns an array of ordinal plural rules associated
  45. // with this translator
  46. PluralsOrdinal() []PluralRule
  47. // returns an array of range plural rules associated
  48. // with this translator
  49. PluralsRange() []PluralRule
  50. // returns the cardinal PluralRule given 'num' and digits/precision of 'v' for locale
  51. CardinalPluralRule(num float64, v uint64) PluralRule
  52. // returns the ordinal PluralRule given 'num' and digits/precision of 'v' for locale
  53. OrdinalPluralRule(num float64, v uint64) PluralRule
  54. // returns the ordinal PluralRule given 'num1', 'num2' and digits/precision of 'v1' and 'v2' for locale
  55. RangePluralRule(num1 float64, v1 uint64, num2 float64, v2 uint64) PluralRule
  56. // returns the locales abbreviated month given the 'month' provided
  57. MonthAbbreviated(month time.Month) string
  58. // returns the locales abbreviated months
  59. MonthsAbbreviated() []string
  60. // returns the locales narrow month given the 'month' provided
  61. MonthNarrow(month time.Month) string
  62. // returns the locales narrow months
  63. MonthsNarrow() []string
  64. // returns the locales wide month given the 'month' provided
  65. MonthWide(month time.Month) string
  66. // returns the locales wide months
  67. MonthsWide() []string
  68. // returns the locales abbreviated weekday given the 'weekday' provided
  69. WeekdayAbbreviated(weekday time.Weekday) string
  70. // returns the locales abbreviated weekdays
  71. WeekdaysAbbreviated() []string
  72. // returns the locales narrow weekday given the 'weekday' provided
  73. WeekdayNarrow(weekday time.Weekday) string
  74. // WeekdaysNarrowreturns the locales narrow weekdays
  75. WeekdaysNarrow() []string
  76. // returns the locales short weekday given the 'weekday' provided
  77. WeekdayShort(weekday time.Weekday) string
  78. // returns the locales short weekdays
  79. WeekdaysShort() []string
  80. // returns the locales wide weekday given the 'weekday' provided
  81. WeekdayWide(weekday time.Weekday) string
  82. // returns the locales wide weekdays
  83. WeekdaysWide() []string
  84. // The following Functions are common Formatting functionsfor the Translator's Locale
  85. // returns 'num' with digits/precision of 'v' for locale and handles both Whole and Real numbers based on 'v'
  86. FmtNumber(num float64, v uint64) string
  87. // returns 'num' with digits/precision of 'v' for locale and handles both Whole and Real numbers based on 'v'
  88. // NOTE: 'num' passed into FmtPercent is assumed to be in percent already
  89. FmtPercent(num float64, v uint64) string
  90. // returns the currency representation of 'num' with digits/precision of 'v' for locale
  91. FmtCurrency(num float64, v uint64, currency currency.Type) string
  92. // returns the currency representation of 'num' with digits/precision of 'v' for locale
  93. // in accounting notation.
  94. FmtAccounting(num float64, v uint64, currency currency.Type) string
  95. // returns the short date representation of 't' for locale
  96. FmtDateShort(t time.Time) string
  97. // returns the medium date representation of 't' for locale
  98. FmtDateMedium(t time.Time) string
  99. // returns the long date representation of 't' for locale
  100. FmtDateLong(t time.Time) string
  101. // returns the full date representation of 't' for locale
  102. FmtDateFull(t time.Time) string
  103. // returns the short time representation of 't' for locale
  104. FmtTimeShort(t time.Time) string
  105. // returns the medium time representation of 't' for locale
  106. FmtTimeMedium(t time.Time) string
  107. // returns the long time representation of 't' for locale
  108. FmtTimeLong(t time.Time) string
  109. // returns the full time representation of 't' for locale
  110. FmtTimeFull(t time.Time) string
  111. }
  112. // String returns the string value of PluralRule
  113. func (p PluralRule) String() string {
  114. switch p {
  115. case PluralRuleZero:
  116. return pluralsString[7:11]
  117. case PluralRuleOne:
  118. return pluralsString[11:14]
  119. case PluralRuleTwo:
  120. return pluralsString[14:17]
  121. case PluralRuleFew:
  122. return pluralsString[17:20]
  123. case PluralRuleMany:
  124. return pluralsString[20:24]
  125. case PluralRuleOther:
  126. return pluralsString[24:]
  127. default:
  128. return pluralsString[:7]
  129. }
  130. }
  131. //
  132. // Precision Notes:
  133. //
  134. // must specify a precision >= 0, and here is why https://play.golang.org/p/LyL90U0Vyh
  135. //
  136. // v := float64(3.141)
  137. // i := float64(int64(v))
  138. //
  139. // fmt.Println(v - i)
  140. //
  141. // or
  142. //
  143. // s := strconv.FormatFloat(v-i, 'f', -1, 64)
  144. // fmt.Println(s)
  145. //
  146. // these will not print what you'd expect: 0.14100000000000001
  147. // and so this library requires a precision to be specified, or
  148. // inaccurate plural rules could be applied.
  149. //
  150. //
  151. //
  152. // n - absolute value of the source number (integer and decimals).
  153. // i - integer digits of n.
  154. // v - number of visible fraction digits in n, with trailing zeros.
  155. // w - number of visible fraction digits in n, without trailing zeros.
  156. // f - visible fractional digits in n, with trailing zeros.
  157. // t - visible fractional digits in n, without trailing zeros.
  158. //
  159. //
  160. // Func(num float64, v uint64) // v = digits/precision and prevents -1 as a special case as this can lead to very unexpected behaviour, see precision note's above.
  161. //
  162. // n := math.Abs(num)
  163. // i := int64(n)
  164. // v := v
  165. //
  166. //
  167. // w := strconv.FormatFloat(num-float64(i), 'f', int(v), 64) // then parse backwards on string until no more zero's....
  168. // f := strconv.FormatFloat(n, 'f', int(v), 64) // then turn everything after decimal into an int64
  169. // t := strconv.FormatFloat(n, 'f', int(v), 64) // then parse backwards on string until no more zero's....
  170. //
  171. //
  172. //
  173. // General Inclusion Rules
  174. // - v will always be available inherently
  175. // - all require n
  176. // - w requires i
  177. //
  178. // W returns the number of visible fraction digits in N, without trailing zeros.
  179. func W(n float64, v uint64) (w int64) {
  180. s := strconv.FormatFloat(n-float64(int64(n)), 'f', int(v), 64)
  181. // with either be '0' or '0.xxxx', so if 1 then w will be zero
  182. // otherwise need to parse
  183. if len(s) != 1 {
  184. s = s[2:]
  185. end := len(s) + 1
  186. for i := end; i >= 0; i-- {
  187. if s[i] != '0' {
  188. end = i + 1
  189. break
  190. }
  191. }
  192. w = int64(len(s[:end]))
  193. }
  194. return
  195. }
  196. // F returns the visible fractional digits in N, with trailing zeros.
  197. func F(n float64, v uint64) (f int64) {
  198. s := strconv.FormatFloat(n-float64(int64(n)), 'f', int(v), 64)
  199. // with either be '0' or '0.xxxx', so if 1 then f will be zero
  200. // otherwise need to parse
  201. if len(s) != 1 {
  202. // ignoring error, because it can't fail as we generated
  203. // the string internally from a real number
  204. f, _ = strconv.ParseInt(s[2:], 10, 64)
  205. }
  206. return
  207. }
  208. // T returns the visible fractional digits in N, without trailing zeros.
  209. func T(n float64, v uint64) (t int64) {
  210. s := strconv.FormatFloat(n-float64(int64(n)), 'f', int(v), 64)
  211. // with either be '0' or '0.xxxx', so if 1 then t will be zero
  212. // otherwise need to parse
  213. if len(s) != 1 {
  214. s = s[2:]
  215. end := len(s) + 1
  216. for i := end; i >= 0; i-- {
  217. if s[i] != '0' {
  218. end = i + 1
  219. break
  220. }
  221. }
  222. // ignoring error, because it can't fail as we generated
  223. // the string internally from a real number
  224. t, _ = strconv.ParseInt(s[:end], 10, 64)
  225. }
  226. return
  227. }