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