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