github.com/graybobo/golang.org-package-offline-cache@v0.0.0-20200626051047-6608995c132f/x/text/display/display.go (about)

     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  // Package display provides display names for languages, scripts and regions in
     6  // a requested language.
     7  //
     8  // The data is based on CLDR's localeDisplayNames. It includes the names of the
     9  // draft level "contributed" or "approved". The resulting tables are quite
    10  // large. The display package is designed so that users can reduce the linked-in
    11  // table sizes by cherry picking the languages one wishes to support. There is a
    12  // Dictionary defined for a selected set of common languages for this purpose.
    13  package display // import "golang.org/x/text/display"
    14  
    15  //go:generate go run maketables.go -output tables.go
    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  // Scripts returns a Namer for naming scripts. It returns nil if there is no
    97  // data for the given tag. The type passed to Name must be either a
    98  // language.Script or a language.Tag. It will not attempt to infer a script for
    99  // tags with an unspecified script.
   100  func Scripts(t language.Tag) Namer {
   101  	if _, index, conf := matcher.Match(t); conf != language.No {
   102  		return scriptNamer(index)
   103  	}
   104  	return nil
   105  }
   106  
   107  type scriptNamer int
   108  
   109  func (n scriptNamer) name(i int) string {
   110  	return lookup(scriptHeaders[:], int(n), i)
   111  }
   112  
   113  // Name implements the Namer interface for script names.
   114  func (n scriptNamer) Name(x interface{}) string {
   115  	return nameScript(n, x)
   116  }
   117  
   118  // Regions returns a Namer for naming regions. It returns nil if there is no
   119  // data for the given tag. The type passed to Name must be either a
   120  // language.Region or a language.Tag. It will not attempt to infer a region for
   121  // tags with an unspecified region.
   122  func Regions(t language.Tag) Namer {
   123  	if _, index, conf := matcher.Match(t); conf != language.No {
   124  		return regionNamer(index)
   125  	}
   126  	return nil
   127  }
   128  
   129  type regionNamer int
   130  
   131  func (n regionNamer) name(i int) string {
   132  	return lookup(regionHeaders[:], int(n), i)
   133  }
   134  
   135  // Name implements the Namer interface for region names.
   136  func (n regionNamer) Name(x interface{}) string {
   137  	return nameRegion(n, x)
   138  }
   139  
   140  // Tags returns a Namer for giving a full description of a tag. The names of
   141  // scripts and regions that are not already implied by the language name will
   142  // in appended within parentheses. It returns nil if there is not data for the
   143  // given tag. The type passed to Name must be a tag.
   144  func Tags(t language.Tag) Namer {
   145  	if _, index, conf := matcher.Match(t); conf != language.No {
   146  		return tagNamer(index)
   147  	}
   148  	return nil
   149  }
   150  
   151  type tagNamer int
   152  
   153  // Name implements the Namer interface for tag names.
   154  func (n tagNamer) Name(x interface{}) string {
   155  	return nameTag(languageNamer(n), scriptNamer(n), regionNamer(n), x)
   156  }
   157  
   158  // lookup finds the name for an entry in a global table, traversing the
   159  // inheritance hierarchy if needed.
   160  func lookup(table []header, dict, want int) string {
   161  	if want == -1 || dict == -1 {
   162  		return ""
   163  	}
   164  	for dict != -1 {
   165  		if s := table[dict].name(want); s != "" {
   166  			return s
   167  		}
   168  		dict = int(parents[dict])
   169  	}
   170  	return ""
   171  }
   172  
   173  // A Dictionary holds a collection of Namers for a single language. One can
   174  // reduce the amount of data linked in to a binary by only referencing
   175  // Dictionaries for the languages one needs to support instead of using the
   176  // generic Namer factories.
   177  type Dictionary struct {
   178  	parent *Dictionary
   179  	lang   header
   180  	script header
   181  	region header
   182  }
   183  
   184  // Tags returns a Namer for giving a full description of a tag. The names of
   185  // scripts and regions that are not already implied by the language name will
   186  // in appended within parentheses. It returns nil if there is not data for the
   187  // given tag. The type passed to Name must be a tag.
   188  func (d *Dictionary) Tags() Namer {
   189  	return dictTags{d}
   190  }
   191  
   192  type dictTags struct {
   193  	d *Dictionary
   194  }
   195  
   196  // Name implements the Namer interface for tag names.
   197  func (n dictTags) Name(x interface{}) string {
   198  	return nameTag(dictLanguages{n.d}, dictScripts{n.d}, dictRegions{n.d}, x)
   199  }
   200  
   201  // Languages returns a Namer for naming languages. It returns nil if there is no
   202  // data for the given tag. The type passed to Name must be either language.Base
   203  // or language.Tag. Note that the result may differ between passing a tag or its
   204  // base language. For example, for English, passing "nl-BE" would return Flemish
   205  // whereas passing "nl" returns "Dutch".
   206  func (d *Dictionary) Languages() Namer {
   207  	return dictLanguages{d}
   208  }
   209  
   210  type dictLanguages struct {
   211  	d *Dictionary
   212  }
   213  
   214  func (n dictLanguages) name(i int) string {
   215  	for d := n.d; d != nil; d = d.parent {
   216  		if s := d.lang.name(i); s != "" {
   217  			return s
   218  		}
   219  	}
   220  	return ""
   221  }
   222  
   223  // Name implements the Namer interface for language names.
   224  func (n dictLanguages) Name(x interface{}) string {
   225  	return nameLanguage(n, x)
   226  }
   227  
   228  // Scripts returns a Namer for naming scripts. It returns nil if there is no
   229  // data for the given tag. The type passed to Name must be either a
   230  // language.Script or a language.Tag. It will not attempt to infer a script for
   231  // tags with an unspecified script.
   232  func (d *Dictionary) Scripts() Namer {
   233  	return dictScripts{d}
   234  }
   235  
   236  type dictScripts struct {
   237  	d *Dictionary
   238  }
   239  
   240  func (n dictScripts) name(i int) string {
   241  	for d := n.d; d != nil; d = d.parent {
   242  		if s := d.script.name(i); s != "" {
   243  			return s
   244  		}
   245  	}
   246  	return ""
   247  }
   248  
   249  // Name implements the Namer interface for script names.
   250  func (n dictScripts) Name(x interface{}) string {
   251  	return nameScript(n, x)
   252  }
   253  
   254  // Regions returns a Namer for naming regions. It returns nil if there is no
   255  // data for the given tag. The type passed to Name must be either a
   256  // language.Region or a language.Tag. It will not attempt to infer a region for
   257  // tags with an unspecified region.
   258  func (d *Dictionary) Regions() Namer {
   259  	return dictRegions{d}
   260  }
   261  
   262  type dictRegions struct {
   263  	d *Dictionary
   264  }
   265  
   266  func (n dictRegions) name(i int) string {
   267  	for d := n.d; d != nil; d = d.parent {
   268  		if s := d.region.name(i); s != "" {
   269  			return s
   270  		}
   271  	}
   272  	return ""
   273  }
   274  
   275  // Name implements the Namer interface for region names.
   276  func (n dictRegions) Name(x interface{}) string {
   277  	return nameRegion(n, x)
   278  }
   279  
   280  // A SelfNamer implements a Namer that returns the name of language in this same
   281  // language. It provides a very compact mechanism to provide a comprehensive
   282  // list of languages to users in their native language.
   283  type SelfNamer struct {
   284  	// Supported defines the values supported by this Namer.
   285  	Supported language.Coverage
   286  }
   287  
   288  var (
   289  	// Self is a shared instance of a SelfNamer.
   290  	Self *SelfNamer = &self
   291  
   292  	self = SelfNamer{language.NewCoverage(selfTagSet.Tags)}
   293  )
   294  
   295  // Name returns the name of a given language tag in the language identified by
   296  // this tag. It supports both the language.Base and language.Tag types.
   297  func (n SelfNamer) Name(x interface{}) string {
   298  	t, _ := language.All.Compose(x)
   299  	base, scr, reg := t.Raw()
   300  	baseScript := language.Script{}
   301  	if (scr == language.Script{} && reg != language.Region{}) {
   302  		// For looking up in the self dictionary, we need to select the
   303  		// maximized script. This is even the case if the script isn't
   304  		// specified.
   305  		s1, _ := t.Script()
   306  		if baseScript = getScript(base); baseScript != s1 {
   307  			scr = s1
   308  		}
   309  	}
   310  
   311  	i, scr, reg := selfTagSet.index(base, scr, reg)
   312  	if i == -1 {
   313  		return ""
   314  	}
   315  
   316  	// Only return the display name if the script matches the expected script.
   317  	if (scr != language.Script{}) {
   318  		if (baseScript == language.Script{}) {
   319  			baseScript = getScript(base)
   320  		}
   321  		if baseScript != scr {
   322  			return ""
   323  		}
   324  	}
   325  
   326  	return selfHeaders[0].name(i)
   327  }
   328  
   329  // getScript returns the maximized script for a base language.
   330  func getScript(b language.Base) language.Script {
   331  	tag, _ := language.Raw.Compose(b)
   332  	scr, _ := tag.Script()
   333  	return scr
   334  }