You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

178 lines
4.7 KiB

  1. // Copyright 2017 The Go Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. package number
  5. import (
  6. "fmt"
  7. "golang.org/x/text/internal/number"
  8. "golang.org/x/text/language"
  9. )
  10. // An Option configures a Formatter.
  11. type Option option
  12. type option func(tag language.Tag, f *number.Formatter)
  13. // TODO: SpellOut requires support of the ICU RBNF format.
  14. // func SpellOut() Option
  15. // NoSeparator causes a number to be displayed without grouping separators.
  16. func NoSeparator() Option {
  17. return func(t language.Tag, f *number.Formatter) {
  18. f.GroupingSize = [2]uint8{}
  19. }
  20. }
  21. // MaxIntegerDigits limits the number of integer digits, eliminating the
  22. // most significant digits.
  23. func MaxIntegerDigits(max int) Option {
  24. return func(t language.Tag, f *number.Formatter) {
  25. if max >= 1<<8 {
  26. max = (1 << 8) - 1
  27. }
  28. f.MaxIntegerDigits = uint8(max)
  29. }
  30. }
  31. // MinIntegerDigits specifies the minimum number of integer digits, adding
  32. // leading zeros when needed.
  33. func MinIntegerDigits(min int) Option {
  34. return func(t language.Tag, f *number.Formatter) {
  35. if min >= 1<<8 {
  36. min = (1 << 8) - 1
  37. }
  38. f.MinIntegerDigits = uint8(min)
  39. }
  40. }
  41. // MaxFractionDigits specifies the maximum number of fractional digits.
  42. func MaxFractionDigits(max int) Option {
  43. return func(t language.Tag, f *number.Formatter) {
  44. if max >= 1<<15 {
  45. max = (1 << 15) - 1
  46. }
  47. f.MaxFractionDigits = int16(max)
  48. }
  49. }
  50. // MinFractionDigits specifies the minimum number of fractional digits.
  51. func MinFractionDigits(min int) Option {
  52. return func(t language.Tag, f *number.Formatter) {
  53. if min >= 1<<8 {
  54. min = (1 << 8) - 1
  55. }
  56. f.MinFractionDigits = uint8(min)
  57. }
  58. }
  59. // Precision sets the maximum number of significant digits. A negative value
  60. // means exact.
  61. func Precision(prec int) Option {
  62. return func(t language.Tag, f *number.Formatter) {
  63. f.SetPrecision(prec)
  64. }
  65. }
  66. // Scale simultaneously sets MinFractionDigits and MaxFractionDigits to the
  67. // given value.
  68. func Scale(decimals int) Option {
  69. return func(t language.Tag, f *number.Formatter) {
  70. f.SetScale(decimals)
  71. }
  72. }
  73. // IncrementString sets the incremental value to which numbers should be
  74. // rounded. For instance: Increment("0.05") will cause 1.44 to round to 1.45.
  75. // IncrementString also sets scale to the scale of the increment.
  76. func IncrementString(decimal string) Option {
  77. increment := 0
  78. scale := 0
  79. d := decimal
  80. p := 0
  81. for ; p < len(d) && '0' <= d[p] && d[p] <= '9'; p++ {
  82. increment *= 10
  83. increment += int(d[p]) - '0'
  84. }
  85. if p < len(d) && d[p] == '.' {
  86. for p++; p < len(d) && '0' <= d[p] && d[p] <= '9'; p++ {
  87. increment *= 10
  88. increment += int(d[p]) - '0'
  89. scale++
  90. }
  91. }
  92. if p < len(d) {
  93. increment = 0
  94. scale = 0
  95. }
  96. return func(t language.Tag, f *number.Formatter) {
  97. f.Increment = uint32(increment)
  98. f.IncrementScale = uint8(scale)
  99. f.SetScale(scale)
  100. }
  101. }
  102. func noop(language.Tag, *number.Formatter) {}
  103. // PatternOverrides allows users to specify alternative patterns for specific
  104. // languages. The Pattern will be overridden for all languages in a subgroup as
  105. // well. The function will panic for invalid input. It is best to create this
  106. // option at startup time.
  107. // PatternOverrides must be the first Option passed to a formatter.
  108. func PatternOverrides(patterns map[string]string) Option {
  109. // TODO: make it so that it does not have to be the first option.
  110. // TODO: use -x-nochild to indicate it does not override child tags.
  111. m := map[language.Tag]*number.Pattern{}
  112. for k, v := range patterns {
  113. tag := language.MustParse(k)
  114. p, err := number.ParsePattern(v)
  115. if err != nil {
  116. panic(fmt.Errorf("number: PatternOverrides: %v", err))
  117. }
  118. m[tag] = p
  119. }
  120. return func(t language.Tag, f *number.Formatter) {
  121. // TODO: Use language grouping relation instead of parent relation.
  122. // TODO: Should parent implement the grouping relation?
  123. for lang := t; ; lang = t.Parent() {
  124. if p, ok := m[lang]; ok {
  125. f.Pattern = *p
  126. break
  127. }
  128. if lang == language.Und {
  129. break
  130. }
  131. }
  132. }
  133. }
  134. // FormatWidth sets the total format width.
  135. func FormatWidth(n int) Option {
  136. if n <= 0 {
  137. return noop
  138. }
  139. return func(t language.Tag, f *number.Formatter) {
  140. f.FormatWidth = uint16(n)
  141. if f.PadRune == 0 {
  142. f.PadRune = ' '
  143. }
  144. }
  145. }
  146. // Pad sets the rune to be used for filling up to the format width.
  147. func Pad(r rune) Option {
  148. return func(t language.Tag, f *number.Formatter) {
  149. f.PadRune = r
  150. }
  151. }
  152. // TODO:
  153. // - FormatPosition (using type aliasing?)
  154. // - Multiplier: find a better way to represent and figure out what to do
  155. // with clashes with percent/permille.
  156. // - NumberingSystem(nu string): not accessable in number.Info now. Also, should
  157. // this be keyed by language or generic?
  158. // - SymbolOverrides(symbols map[string]map[number.SymbolType]string) Option