123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420 |
- // Copyright 2014 The Go Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style
- // license that can be found in the LICENSE file.
- //go:generate go run maketables.go -output tables.go
- // Package display provides display names for languages, scripts and regions in
- // a requested language.
- //
- // The data is based on CLDR's localeDisplayNames. It includes the names of the
- // draft level "contributed" or "approved". The resulting tables are quite
- // large. The display package is designed so that users can reduce the linked-in
- // table sizes by cherry picking the languages one wishes to support. There is a
- // Dictionary defined for a selected set of common languages for this purpose.
- package display // import "golang.org/x/text/language/display"
- import (
- "fmt"
- "strings"
- "golang.org/x/text/internal/format"
- "golang.org/x/text/language"
- )
- /*
- TODO:
- All fairly low priority at the moment:
- - Include alternative and variants as an option (using func options).
- - Option for returning the empty string for undefined values.
- - Support variants, currencies, time zones, option names and other data
- provided in CLDR.
- - Do various optimizations:
- - Reduce size of offset tables.
- - Consider compressing infrequently used languages and decompress on demand.
- */
- // A Formatter formats a tag in the current language. It is used in conjunction
- // with the message package.
- type Formatter struct {
- lookup func(tag int, x interface{}) string
- x interface{}
- }
- // Format implements "golang.org/x/text/internal/format".Formatter.
- func (f Formatter) Format(state format.State, verb rune) {
- // TODO: there are a lot of inefficiencies in this code. Fix it when we
- // language.Tag has embedded compact tags.
- t := state.Language()
- _, index, _ := matcher.Match(t)
- str := f.lookup(index, f.x)
- if str == "" {
- // TODO: use language-specific punctuation.
- // TODO: use codePattern instead of language?
- if unknown := f.lookup(index, language.Und); unknown != "" {
- fmt.Fprintf(state, "%v (%v)", unknown, f.x)
- } else {
- fmt.Fprintf(state, "[language: %v]", f.x)
- }
- } else {
- state.Write([]byte(str))
- }
- }
- // Language returns a Formatter that renders the name for lang in the
- // current language. x may be a language.Base or a language.Tag.
- // It renders lang in the default language if no translation for the current
- // language is supported.
- func Language(lang interface{}) Formatter {
- return Formatter{langFunc, lang}
- }
- // Region returns a Formatter that renders the name for region in the current
- // language. region may be a language.Region or a language.Tag.
- // It renders region in the default language if no translation for the current
- // language is supported.
- func Region(region interface{}) Formatter {
- return Formatter{regionFunc, region}
- }
- // Script returns a Formatter that renders the name for script in the current
- // language. script may be a language.Script or a language.Tag.
- // It renders script in the default language if no translation for the current
- // language is supported.
- func Script(script interface{}) Formatter {
- return Formatter{scriptFunc, script}
- }
- // Script returns a Formatter that renders the name for tag in the current
- // language. tag may be a language.Tag.
- // It renders tag in the default language if no translation for the current
- // language is supported.
- func Tag(tag interface{}) Formatter {
- return Formatter{tagFunc, tag}
- }
- // A Namer is used to get the name for a given value, such as a Tag, Language,
- // Script or Region.
- type Namer interface {
- // Name returns a display string for the given value. A Namer returns an
- // empty string for values it does not support. A Namer may support naming
- // an unspecified value. For example, when getting the name for a region for
- // a tag that does not have a defined Region, it may return the name for an
- // unknown region. It is up to the user to filter calls to Name for values
- // for which one does not want to have a name string.
- Name(x interface{}) string
- }
- var (
- // Supported lists the languages for which names are defined.
- Supported language.Coverage
- // The set of all possible values for which names are defined. Note that not
- // all Namer implementations will cover all the values of a given type.
- // A Namer will return the empty string for unsupported values.
- Values language.Coverage
- matcher language.Matcher
- )
- func init() {
- tags := make([]language.Tag, numSupported)
- s := supported
- for i := range tags {
- p := strings.IndexByte(s, '|')
- tags[i] = language.Raw.Make(s[:p])
- s = s[p+1:]
- }
- matcher = language.NewMatcher(tags)
- Supported = language.NewCoverage(tags)
- Values = language.NewCoverage(langTagSet.Tags, supportedScripts, supportedRegions)
- }
- // Languages returns a Namer for naming languages. It returns nil if there is no
- // data for the given tag. The type passed to Name must be either language.Base
- // or language.Tag. Note that the result may differ between passing a tag or its
- // base language. For example, for English, passing "nl-BE" would return Flemish
- // whereas passing "nl" returns "Dutch".
- func Languages(t language.Tag) Namer {
- if _, index, conf := matcher.Match(t); conf != language.No {
- return languageNamer(index)
- }
- return nil
- }
- type languageNamer int
- func langFunc(i int, x interface{}) string {
- return nameLanguage(languageNamer(i), x)
- }
- func (n languageNamer) name(i int) string {
- return lookup(langHeaders[:], int(n), i)
- }
- // Name implements the Namer interface for language names.
- func (n languageNamer) Name(x interface{}) string {
- return nameLanguage(n, x)
- }
- // nonEmptyIndex walks up the parent chain until a non-empty header is found.
- // It returns -1 if no index could be found.
- func nonEmptyIndex(h []header, index int) int {
- for ; index != -1 && h[index].data == ""; index = int(parents[index]) {
- }
- return index
- }
- // Scripts returns a Namer for naming scripts. It returns nil if there is no
- // data for the given tag. The type passed to Name must be either a
- // language.Script or a language.Tag. It will not attempt to infer a script for
- // tags with an unspecified script.
- func Scripts(t language.Tag) Namer {
- if _, index, conf := matcher.Match(t); conf != language.No {
- if index = nonEmptyIndex(scriptHeaders[:], index); index != -1 {
- return scriptNamer(index)
- }
- }
- return nil
- }
- type scriptNamer int
- func scriptFunc(i int, x interface{}) string {
- return nameScript(scriptNamer(i), x)
- }
- func (n scriptNamer) name(i int) string {
- return lookup(scriptHeaders[:], int(n), i)
- }
- // Name implements the Namer interface for script names.
- func (n scriptNamer) Name(x interface{}) string {
- return nameScript(n, x)
- }
- // Regions returns a Namer for naming regions. It returns nil if there is no
- // data for the given tag. The type passed to Name must be either a
- // language.Region or a language.Tag. It will not attempt to infer a region for
- // tags with an unspecified region.
- func Regions(t language.Tag) Namer {
- if _, index, conf := matcher.Match(t); conf != language.No {
- if index = nonEmptyIndex(regionHeaders[:], index); index != -1 {
- return regionNamer(index)
- }
- }
- return nil
- }
- type regionNamer int
- func regionFunc(i int, x interface{}) string {
- return nameRegion(regionNamer(i), x)
- }
- func (n regionNamer) name(i int) string {
- return lookup(regionHeaders[:], int(n), i)
- }
- // Name implements the Namer interface for region names.
- func (n regionNamer) Name(x interface{}) string {
- return nameRegion(n, x)
- }
- // Tags returns a Namer for giving a full description of a tag. The names of
- // scripts and regions that are not already implied by the language name will
- // in appended within parentheses. It returns nil if there is not data for the
- // given tag. The type passed to Name must be a tag.
- func Tags(t language.Tag) Namer {
- if _, index, conf := matcher.Match(t); conf != language.No {
- return tagNamer(index)
- }
- return nil
- }
- type tagNamer int
- func tagFunc(i int, x interface{}) string {
- return nameTag(languageNamer(i), scriptNamer(i), regionNamer(i), x)
- }
- // Name implements the Namer interface for tag names.
- func (n tagNamer) Name(x interface{}) string {
- return nameTag(languageNamer(n), scriptNamer(n), regionNamer(n), x)
- }
- // lookup finds the name for an entry in a global table, traversing the
- // inheritance hierarchy if needed.
- func lookup(table []header, dict, want int) string {
- for dict != -1 {
- if s := table[dict].name(want); s != "" {
- return s
- }
- dict = int(parents[dict])
- }
- return ""
- }
- // A Dictionary holds a collection of Namers for a single language. One can
- // reduce the amount of data linked in to a binary by only referencing
- // Dictionaries for the languages one needs to support instead of using the
- // generic Namer factories.
- type Dictionary struct {
- parent *Dictionary
- lang header
- script header
- region header
- }
- // Tags returns a Namer for giving a full description of a tag. The names of
- // scripts and regions that are not already implied by the language name will
- // in appended within parentheses. It returns nil if there is not data for the
- // given tag. The type passed to Name must be a tag.
- func (d *Dictionary) Tags() Namer {
- return dictTags{d}
- }
- type dictTags struct {
- d *Dictionary
- }
- // Name implements the Namer interface for tag names.
- func (n dictTags) Name(x interface{}) string {
- return nameTag(dictLanguages{n.d}, dictScripts{n.d}, dictRegions{n.d}, x)
- }
- // Languages returns a Namer for naming languages. It returns nil if there is no
- // data for the given tag. The type passed to Name must be either language.Base
- // or language.Tag. Note that the result may differ between passing a tag or its
- // base language. For example, for English, passing "nl-BE" would return Flemish
- // whereas passing "nl" returns "Dutch".
- func (d *Dictionary) Languages() Namer {
- return dictLanguages{d}
- }
- type dictLanguages struct {
- d *Dictionary
- }
- func (n dictLanguages) name(i int) string {
- for d := n.d; d != nil; d = d.parent {
- if s := d.lang.name(i); s != "" {
- return s
- }
- }
- return ""
- }
- // Name implements the Namer interface for language names.
- func (n dictLanguages) Name(x interface{}) string {
- return nameLanguage(n, x)
- }
- // Scripts returns a Namer for naming scripts. It returns nil if there is no
- // data for the given tag. The type passed to Name must be either a
- // language.Script or a language.Tag. It will not attempt to infer a script for
- // tags with an unspecified script.
- func (d *Dictionary) Scripts() Namer {
- return dictScripts{d}
- }
- type dictScripts struct {
- d *Dictionary
- }
- func (n dictScripts) name(i int) string {
- for d := n.d; d != nil; d = d.parent {
- if s := d.script.name(i); s != "" {
- return s
- }
- }
- return ""
- }
- // Name implements the Namer interface for script names.
- func (n dictScripts) Name(x interface{}) string {
- return nameScript(n, x)
- }
- // Regions returns a Namer for naming regions. It returns nil if there is no
- // data for the given tag. The type passed to Name must be either a
- // language.Region or a language.Tag. It will not attempt to infer a region for
- // tags with an unspecified region.
- func (d *Dictionary) Regions() Namer {
- return dictRegions{d}
- }
- type dictRegions struct {
- d *Dictionary
- }
- func (n dictRegions) name(i int) string {
- for d := n.d; d != nil; d = d.parent {
- if s := d.region.name(i); s != "" {
- return s
- }
- }
- return ""
- }
- // Name implements the Namer interface for region names.
- func (n dictRegions) Name(x interface{}) string {
- return nameRegion(n, x)
- }
- // A SelfNamer implements a Namer that returns the name of language in this same
- // language. It provides a very compact mechanism to provide a comprehensive
- // list of languages to users in their native language.
- type SelfNamer struct {
- // Supported defines the values supported by this Namer.
- Supported language.Coverage
- }
- var (
- // Self is a shared instance of a SelfNamer.
- Self *SelfNamer = &self
- self = SelfNamer{language.NewCoverage(selfTagSet.Tags)}
- )
- // Name returns the name of a given language tag in the language identified by
- // this tag. It supports both the language.Base and language.Tag types.
- func (n SelfNamer) Name(x interface{}) string {
- t, _ := language.All.Compose(x)
- base, scr, reg := t.Raw()
- baseScript := language.Script{}
- if (scr == language.Script{} && reg != language.Region{}) {
- // For looking up in the self dictionary, we need to select the
- // maximized script. This is even the case if the script isn't
- // specified.
- s1, _ := t.Script()
- if baseScript = getScript(base); baseScript != s1 {
- scr = s1
- }
- }
- i, scr, reg := selfTagSet.index(base, scr, reg)
- if i == -1 {
- return ""
- }
- // Only return the display name if the script matches the expected script.
- if (scr != language.Script{}) {
- if (baseScript == language.Script{}) {
- baseScript = getScript(base)
- }
- if baseScript != scr {
- return ""
- }
- }
- return selfHeaders[0].name(i)
- }
- // getScript returns the maximized script for a base language.
- func getScript(b language.Base) language.Script {
- tag, _ := language.Raw.Compose(b)
- scr, _ := tag.Script()
- return scr
- }
|