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.
 
 
 

153 lines
4.7 KiB

  1. // Copyright 2016 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. //go:generate go run gen.go gen_common.go
  5. // Package number contains tools and data for formatting numbers.
  6. package number
  7. import (
  8. "unicode/utf8"
  9. "golang.org/x/text/internal/language/compact"
  10. "golang.org/x/text/language"
  11. )
  12. // Info holds number formatting configuration data.
  13. type Info struct {
  14. system systemData // numbering system information
  15. symIndex symOffset // index to symbols
  16. }
  17. // InfoFromLangID returns a Info for the given compact language identifier and
  18. // numbering system identifier. If system is the empty string, the default
  19. // numbering system will be taken for that language.
  20. func InfoFromLangID(compactIndex compact.ID, numberSystem string) Info {
  21. p := langToDefaults[compactIndex]
  22. // Lookup the entry for the language.
  23. pSymIndex := symOffset(0) // Default: Latin, default symbols
  24. system, ok := systemMap[numberSystem]
  25. if !ok {
  26. // Take the value for the default numbering system. This is by far the
  27. // most common case as an alternative numbering system is hardly used.
  28. if p&hasNonLatnMask == 0 { // Latn digits.
  29. pSymIndex = p
  30. } else { // Non-Latn or multiple numbering systems.
  31. // Take the first entry from the alternatives list.
  32. data := langToAlt[p&^hasNonLatnMask]
  33. pSymIndex = data.symIndex
  34. system = data.system
  35. }
  36. } else {
  37. langIndex := compactIndex
  38. ns := system
  39. outerLoop:
  40. for ; ; p = langToDefaults[langIndex] {
  41. if p&hasNonLatnMask == 0 {
  42. if ns == 0 {
  43. // The index directly points to the symbol data.
  44. pSymIndex = p
  45. break
  46. }
  47. // Move to the parent and retry.
  48. langIndex = langIndex.Parent()
  49. } else {
  50. // The index points to a list of symbol data indexes.
  51. for _, e := range langToAlt[p&^hasNonLatnMask:] {
  52. if e.compactTag != langIndex {
  53. if langIndex == 0 {
  54. // The CLDR root defines full symbol information for
  55. // all numbering systems (even though mostly by
  56. // means of aliases). Fall back to the default entry
  57. // for Latn if there is no data for the numbering
  58. // system of this language.
  59. if ns == 0 {
  60. break
  61. }
  62. // Fall back to Latin and start from the original
  63. // language. See
  64. // https://unicode.org/reports/tr35/#Locale_Inheritance.
  65. ns = numLatn
  66. langIndex = compactIndex
  67. continue outerLoop
  68. }
  69. // Fall back to parent.
  70. langIndex = langIndex.Parent()
  71. } else if e.system == ns {
  72. pSymIndex = e.symIndex
  73. break outerLoop
  74. }
  75. }
  76. }
  77. }
  78. }
  79. if int(system) >= len(numSysData) { // algorithmic
  80. // Will generate ASCII digits in case the user inadvertently calls
  81. // WriteDigit or Digit on it.
  82. d := numSysData[0]
  83. d.id = system
  84. return Info{
  85. system: d,
  86. symIndex: pSymIndex,
  87. }
  88. }
  89. return Info{
  90. system: numSysData[system],
  91. symIndex: pSymIndex,
  92. }
  93. }
  94. // InfoFromTag returns a Info for the given language tag.
  95. func InfoFromTag(t language.Tag) Info {
  96. return InfoFromLangID(tagToID(t), t.TypeForKey("nu"))
  97. }
  98. // IsDecimal reports if the numbering system can convert decimal to native
  99. // symbols one-to-one.
  100. func (n Info) IsDecimal() bool {
  101. return int(n.system.id) < len(numSysData)
  102. }
  103. // WriteDigit writes the UTF-8 sequence for n corresponding to the given ASCII
  104. // digit to dst and reports the number of bytes written. dst must be large
  105. // enough to hold the rune (can be up to utf8.UTFMax bytes).
  106. func (n Info) WriteDigit(dst []byte, asciiDigit rune) int {
  107. copy(dst, n.system.zero[:n.system.digitSize])
  108. dst[n.system.digitSize-1] += byte(asciiDigit - '0')
  109. return int(n.system.digitSize)
  110. }
  111. // AppendDigit appends the UTF-8 sequence for n corresponding to the given digit
  112. // to dst and reports the number of bytes written. dst must be large enough to
  113. // hold the rune (can be up to utf8.UTFMax bytes).
  114. func (n Info) AppendDigit(dst []byte, digit byte) []byte {
  115. dst = append(dst, n.system.zero[:n.system.digitSize]...)
  116. dst[len(dst)-1] += digit
  117. return dst
  118. }
  119. // Digit returns the digit for the numbering system for the corresponding ASCII
  120. // value. For example, ni.Digit('3') could return '三'. Note that the argument
  121. // is the rune constant '3', which equals 51, not the integer constant 3.
  122. func (n Info) Digit(asciiDigit rune) rune {
  123. var x [utf8.UTFMax]byte
  124. n.WriteDigit(x[:], asciiDigit)
  125. r, _ := utf8.DecodeRune(x[:])
  126. return r
  127. }
  128. // Symbol returns the string for the given symbol type.
  129. func (n Info) Symbol(t SymbolType) string {
  130. return symData.Elem(int(symIndex[n.symIndex][t]))
  131. }
  132. func formatForLang(t language.Tag, index []byte) *Pattern {
  133. return &formats[index[tagToID(t)]]
  134. }
  135. func tagToID(t language.Tag) compact.ID {
  136. id, _ := compact.RegionalID(compact.Tag(t))
  137. return id
  138. }