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.

187 lines
4.8 KiB

  1. // Copyright 2014 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 language
  5. import (
  6. "fmt"
  7. "sort"
  8. "golang.org/x/text/internal/language"
  9. )
  10. // The Coverage interface is used to define the level of coverage of an
  11. // internationalization service. Note that not all types are supported by all
  12. // services. As lists may be generated on the fly, it is recommended that users
  13. // of a Coverage cache the results.
  14. type Coverage interface {
  15. // Tags returns the list of supported tags.
  16. Tags() []Tag
  17. // BaseLanguages returns the list of supported base languages.
  18. BaseLanguages() []Base
  19. // Scripts returns the list of supported scripts.
  20. Scripts() []Script
  21. // Regions returns the list of supported regions.
  22. Regions() []Region
  23. }
  24. var (
  25. // Supported defines a Coverage that lists all supported subtags. Tags
  26. // always returns nil.
  27. Supported Coverage = allSubtags{}
  28. )
  29. // TODO:
  30. // - Support Variants, numbering systems.
  31. // - CLDR coverage levels.
  32. // - Set of common tags defined in this package.
  33. type allSubtags struct{}
  34. // Regions returns the list of supported regions. As all regions are in a
  35. // consecutive range, it simply returns a slice of numbers in increasing order.
  36. // The "undefined" region is not returned.
  37. func (s allSubtags) Regions() []Region {
  38. reg := make([]Region, language.NumRegions)
  39. for i := range reg {
  40. reg[i] = Region{language.Region(i + 1)}
  41. }
  42. return reg
  43. }
  44. // Scripts returns the list of supported scripts. As all scripts are in a
  45. // consecutive range, it simply returns a slice of numbers in increasing order.
  46. // The "undefined" script is not returned.
  47. func (s allSubtags) Scripts() []Script {
  48. scr := make([]Script, language.NumScripts)
  49. for i := range scr {
  50. scr[i] = Script{language.Script(i + 1)}
  51. }
  52. return scr
  53. }
  54. // BaseLanguages returns the list of all supported base languages. It generates
  55. // the list by traversing the internal structures.
  56. func (s allSubtags) BaseLanguages() []Base {
  57. bs := language.BaseLanguages()
  58. base := make([]Base, len(bs))
  59. for i, b := range bs {
  60. base[i] = Base{b}
  61. }
  62. return base
  63. }
  64. // Tags always returns nil.
  65. func (s allSubtags) Tags() []Tag {
  66. return nil
  67. }
  68. // coverage is used by NewCoverage which is used as a convenient way for
  69. // creating Coverage implementations for partially defined data. Very often a
  70. // package will only need to define a subset of slices. coverage provides a
  71. // convenient way to do this. Moreover, packages using NewCoverage, instead of
  72. // their own implementation, will not break if later new slice types are added.
  73. type coverage struct {
  74. tags func() []Tag
  75. bases func() []Base
  76. scripts func() []Script
  77. regions func() []Region
  78. }
  79. func (s *coverage) Tags() []Tag {
  80. if s.tags == nil {
  81. return nil
  82. }
  83. return s.tags()
  84. }
  85. // bases implements sort.Interface and is used to sort base languages.
  86. type bases []Base
  87. func (b bases) Len() int {
  88. return len(b)
  89. }
  90. func (b bases) Swap(i, j int) {
  91. b[i], b[j] = b[j], b[i]
  92. }
  93. func (b bases) Less(i, j int) bool {
  94. return b[i].langID < b[j].langID
  95. }
  96. // BaseLanguages returns the result from calling s.bases if it is specified or
  97. // otherwise derives the set of supported base languages from tags.
  98. func (s *coverage) BaseLanguages() []Base {
  99. if s.bases == nil {
  100. tags := s.Tags()
  101. if len(tags) == 0 {
  102. return nil
  103. }
  104. a := make([]Base, len(tags))
  105. for i, t := range tags {
  106. a[i] = Base{language.Language(t.lang())}
  107. }
  108. sort.Sort(bases(a))
  109. k := 0
  110. for i := 1; i < len(a); i++ {
  111. if a[k] != a[i] {
  112. k++
  113. a[k] = a[i]
  114. }
  115. }
  116. return a[:k+1]
  117. }
  118. return s.bases()
  119. }
  120. func (s *coverage) Scripts() []Script {
  121. if s.scripts == nil {
  122. return nil
  123. }
  124. return s.scripts()
  125. }
  126. func (s *coverage) Regions() []Region {
  127. if s.regions == nil {
  128. return nil
  129. }
  130. return s.regions()
  131. }
  132. // NewCoverage returns a Coverage for the given lists. It is typically used by
  133. // packages providing internationalization services to define their level of
  134. // coverage. A list may be of type []T or func() []T, where T is either Tag,
  135. // Base, Script or Region. The returned Coverage derives the value for Bases
  136. // from Tags if no func or slice for []Base is specified. For other unspecified
  137. // types the returned Coverage will return nil for the respective methods.
  138. func NewCoverage(list ...interface{}) Coverage {
  139. s := &coverage{}
  140. for _, x := range list {
  141. switch v := x.(type) {
  142. case func() []Base:
  143. s.bases = v
  144. case func() []Script:
  145. s.scripts = v
  146. case func() []Region:
  147. s.regions = v
  148. case func() []Tag:
  149. s.tags = v
  150. case []Base:
  151. s.bases = func() []Base { return v }
  152. case []Script:
  153. s.scripts = func() []Script { return v }
  154. case []Region:
  155. s.regions = func() []Region { return v }
  156. case []Tag:
  157. s.tags = func() []Tag { return v }
  158. default:
  159. panic(fmt.Sprintf("language: unsupported set type %T", v))
  160. }
  161. }
  162. return s
  163. }