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 }