blob: a24fd1a4d699d102506d638723fd80aa9aada32c [file] [log] [blame]
Don Newton98fd8812019-09-23 15:15:02 -04001// 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 "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.
18type 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
32var (
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
43type 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.
48func (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.
59func (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.
69func (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.
79func (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.
88type coverage struct {
89 tags func() []Tag
90 bases func() []Base
91 scripts func() []Script
92 regions func() []Region
93}
94
95func (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.
103type bases []Base
104
105func (b bases) Len() int {
106 return len(b)
107}
108
109func (b bases) Swap(i, j int) {
110 b[i], b[j] = b[j], b[i]
111}
112
113func (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.
119func (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
142func (s *coverage) Scripts() []Script {
143 if s.scripts == nil {
144 return nil
145 }
146 return s.scripts()
147}
148
149func (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.
162func 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}