github.com/insionng/yougam@v0.0.0-20170714101924-2bc18d833463/libraries/golang/text/language/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  //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 "github.com/insionng/yougam/libraries/x/text/language/display"
    16  
    17  import (
    18  	"strings"
    19  
    20  	"github.com/insionng/yougam/libraries/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  }