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 }