OSDN Git Service

new repo
[bytom/vapor.git] / vendor / golang.org / x / text / language / display / display.go
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 //go:generate go run maketables.go -output tables.go
6
7 // Package display provides display names for languages, scripts and regions in
8 // a requested language.
9 //
10 // The data is based on CLDR's localeDisplayNames. It includes the names of the
11 // draft level "contributed" or "approved". The resulting tables are quite
12 // large. The display package is designed so that users can reduce the linked-in
13 // table sizes by cherry picking the languages one wishes to support. There is a
14 // Dictionary defined for a selected set of common languages for this purpose.
15 package display // import "golang.org/x/text/language/display"
16
17 import (
18         "strings"
19
20         "golang.org/x/text/language"
21 )
22
23 /*
24 TODO:
25 All fairly low priority at the moment:
26   - Include alternative and variants as an option (using func options).
27   - Option for returning the empty string for undefined values.
28   - Support variants, currencies, time zones, option names and other data
29     provided in CLDR.
30   - Do various optimizations:
31     - Reduce size of offset tables.
32     - Consider compressing infrequently used languages and decompress on demand.
33 */
34
35 // A Namer is used to get the name for a given value, such as a Tag, Language,
36 // Script or Region.
37 type Namer interface {
38         // Name returns a display string for the given value. A Namer returns an
39         // empty string for values it does not support. A Namer may support naming
40         // an unspecified value. For example, when getting the name for a region for
41         // a tag that does not have a defined Region, it may return the name for an
42         // unknown region. It is up to the user to filter calls to Name for values
43         // for which one does not want to have a name string.
44         Name(x interface{}) string
45 }
46
47 var (
48         // Supported lists the languages for which names are defined.
49         Supported language.Coverage
50
51         // The set of all possible values for which names are defined. Note that not
52         // all Namer implementations will cover all the values of a given type.
53         // A Namer will return the empty string for unsupported values.
54         Values language.Coverage
55
56         matcher language.Matcher
57 )
58
59 func init() {
60         tags := make([]language.Tag, numSupported)
61         s := supported
62         for i := range tags {
63                 p := strings.IndexByte(s, '|')
64                 tags[i] = language.Raw.Make(s[:p])
65                 s = s[p+1:]
66         }
67         matcher = language.NewMatcher(tags)
68         Supported = language.NewCoverage(tags)
69
70         Values = language.NewCoverage(langTagSet.Tags, supportedScripts, supportedRegions)
71 }
72
73 // Languages returns a Namer for naming languages. It returns nil if there is no
74 // data for the given tag. The type passed to Name must be either language.Base
75 // or language.Tag. Note that the result may differ between passing a tag or its
76 // base language. For example, for English, passing "nl-BE" would return Flemish
77 // whereas passing "nl" returns "Dutch".
78 func Languages(t language.Tag) Namer {
79         if _, index, conf := matcher.Match(t); conf != language.No {
80                 return languageNamer(index)
81         }
82         return nil
83 }
84
85 type languageNamer int
86
87 func (n languageNamer) name(i int) string {
88         return lookup(langHeaders[:], int(n), i)
89 }
90
91 // Name implements the Namer interface for language names.
92 func (n languageNamer) Name(x interface{}) string {
93         return nameLanguage(n, x)
94 }
95
96 // nonEmptyIndex walks up the parent chain until a non-empty header is found.
97 // It returns -1 if no index could be found.
98 func nonEmptyIndex(h []header, index int) int {
99         for ; index != -1 && h[index].data == ""; index = int(parents[index]) {
100         }
101         return index
102 }
103
104 // Scripts returns a Namer for naming scripts. It returns nil if there is no
105 // data for the given tag. The type passed to Name must be either a
106 // language.Script or a language.Tag. It will not attempt to infer a script for
107 // tags with an unspecified script.
108 func Scripts(t language.Tag) Namer {
109         if _, index, conf := matcher.Match(t); conf != language.No {
110                 if index = nonEmptyIndex(scriptHeaders[:], index); index != -1 {
111                         return scriptNamer(index)
112                 }
113         }
114         return nil
115 }
116
117 type scriptNamer int
118
119 func (n scriptNamer) name(i int) string {
120         return lookup(scriptHeaders[:], int(n), i)
121 }
122
123 // Name implements the Namer interface for script names.
124 func (n scriptNamer) Name(x interface{}) string {
125         return nameScript(n, x)
126 }
127
128 // Regions returns a Namer for naming regions. It returns nil if there is no
129 // data for the given tag. The type passed to Name must be either a
130 // language.Region or a language.Tag. It will not attempt to infer a region for
131 // tags with an unspecified region.
132 func Regions(t language.Tag) Namer {
133         if _, index, conf := matcher.Match(t); conf != language.No {
134                 if index = nonEmptyIndex(regionHeaders[:], index); index != -1 {
135                         return regionNamer(index)
136                 }
137         }
138         return nil
139 }
140
141 type regionNamer int
142
143 func (n regionNamer) name(i int) string {
144         return lookup(regionHeaders[:], int(n), i)
145 }
146
147 // Name implements the Namer interface for region names.
148 func (n regionNamer) Name(x interface{}) string {
149         return nameRegion(n, x)
150 }
151
152 // Tags returns a Namer for giving a full description of a tag. The names of
153 // scripts and regions that are not already implied by the language name will
154 // in appended within parentheses. It returns nil if there is not data for the
155 // given tag. The type passed to Name must be a tag.
156 func Tags(t language.Tag) Namer {
157         if _, index, conf := matcher.Match(t); conf != language.No {
158                 return tagNamer(index)
159         }
160         return nil
161 }
162
163 type tagNamer int
164
165 // Name implements the Namer interface for tag names.
166 func (n tagNamer) Name(x interface{}) string {
167         return nameTag(languageNamer(n), scriptNamer(n), regionNamer(n), x)
168 }
169
170 // lookup finds the name for an entry in a global table, traversing the
171 // inheritance hierarchy if needed.
172 func lookup(table []header, dict, want int) string {
173         for dict != -1 {
174                 if s := table[dict].name(want); s != "" {
175                         return s
176                 }
177                 dict = int(parents[dict])
178         }
179         return ""
180 }
181
182 // A Dictionary holds a collection of Namers for a single language. One can
183 // reduce the amount of data linked in to a binary by only referencing
184 // Dictionaries for the languages one needs to support instead of using the
185 // generic Namer factories.
186 type Dictionary struct {
187         parent *Dictionary
188         lang   header
189         script header
190         region header
191 }
192
193 // Tags returns a Namer for giving a full description of a tag. The names of
194 // scripts and regions that are not already implied by the language name will
195 // in appended within parentheses. It returns nil if there is not data for the
196 // given tag. The type passed to Name must be a tag.
197 func (d *Dictionary) Tags() Namer {
198         return dictTags{d}
199 }
200
201 type dictTags struct {
202         d *Dictionary
203 }
204
205 // Name implements the Namer interface for tag names.
206 func (n dictTags) Name(x interface{}) string {
207         return nameTag(dictLanguages{n.d}, dictScripts{n.d}, dictRegions{n.d}, x)
208 }
209
210 // Languages returns a Namer for naming languages. It returns nil if there is no
211 // data for the given tag. The type passed to Name must be either language.Base
212 // or language.Tag. Note that the result may differ between passing a tag or its
213 // base language. For example, for English, passing "nl-BE" would return Flemish
214 // whereas passing "nl" returns "Dutch".
215 func (d *Dictionary) Languages() Namer {
216         return dictLanguages{d}
217 }
218
219 type dictLanguages struct {
220         d *Dictionary
221 }
222
223 func (n dictLanguages) name(i int) string {
224         for d := n.d; d != nil; d = d.parent {
225                 if s := d.lang.name(i); s != "" {
226                         return s
227                 }
228         }
229         return ""
230 }
231
232 // Name implements the Namer interface for language names.
233 func (n dictLanguages) Name(x interface{}) string {
234         return nameLanguage(n, x)
235 }
236
237 // Scripts returns a Namer for naming scripts. It returns nil if there is no
238 // data for the given tag. The type passed to Name must be either a
239 // language.Script or a language.Tag. It will not attempt to infer a script for
240 // tags with an unspecified script.
241 func (d *Dictionary) Scripts() Namer {
242         return dictScripts{d}
243 }
244
245 type dictScripts struct {
246         d *Dictionary
247 }
248
249 func (n dictScripts) name(i int) string {
250         for d := n.d; d != nil; d = d.parent {
251                 if s := d.script.name(i); s != "" {
252                         return s
253                 }
254         }
255         return ""
256 }
257
258 // Name implements the Namer interface for script names.
259 func (n dictScripts) Name(x interface{}) string {
260         return nameScript(n, x)
261 }
262
263 // Regions returns a Namer for naming regions. It returns nil if there is no
264 // data for the given tag. The type passed to Name must be either a
265 // language.Region or a language.Tag. It will not attempt to infer a region for
266 // tags with an unspecified region.
267 func (d *Dictionary) Regions() Namer {
268         return dictRegions{d}
269 }
270
271 type dictRegions struct {
272         d *Dictionary
273 }
274
275 func (n dictRegions) name(i int) string {
276         for d := n.d; d != nil; d = d.parent {
277                 if s := d.region.name(i); s != "" {
278                         return s
279                 }
280         }
281         return ""
282 }
283
284 // Name implements the Namer interface for region names.
285 func (n dictRegions) Name(x interface{}) string {
286         return nameRegion(n, x)
287 }
288
289 // A SelfNamer implements a Namer that returns the name of language in this same
290 // language. It provides a very compact mechanism to provide a comprehensive
291 // list of languages to users in their native language.
292 type SelfNamer struct {
293         // Supported defines the values supported by this Namer.
294         Supported language.Coverage
295 }
296
297 var (
298         // Self is a shared instance of a SelfNamer.
299         Self *SelfNamer = &self
300
301         self = SelfNamer{language.NewCoverage(selfTagSet.Tags)}
302 )
303
304 // Name returns the name of a given language tag in the language identified by
305 // this tag. It supports both the language.Base and language.Tag types.
306 func (n SelfNamer) Name(x interface{}) string {
307         t, _ := language.All.Compose(x)
308         base, scr, reg := t.Raw()
309         baseScript := language.Script{}
310         if (scr == language.Script{} && reg != language.Region{}) {
311                 // For looking up in the self dictionary, we need to select the
312                 // maximized script. This is even the case if the script isn't
313                 // specified.
314                 s1, _ := t.Script()
315                 if baseScript = getScript(base); baseScript != s1 {
316                         scr = s1
317                 }
318         }
319
320         i, scr, reg := selfTagSet.index(base, scr, reg)
321         if i == -1 {
322                 return ""
323         }
324
325         // Only return the display name if the script matches the expected script.
326         if (scr != language.Script{}) {
327                 if (baseScript == language.Script{}) {
328                         baseScript = getScript(base)
329                 }
330                 if baseScript != scr {
331                         return ""
332                 }
333         }
334
335         return selfHeaders[0].name(i)
336 }
337
338 // getScript returns the maximized script for a base language.
339 func getScript(b language.Base) language.Script {
340         tag, _ := language.Raw.Compose(b)
341         scr, _ := tag.Script()
342         return scr
343 }