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