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