...

Source file src/golang.org/x/text/language/display/display.go

Documentation: golang.org/x/text/language/display

     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  	"fmt"
    19  	"strings"
    20  
    21  	"golang.org/x/text/internal/format"
    22  	"golang.org/x/text/language"
    23  )
    24  
    25  /*
    26  TODO:
    27  All 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.
    39  type 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.
    45  func (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.
    68  func 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.
    76  func 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.
    84  func Script(script interface{}) Formatter {
    85  	return Formatter{scriptFunc, script}
    86  }
    87  
    88  // Tag 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.
    92  func 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.
    98  type 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  
   108  var (
   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  
   120  func 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".
   139  func 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  
   146  type languageNamer int
   147  
   148  func langFunc(i int, x interface{}) string {
   149  	return nameLanguage(languageNamer(i), x)
   150  }
   151  
   152  func (n languageNamer) name(i int) string {
   153  	return lookup(langHeaders[:], int(n), i)
   154  }
   155  
   156  // Name implements the Namer interface for language names.
   157  func (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.
   163  func 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.
   173  func 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  
   182  type scriptNamer int
   183  
   184  func scriptFunc(i int, x interface{}) string {
   185  	return nameScript(scriptNamer(i), x)
   186  }
   187  
   188  func (n scriptNamer) name(i int) string {
   189  	return lookup(scriptHeaders[:], int(n), i)
   190  }
   191  
   192  // Name implements the Namer interface for script names.
   193  func (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.
   201  func 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  
   210  type regionNamer int
   211  
   212  func regionFunc(i int, x interface{}) string {
   213  	return nameRegion(regionNamer(i), x)
   214  }
   215  
   216  func (n regionNamer) name(i int) string {
   217  	return lookup(regionHeaders[:], int(n), i)
   218  }
   219  
   220  // Name implements the Namer interface for region names.
   221  func (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.
   229  func 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  
   236  type tagNamer int
   237  
   238  func 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.
   243  func (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.
   249  func 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.
   263  type 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.
   274  func (d *Dictionary) Tags() Namer {
   275  	return dictTags{d}
   276  }
   277  
   278  type dictTags struct {
   279  	d *Dictionary
   280  }
   281  
   282  // Name implements the Namer interface for tag names.
   283  func (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".
   292  func (d *Dictionary) Languages() Namer {
   293  	return dictLanguages{d}
   294  }
   295  
   296  type dictLanguages struct {
   297  	d *Dictionary
   298  }
   299  
   300  func (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.
   310  func (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.
   318  func (d *Dictionary) Scripts() Namer {
   319  	return dictScripts{d}
   320  }
   321  
   322  type dictScripts struct {
   323  	d *Dictionary
   324  }
   325  
   326  func (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.
   336  func (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.
   344  func (d *Dictionary) Regions() Namer {
   345  	return dictRegions{d}
   346  }
   347  
   348  type dictRegions struct {
   349  	d *Dictionary
   350  }
   351  
   352  func (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.
   362  func (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.
   369  type SelfNamer struct {
   370  	// Supported defines the values supported by this Namer.
   371  	Supported language.Coverage
   372  }
   373  
   374  var (
   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.
   383  func (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.
   416  func getScript(b language.Base) language.Script {
   417  	tag, _ := language.Raw.Compose(b)
   418  	scr, _ := tag.Script()
   419  	return scr
   420  }
   421  

View as plain text