blob: 101fd23c1d4a4b9154c238dcaf1ad79d2f8fed27 [file] [log] [blame]
Scott Bakere7144bc2019-10-01 14:16:47 -07001// 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}