1 /* 2 * Hunt - A refined core library for D programming language. 3 * 4 * Copyright (C) 2018-2019 HuntLabs 5 * 6 * Website: https://www.huntlabs.net/ 7 * 8 * Licensed under the Apache-2.0 License. 9 * 10 */ 11 12 module hunt.util.Locale; 13 14 import hunt.Exceptions; 15 import hunt.Functions; 16 import hunt.stream.Common; 17 import hunt.logging.ConsoleLogger; 18 import hunt.collection; 19 import hunt.system.Environment; 20 import hunt.util.StringBuilder; 21 import hunt.util.Common; 22 import hunt.util.Configuration; 23 24 import std.array; 25 import std.concurrency : initOnce; 26 import std.string; 27 28 /** 29 * A <code>Locale</code> object represents a specific geographical, political, 30 * or cultural region. An operation that requires a <code>Locale</code> to perform 31 * its task is called <em>locale-sensitive</em> and uses the <code>Locale</code> 32 * to tailor information for the user. For example, displaying a number 33 * is a locale-sensitive operation— the number should be formatted 34 * according to the customs and conventions of the user's native country, 35 * region, or culture. 36 * 37 * <p> The {@code Locale} class implements IETF BCP 47 which is composed of 38 * <a href="http://tools.ietf.org/html/rfc4647">RFC 4647 "Matching of Language 39 * Tags"</a> and <a href="http://tools.ietf.org/html/rfc5646">RFC 5646 "Tags 40 * for Identifying Languages"</a> with support for the LDML (UTS#35, "Unicode 41 * Locale Data Markup Language") BCP 47-compatible extensions for locale data 42 * exchange. 43 * 44 * <p> A <code>Locale</code> object logically consists of the fields 45 * described below. 46 * 47 * <dl> 48 * <dt><a id="def_language"><b>language</b></a></dt> 49 * 50 * <dd>ISO 639 alpha-2 or alpha-3 language code, or registered 51 * language subtags up to 8 alpha letters (for future enhancements). 52 * When a language has both an alpha-2 code and an alpha-3 code, the 53 * alpha-2 code must be used. You can find a full list of valid 54 * language codes in the IANA Language Subtag Registry (search for 55 * "Type: language"). The language field is case insensitive, but 56 * <code>Locale</code> always canonicalizes to lower case.</dd> 57 * 58 * <dd>Well-formed language values have the form 59 * <code>[a-zA-Z]{2,8}</code>. Note that this is not the full 60 * BCP47 language production, since it excludes extlang. They are 61 * not needed since modern three-letter language codes replace 62 * them.</dd> 63 * 64 * <dd>Example: "en" (English), "ja" (Japanese), "kok" (Konkani)</dd> 65 * 66 * <dt><a id="def_script"><b>script</b></a></dt> 67 * 68 * <dd>ISO 15924 alpha-4 script code. You can find a full list of 69 * valid script codes in the IANA Language Subtag Registry (search 70 * for "Type: script"). The script field is case insensitive, but 71 * <code>Locale</code> always canonicalizes to title case (the first 72 * letter is upper case and the rest of the letters are lower 73 * case).</dd> 74 * 75 * <dd>Well-formed script values have the form 76 * <code>[a-zA-Z]{4}</code></dd> 77 * 78 * <dd>Example: "Latn" (Latin), "Cyrl" (Cyrillic)</dd> 79 * 80 * <dt><a id="def_region"><b>country (region)</b></a></dt> 81 * 82 * <dd>ISO 3166 alpha-2 country code or UN M.49 numeric-3 area code. 83 * You can find a full list of valid country and region codes in the 84 * IANA Language Subtag Registry (search for "Type: region"). The 85 * country (region) field is case insensitive, but 86 * <code>Locale</code> always canonicalizes to upper case.</dd> 87 * 88 * <dd>Well-formed country/region values have 89 * the form <code>[a-zA-Z]{2} | [0-9]{3}</code></dd> 90 * 91 * <dd>Example: "US" (United States), "FR" (France), "029" 92 * (Caribbean)</dd> 93 * 94 * <dt><a id="def_variant"><b>variant</b></a></dt> 95 * 96 * <dd>Any arbitrary value used to indicate a variation of a 97 * <code>Locale</code>. Where there are two or more variant values 98 * each indicating its own semantics, these values should be ordered 99 * by importance, with most important first, separated by 100 * underscore('_'). The variant field is case sensitive.</dd> 101 * 102 * <dd>Note: IETF BCP 47 places syntactic restrictions on variant 103 * subtags. Also BCP 47 subtags are strictly used to indicate 104 * additional variations that define a language or its dialects that 105 * are not covered by any combinations of language, script and 106 * region subtags. You can find a full list of valid variant codes 107 * in the IANA Language Subtag Registry (search for "Type: variant"). 108 * 109 * <p>However, the variant field in <code>Locale</code> has 110 * historically been used for any kind of variation, not just 111 * language variations. For example, some supported variants 112 * available in Java SE Runtime Environments indicate alternative 113 * cultural behaviors such as calendar type or number script. In 114 * BCP 47 this kind of information, which does not identify the 115 * language, is supported by extension subtags or private use 116 * subtags.</dd> 117 * 118 * <dd>Well-formed variant values have the form <code>SUBTAG 119 * (('_'|'-') SUBTAG)*</code> where <code>SUBTAG = 120 * [0-9][0-9a-zA-Z]{3} | [0-9a-zA-Z]{5,8}</code>. (Note: BCP 47 only 121 * uses hyphen ('-') as a delimiter, this is more lenient).</dd> 122 * 123 * <dd>Example: "polyton" (Polytonic Greek), "POSIX"</dd> 124 * 125 * <dt><a id="def_extensions"><b>extensions</b></a></dt> 126 * 127 * <dd>A map from single character keys to string values, indicating 128 * extensions apart from language identification. The extensions in 129 * <code>Locale</code> implement the semantics and syntax of BCP 47 130 * extension subtags and private use subtags. The extensions are 131 * case insensitive, but <code>Locale</code> canonicalizes all 132 * extension keys and values to lower case. Note that extensions 133 * cannot have empty values.</dd> 134 * 135 * <dd>Well-formed keys are single characters from the set 136 * <code>[0-9a-zA-Z]</code>. Well-formed values have the form 137 * <code>SUBTAG ('-' SUBTAG)*</code> where for the key 'x' 138 * <code>SUBTAG = [0-9a-zA-Z]{1,8}</code> and for other keys 139 * <code>SUBTAG = [0-9a-zA-Z]{2,8}</code> (that is, 'x' allows 140 * single-character subtags).</dd> 141 * 142 * <dd>Example: key="u"/value="ca-japanese" (Japanese Calendar), 143 * key="x"/value="java-1-7"</dd> 144 * </dl> 145 * 146 * <b>Note:</b> Although BCP 47 requires field values to be registered 147 * in the IANA Language Subtag Registry, the <code>Locale</code> class 148 * does not provide any validation features. The <code>Builder</code> 149 * only checks if an individual field satisfies the syntactic 150 * requirement (is well-formed), but does not validate the value 151 * itself. See {@link Builder} for details. 152 * 153 * <h3><a id="def_locale_extension">Unicode locale/language extension</a></h3> 154 * 155 * <p>UTS#35, "Unicode Locale Data Markup Language" defines optional 156 * attributes and keywords to override or refine the default behavior 157 * associated with a locale. A keyword is represented by a pair of 158 * key and type. For example, "nu-thai" indicates that Thai local 159 * digits (value:"thai") should be used for formatting numbers 160 * (key:"nu"). 161 * 162 * <p>The keywords are mapped to a BCP 47 extension value using the 163 * extension key 'u' ({@link #UNICODE_LOCALE_EXTENSION}). The above 164 * example, "nu-thai", becomes the extension "u-nu-thai". 165 * 166 * <p>Thus, when a <code>Locale</code> object contains Unicode locale 167 * attributes and keywords, 168 * <code>getExtension(UNICODE_LOCALE_EXTENSION)</code> will return a 169 * string representing this information, for example, "nu-thai". The 170 * <code>Locale</code> class also provides {@link 171 * #getUnicodeLocaleAttributes}, {@link #getUnicodeLocaleKeys}, and 172 * {@link #getUnicodeLocaleType} which allow you to access Unicode 173 * locale attributes and key/type pairs directly. When represented as 174 * a string, the Unicode Locale Extension lists attributes 175 * alphabetically, followed by key/type sequences with keys listed 176 * alphabetically (the order of subtags comprising a key's type is 177 * fixed when the type is defined) 178 * 179 * <p>A well-formed locale key has the form 180 * <code>[0-9a-zA-Z]{2}</code>. A well-formed locale type has the 181 * form <code>"" | [0-9a-zA-Z]{3,8} ('-' [0-9a-zA-Z]{3,8})*</code> (it 182 * can be empty, or a series of subtags 3-8 alphanums in length). A 183 * well-formed locale attribute has the form 184 * <code>[0-9a-zA-Z]{3,8}</code> (it is a single subtag with the same 185 * form as a locale type subtag). 186 * 187 * <p>The Unicode locale extension specifies optional behavior in 188 * locale-sensitive services. Although the LDML specification defines 189 * various keys and values, actual locale-sensitive service 190 * implementations in a Java Runtime Environment might not support any 191 * particular Unicode locale attributes or key/type pairs. 192 * 193 * <h4>Creating a Locale</h4> 194 * 195 * <p>There are several different ways to create a <code>Locale</code> 196 * object. 197 * 198 * <h5>Builder</h5> 199 * 200 * <p>Using {@link Builder} you can construct a <code>Locale</code> object 201 * that conforms to BCP 47 syntax. 202 * 203 * <h5>Constructors</h5> 204 * 205 * <p>The <code>Locale</code> class provides three constructors: 206 * <blockquote> 207 * <pre> 208 * {@link #Locale(string language)} 209 * {@link #Locale(string language, string country)} 210 * {@link #Locale(string language, string country, string variant)} 211 * </pre> 212 * </blockquote> 213 * These constructors allow you to create a <code>Locale</code> object 214 * with language, country and variant, but you cannot specify 215 * script or extensions. 216 * 217 * <h5>Factory Methods</h5> 218 * 219 * <p>The method {@link #forLanguageTag} creates a <code>Locale</code> 220 * object for a well-formed BCP 47 language tag. 221 * 222 * <h5>Locale Constants</h5> 223 * 224 * <p>The <code>Locale</code> class provides a number of convenient constants 225 * that you can use to create <code>Locale</code> objects for commonly used 226 * locales. For example, the following creates a <code>Locale</code> object 227 * for the United States: 228 * <blockquote> 229 * <pre> 230 * Locale.US 231 * </pre> 232 * </blockquote> 233 * 234 * <h4><a id="LocaleMatching">Locale Matching</a></h4> 235 * 236 * <p>If an application or a system is internationalized and provides localized 237 * resources for multiple locales, it sometimes needs to find one or more 238 * locales (or language tags) which meet each user's specific preferences. Note 239 * that a term "language tag" is used interchangeably with "locale" in this 240 * locale matching documentation. 241 * 242 * <p>In order to do matching a user's preferred locales to a set of language 243 * tags, <a href="http://tools.ietf.org/html/rfc4647">RFC 4647 Matching of 244 * Language Tags</a> defines two mechanisms: filtering and lookup. 245 * <em>Filtering</em> is used to get all matching locales, whereas 246 * <em>lookup</em> is to choose the best matching locale. 247 * Matching is done case-insensitively. These matching mechanisms are described 248 * in the following sections. 249 * 250 * <p>A user's preference is called a <em>Language Priority List</em> and is 251 * expressed as a list of language ranges. There are syntactically two types of 252 * language ranges: basic and extended. See 253 * {@link Locale.LanguageRange Locale.LanguageRange} for details. 254 * 255 * <h5>Filtering</h5> 256 * 257 * <p>The filtering operation returns all matching language tags. It is defined 258 * in RFC 4647 as follows: 259 * "In filtering, each language range represents the least specific language 260 * tag (that is, the language tag with fewest number of subtags) that is an 261 * acceptable match. All of the language tags in the matching set of tags will 262 * have an equal or greater number of subtags than the language range. Every 263 * non-wildcard subtag in the language range will appear in every one of the 264 * matching language tags." 265 * 266 * <p>There are two types of filtering: filtering for basic language ranges 267 * (called "basic filtering") and filtering for extended language ranges 268 * (called "extended filtering"). They may return different results by what 269 * kind of language ranges are included in the given Language Priority List. 270 * {@link Locale.FilteringMode} is a parameter to specify how filtering should 271 * be done. 272 * 273 * <h5>Lookup</h5> 274 * 275 * <p>The lookup operation returns the best matching language tags. It is 276 * defined in RFC 4647 as follows: 277 * "By contrast with filtering, each language range represents the most 278 * specific tag that is an acceptable match. The first matching tag found, 279 * according to the user's priority, is considered the closest match and is the 280 * item returned." 281 * 282 * <p>For example, if a Language Priority List consists of two language ranges, 283 * {@code "zh-Hant-TW"} and {@code "en-US"}, in prioritized order, lookup 284 * method progressively searches the language tags below in order to find the 285 * best matching language tag. 286 * <blockquote> 287 * <pre> 288 * 1. zh-Hant-TW 289 * 2. zh-Hant 290 * 3. zh 291 * 4. en-US 292 * 5. en 293 * </pre> 294 * </blockquote> 295 * If there is a language tag which matches completely to a language range 296 * above, the language tag is returned. 297 * 298 * <p>{@code "*"} is the special language range, and it is ignored in lookup. 299 * 300 * <p>If multiple language tags match as a result of the subtag {@code '*'} 301 * included in a language range, the first matching language tag returned by 302 * an {@link Iterator} over a {@link Collection} of language tags is treated as 303 * the best matching one. 304 * 305 * <h4>Use of Locale</h4> 306 * 307 * <p>Once you've created a <code>Locale</code> you can query it for information 308 * about itself. Use <code>getCountry</code> to get the country (or region) 309 * code and <code>getLanguage</code> to get the language code. 310 * You can use <code>getDisplayCountry</code> to get the 311 * name of the country suitable for displaying to the user. Similarly, 312 * you can use <code>getDisplayLanguage</code> to get the name of 313 * the language suitable for displaying to the user. Interestingly, 314 * the <code>getDisplayXXX</code> methods are themselves locale-sensitive 315 * and have two versions: one that uses the default 316 * {@link LocaleCategory#DISPLAY DISPLAY} locale and one 317 * that uses the locale specified as an argument. 318 * 319 * <p>The Java Platform provides a number of classes that perform locale-sensitive 320 * operations. For example, the <code>NumberFormat</code> class formats 321 * numbers, currency, and percentages in a locale-sensitive manner. Classes 322 * such as <code>NumberFormat</code> have several convenience methods 323 * for creating a default object of that type. For example, the 324 * <code>NumberFormat</code> class provides these three convenience methods 325 * for creating a default <code>NumberFormat</code> object: 326 * <blockquote> 327 * <pre> 328 * NumberFormat.getInstance() 329 * NumberFormat.getCurrencyInstance() 330 * NumberFormat.getPercentInstance() 331 * </pre> 332 * </blockquote> 333 * Each of these methods has two variants; one with an explicit locale 334 * and one without; the latter uses the default 335 * {@link LocaleCategory#FORMAT FORMAT} locale: 336 * <blockquote> 337 * <pre> 338 * NumberFormat.getInstance(myLocale) 339 * NumberFormat.getCurrencyInstance(myLocale) 340 * NumberFormat.getPercentInstance(myLocale) 341 * </pre> 342 * </blockquote> 343 * A <code>Locale</code> is the mechanism for identifying the kind of object 344 * (<code>NumberFormat</code>) that you would like to get. The locale is 345 * <STRONG>just</STRONG> a mechanism for identifying objects, 346 * <STRONG>not</STRONG> a container for the objects themselves. 347 * 348 * <h4>Compatibility</h4> 349 * 350 * <p>In order to maintain compatibility with existing usage, Locale's 351 * constructors retain their behavior prior to the Java Runtime 352 * Environment version 1.7. The same is largely true for the 353 * <code>toString</code> method. Thus Locale objects can continue to 354 * be used as they were. In particular, clients who parse the output 355 * of toString into language, country, and variant fields can continue 356 * to do so (although this is strongly discouraged), although the 357 * variant field will have additional information in it if script or 358 * extensions are present. 359 * 360 * <p>In addition, BCP 47 imposes syntax restrictions that are not 361 * imposed by Locale's constructors. This means that conversions 362 * between some Locales and BCP 47 language tags cannot be made without 363 * losing information. Thus <code>toLanguageTag</code> cannot 364 * represent the state of locales whose language, country, or variant 365 * do not conform to BCP 47. 366 * 367 * <p>Because of these issues, it is recommended that clients migrate 368 * away from constructing non-conforming locales and use the 369 * <code>forLanguageTag</code> and <code>Locale.Builder</code> APIs instead. 370 * Clients desiring a string representation of the complete locale can 371 * then always rely on <code>toLanguageTag</code> for this purpose. 372 * 373 * <h5><a id="special_cases_constructor">Special cases</a></h5> 374 * 375 * <p>For compatibility reasons, two 376 * non-conforming locales are treated as special cases. These are 377 * <b>{@code ja_JP_JP}</b> and <b>{@code th_TH_TH}</b>. These are ill-formed 378 * in BCP 47 since the variants are too short. To ease migration to BCP 47, 379 * these are treated specially during construction. These two cases (and only 380 * these) cause a constructor to generate an extension, all other values behave 381 * exactly as they did prior to Java 7. 382 * 383 * <p>Java has used {@code ja_JP_JP} to represent Japanese as used in 384 * Japan together with the Japanese Imperial calendar. This is now 385 * representable using a Unicode locale extension, by specifying the 386 * Unicode locale key {@code ca} (for "calendar") and type 387 * {@code japanese}. When the Locale constructor is called with the 388 * arguments "ja", "JP", "JP", the extension "u-ca-japanese" is 389 * automatically added. 390 * 391 * <p>Java has used {@code th_TH_TH} to represent Thai as used in 392 * Thailand together with Thai digits. This is also now representable using 393 * a Unicode locale extension, by specifying the Unicode locale key 394 * {@code nu} (for "number") and value {@code thai}. When the Locale 395 * constructor is called with the arguments "th", "TH", "TH", the 396 * extension "u-nu-thai" is automatically added. 397 * 398 * <h5>Serialization</h5> 399 * 400 * <p>During serialization, writeObject writes all fields to the output 401 * stream, including extensions. 402 * 403 * <p>During deserialization, readResolve adds extensions as described 404 * in <a href="#special_cases_constructor">Special Cases</a>, only 405 * for the two cases th_TH_TH and ja_JP_JP. 406 * 407 * <h5>Legacy language codes</h5> 408 * 409 * <p>Locale's constructor has always converted three language codes to 410 * their earlier, obsoleted forms: {@code he} maps to {@code iw}, 411 * {@code yi} maps to {@code ji}, and {@code id} maps to 412 * {@code in}. This continues to be the case, in order to not break 413 * backwards compatibility. 414 * 415 * <p>The APIs added in 1.7 map between the old and new language codes, 416 * maintaining the old codes internal to Locale (so that 417 * <code>getLanguage</code> and <code>toString</code> reflect the old 418 * code), but using the new codes in the BCP 47 language tag APIs (so 419 * that <code>toLanguageTag</code> reflects the new one). This 420 * preserves the equivalence between Locales no matter which code or 421 * API is used to construct them. Java's default resource bundle 422 * lookup mechanism also implements this mapping, so that resources 423 * can be named using either convention, see {@link ResourceBundle.Control}. 424 * 425 * <h5>Three-letter language/country(region) codes</h5> 426 * 427 * <p>The Locale constructors have always specified that the language 428 * and the country param be two characters in length, although in 429 * practice they have accepted any length. The specification has now 430 * been relaxed to allow language codes of two to eight characters and 431 * country (region) codes of two to three characters, and in 432 * particular, three-letter language codes and three-digit region 433 * codes as specified in the IANA Language Subtag Registry. For 434 * compatibility, the implementation still does not impose a length 435 * constraint. 436 * 437 * @see Builder 438 * @see ResourceBundle 439 * @see java.text.Format 440 * @see java.text.NumberFormat 441 * @see java.text.Collator 442 * @author Mark Davis 443 */ 444 final class Locale // : Cloneable 445 { 446 447 private __gshared Locale[size_t] LOCALECACHE; 448 449 /** Useful constant for language. 450 */ 451 static Locale ENGLISH() { 452 __gshared Locale m; 453 return initOnce!(m)(createConstant("en", "")); 454 } 455 456 /** Useful constant for language. 457 */ 458 static Locale FRENCH() { 459 __gshared Locale m; 460 return initOnce!(m)(createConstant("fr", "")); 461 } 462 463 /** Useful constant for language. 464 */ 465 static Locale GERMAN() { 466 __gshared Locale m; 467 return initOnce!(m)(createConstant("de", "")); 468 } 469 470 /** Useful constant for language. 471 */ 472 static Locale ITALIAN() { 473 __gshared Locale m; 474 return initOnce!(m)(createConstant("it", "")); 475 } 476 477 /** Useful constant for language. 478 */ 479 static Locale JAPANESE() { 480 __gshared Locale m; 481 return initOnce!(m)(createConstant("ja", "")); 482 } 483 484 /** Useful constant for language. 485 */ 486 static Locale KOREAN() { 487 __gshared Locale m; 488 return initOnce!(m)(createConstant("ko", "")); 489 } 490 491 /** Useful constant for language. 492 */ 493 static Locale CHINESE() { 494 __gshared Locale m; 495 return initOnce!(m)(createConstant("zh", "")); 496 } 497 498 /** Useful constant for language. 499 */ 500 static Locale SIMPLIFIED_CHINESE() { 501 __gshared Locale m; 502 return initOnce!(m)(createConstant("zh", "CN")); 503 } 504 505 /** Useful constant for language. 506 */ 507 static Locale TRADITIONAL_CHINESE() { 508 __gshared Locale m; 509 return initOnce!(m)(createConstant("zh", "TW")); 510 } 511 512 /** Useful constant for country. 513 */ 514 static Locale FRANCE() { 515 __gshared Locale m; 516 return initOnce!(m)(createConstant("fr", "FR")); 517 } 518 519 /** Useful constant for country. 520 */ 521 static Locale GERMANY() { 522 __gshared Locale m; 523 return initOnce!(m)(createConstant("de", "DE")); 524 } 525 526 /** Useful constant for country. 527 */ 528 static Locale ITALY() { 529 __gshared Locale m; 530 return initOnce!(m)(createConstant("it", "IT")); 531 } 532 533 /** Useful constant for country. 534 */ 535 static Locale JAPAN() { 536 __gshared Locale m; 537 return initOnce!(m)(createConstant("ja", "JP")); 538 } 539 540 /** Useful constant for country. 541 */ 542 static Locale KOREA() { 543 __gshared Locale m; 544 return initOnce!(m)(createConstant("ko", "KR")); 545 } 546 547 /** Useful constant for country. 548 */ 549 550 alias CHINA = SIMPLIFIED_CHINESE; 551 552 /** Useful constant for country. 553 */ 554 alias PRC = SIMPLIFIED_CHINESE; 555 556 /** Useful constant for country. 557 */ 558 alias TAIWAN = TRADITIONAL_CHINESE; 559 560 561 /** Useful constant for country. 562 */ 563 static Locale UK() { 564 __gshared Locale m; 565 return initOnce!(m)(createConstant("en", "GB")); 566 } 567 568 /** Useful constant for country. 569 */ 570 static Locale US() { 571 __gshared Locale m; 572 return initOnce!(m)(createConstant("en", "US")); 573 } 574 575 /** Useful constant for country. 576 */ 577 static Locale CANADA() { 578 __gshared Locale m; 579 return initOnce!(m)(createConstant("en", "CA")); 580 } 581 582 /** Useful constant for country. 583 */ 584 static Locale CANADA_FRENCH() { 585 __gshared Locale m; 586 return initOnce!(m)(createConstant("fr", "CA")); 587 } 588 589 /** 590 * Useful constant for the root locale. The root locale is the locale whose 591 * language, country, and variant are empty ("") strings. This is regarded 592 * as the base locale of all locales, and is used as the language/country 593 * neutral locale for the locale sensitive operations. 594 * 595 */ 596 static Locale ROOT() { 597 __gshared Locale m; 598 return initOnce!(m)(createConstant("", "")); 599 } 600 601 /** 602 * The key for the private use extension ('x'). 603 * 604 * @see #getExtension(char) 605 * @see Builder#setExtension(char, string) 606 */ 607 enum char PRIVATE_USE_EXTENSION = 'x'; 608 609 /** 610 * The key for Unicode locale extension ('u'). 611 * 612 * @see #getExtension(char) 613 * @see Builder#setExtension(char, string) 614 */ 615 enum char UNICODE_LOCALE_EXTENSION = 'u'; 616 617 618 /** 619 * Enum for specifying the type defined in ISO 3166. This enum is used to 620 * retrieve the two-letter ISO3166-1 alpha-2, three-letter ISO3166-1 621 * alpha-3, four-letter ISO3166-3 country codes. 622 * 623 * @see #getISOCountries(Locale.IsoCountryCode) 624 */ 625 static class IsoCountryCode 626 { 627 /** 628 * PART1_ALPHA2 is used to represent the ISO3166-1 alpha-2 two letter 629 * country codes. 630 */ 631 __gshared IsoCountryCode PART1_ALPHA2; 632 633 shared static this() 634 { 635 PART1_ALPHA2 = new class IsoCountryCode 636 { 637 override Set!(string) createCountryCodeSet() 638 { 639 implementationMissing(); 640 return null; 641 // return Set.of(Locale.getISOCountries()); 642 } 643 }; 644 PART1_ALPHA3 = new class IsoCountryCode 645 { 646 override Set!(string) createCountryCodeSet() 647 { 648 implementationMissing(); 649 return null; 650 // return LocaleISOData.computeISO3166_1Alpha3Countries(); 651 } 652 }; 653 654 PART3 = new class IsoCountryCode 655 { 656 override Set!(string) createCountryCodeSet() 657 { 658 implementationMissing(); 659 return null; 660 // return Set.of(LocaleISOData.ISO3166_3); 661 } 662 }; 663 } 664 665 /** 666 * 667 * PART1_ALPHA3 is used to represent the ISO3166-1 alpha-3 three letter 668 * country codes. 669 */ 670 __gshared IsoCountryCode PART1_ALPHA3; 671 672 /** 673 * PART3 is used to represent the ISO3166-3 four letter country codes. 674 */ 675 __gshared IsoCountryCode PART3; 676 677 /** 678 * Concrete implementation of this method attempts to compute value 679 * for iso3166CodesMap for each IsoCountryCode type key. 680 */ 681 abstract Set!(string) createCountryCodeSet(); 682 683 /** 684 * Map to hold country codes for each ISO3166 part. 685 */ 686 private __gshared Map!(IsoCountryCode, Set!(string)) iso3166CodesMap; 687 688 shared static this() 689 { 690 iso3166CodesMap = new HashMap!(IsoCountryCode, Set!(string))(); 691 } 692 693 /** 694 * This method is called from Locale class to retrieve country code set 695 * for getISOCountries(type) 696 */ 697 static Set!(string) retrieveISOCountryCodes(IsoCountryCode type) 698 { 699 implementationMissing(); 700 return null; 701 // return iso3166CodesMap.computeIfAbsent(type, IsoCountryCode.createCountryCodeSet); 702 } 703 } 704 705 /** 706 * Display types for retrieving localized names from the name providers. 707 */ 708 private enum int DISPLAY_LANGUAGE = 0; 709 private enum int DISPLAY_COUNTRY = 1; 710 private enum int DISPLAY_VARIANT = 2; 711 private enum int DISPLAY_SCRIPT = 3; 712 private enum int DISPLAY_UEXT_KEY = 4; 713 private enum int DISPLAY_UEXT_TYPE = 5; 714 715 private string language; 716 private string script; 717 private string region; 718 private string variant; 719 720 /** 721 * Private constructor used by getInstance method 722 */ 723 // private this(BaseLocale baseLocale, LocaleExtensions extensions) { 724 // this.baseLocale = baseLocale; 725 // this.localeExtensions = extensions; 726 // } 727 728 /** 729 * Construct a locale from language, country and variant. 730 * This constructor normalizes the language value to lowercase and 731 * the country value to uppercase. 732 * <p> 733 * <b>Note:</b> 734 * <ul> 735 * <li>ISO 639 is not a stable standard; some of the language codes it defines 736 * (specifically "iw", "ji", and "in") have changed. This constructor accepts both the 737 * old codes ("iw", "ji", and "in") and the new codes ("he", "yi", and "id"), but all other 738 * API on Locale will return only the OLD codes. 739 * <li>For backward compatibility reasons, this constructor does not make 740 * any syntactic checks on the input. 741 * <li>The two cases ("ja", "JP", "JP") and ("th", "TH", "TH") are handled specially, 742 * see <a href="#special_cases_constructor">Special Cases</a> for more information. 743 * </ul> 744 * 745 * @param language An ISO 639 alpha-2 or alpha-3 language code, or a language subtag 746 * up to 8 characters in length. See the <code>Locale</code> class description about 747 * valid language values. 748 * @param country An ISO 3166 alpha-2 country code or a UN M.49 numeric-3 area code. 749 * See the <code>Locale</code> class description about valid country values. 750 * @param variant Any arbitrary value used to indicate a variation of a <code>Locale</code>. 751 * See the <code>Locale</code> class description for the details. 752 * @exception NullPointerException thrown if any argument is null. 753 */ 754 this(string language, string country, string variant) 755 { 756 if (language is null || country is null || variant is null) 757 { 758 throw new NullPointerException(); 759 } 760 761 this.language = convertOldISOCodes(language); 762 this.region = country; 763 this.variant = variant; 764 } 765 766 /** 767 * Construct a locale from language and country. 768 * This constructor normalizes the language value to lowercase and 769 * the country value to uppercase. 770 * <p> 771 * <b>Note:</b> 772 * <ul> 773 * <li>ISO 639 is not a stable standard; some of the language codes it defines 774 * (specifically "iw", "ji", and "in") have changed. This constructor accepts both the 775 * old codes ("iw", "ji", and "in") and the new codes ("he", "yi", and "id"), but all other 776 * API on Locale will return only the OLD codes. 777 * <li>For backward compatibility reasons, this constructor does not make 778 * any syntactic checks on the input. 779 * </ul> 780 * 781 * @param language An ISO 639 alpha-2 or alpha-3 language code, or a language subtag 782 * up to 8 characters in length. See the <code>Locale</code> class description about 783 * valid language values. 784 * @param country An ISO 3166 alpha-2 country code or a UN M.49 numeric-3 area code. 785 * See the <code>Locale</code> class description about valid country values. 786 * @exception NullPointerException thrown if either argument is null. 787 */ 788 this(string language, string country) 789 { 790 this(language, country, ""); 791 } 792 793 /** 794 * Construct a locale from a language code. 795 * This constructor normalizes the language value to lowercase. 796 * <p> 797 * <b>Note:</b> 798 * <ul> 799 * <li>ISO 639 is not a stable standard; some of the language codes it defines 800 * (specifically "iw", "ji", and "in") have changed. This constructor accepts both the 801 * old codes ("iw", "ji", and "in") and the new codes ("he", "yi", and "id"), but all other 802 * API on Locale will return only the OLD codes. 803 * <li>For backward compatibility reasons, this constructor does not make 804 * any syntactic checks on the input. 805 * </ul> 806 * 807 * @param language An ISO 639 alpha-2 or alpha-3 language code, or a language subtag 808 * up to 8 characters in length. See the <code>Locale</code> class description about 809 * valid language values. 810 * @exception NullPointerException thrown if argument is null. 811 */ 812 this(string language) 813 { 814 this(language, "", ""); 815 } 816 817 /** 818 * This method must be called only for creating the Locale.* 819 * constants due to making shortcuts. 820 */ 821 private static Locale createConstant(string lang, string country) 822 { 823 // BaseLocale base = BaseLocale.createInstance(lang, country); 824 // return getInstance(base, null); 825 Locale le = new Locale(lang, country); 826 size_t key = hashOf(lang, country) ; 827 LOCALECACHE[key] = le; 828 829 return le; 830 } 831 832 static size_t hashOf(string language, string country, string script = null, string variant = null) { 833 size_t r = 0; 834 if(language !is null) 835 r += .hashOf(language.toLower()); 836 if(country !is null) 837 r += .hashOf(country.toLower()); 838 if(script !is null) 839 r += .hashOf(script.toLower()); 840 if(variant !is null) 841 r += .hashOf(variant.toLower()); 842 return r; 843 } 844 845 /** 846 * Returns a <code>Locale</code> constructed from the given 847 * <code>language</code>, <code>country</code> and 848 * <code>variant</code>. If the same <code>Locale</code> instance 849 * is available in the cache, then that instance is 850 * returned. Otherwise, a new <code>Locale</code> instance is 851 * created and cached. 852 * 853 * @param language lowercase 2 to 8 language code. 854 * @param country uppercase two-letter ISO-3166 code and numeric-3 UN M.49 area code. 855 * @param variant vendor and browser specific code. See class description. 856 * @return the <code>Locale</code> instance requested 857 * @exception NullPointerException if any argument is null. 858 */ 859 static Locale getInstance(string language, string country, string variant) { 860 return getInstance(language, "", country, variant); 861 } 862 863 static Locale getInstance(string language, string script, string country, string variant) { 864 if (language is null || script is null || country is null || variant is null) { 865 throw new NullPointerException(); 866 } 867 868 version(HUNT_DEBUG) { 869 tracef("language=%s, script=%s, country=%s, variant=%s", 870 language, script, country, variant); 871 } 872 size_t key = hashOf(language, country, script, variant); 873 Locale le = LOCALECACHE.get(key, null); 874 if(le is null) { 875 le = new Locale(language, country, variant); 876 LOCALECACHE[key] = le; 877 } 878 return le; 879 } 880 881 /** 882 * Gets the current value of the default locale for this instance 883 * of the Java Virtual Machine. 884 * <p> 885 * The Java Virtual Machine sets the default locale during startup 886 * based on the host environment. It is used by many locale-sensitive 887 * methods if no locale is explicitly specified. 888 * It can be changed using the 889 * {@link #setDefault(java.util.Locale) setDefault} method. 890 * 891 * @return the default locale for this instance of the Java Virtual Machine 892 */ 893 static Locale getDefault() 894 { 895 return initOnce!(defaultLocale)(initDefault()); 896 // do not synchronize this method - see 4071298 897 } 898 private __gshared Locale defaultLocale; 899 900 /** 901 * Gets the current value of the default locale for the specified Category 902 * for this instance of the Java Virtual Machine. 903 * <p> 904 * The Java Virtual Machine sets the default locale during startup based 905 * on the host environment. It is used by many locale-sensitive methods 906 * if no locale is explicitly specified. It can be changed using the 907 * setDefault(LocaleCategory, Locale) method. 908 * 909 * @param category - the specified category to get the default locale 910 * @throws NullPointerException if category is null 911 * @return the default locale for the specified Category for this instance 912 * of the Java Virtual Machine 913 * @see #setDefault(LocaleCategory, Locale) 914 */ 915 static Locale getDefault(LocaleCategory category) { 916 // do not synchronize this method - see 4071298 917 if(category == LocaleCategory.DISPLAY) { 918 if (defaultDisplayLocale is null) { 919 synchronized { 920 if (defaultDisplayLocale is null) { 921 defaultDisplayLocale = initDefault(category); 922 } 923 } 924 } 925 return defaultDisplayLocale; 926 } else if(category == LocaleCategory.FORMAT) { 927 if (defaultFormatLocale is null) { 928 synchronized { 929 if (defaultFormatLocale is null) { 930 defaultFormatLocale = initDefault(category); 931 } 932 } 933 } 934 return defaultFormatLocale; 935 } else { 936 assert(false, "Unknown locale category"); 937 } 938 } 939 940 private static Locale initDefault() { 941 ConfigBuilder props = Environment.getProperties(); 942 string language = props.getProperty("user.language", "en"); 943 string script = props.getProperty("user.script", ""); 944 string country = props.getProperty("user.country", ""); 945 string variant = props.getProperty("user.variant", ""); 946 947 return getInstance(language, script, country, variant); 948 } 949 950 private static Locale initDefault(LocaleCategory category) { 951 ConfigBuilder props = Environment.getProperties(); 952 Locale defaultLocale = getDefault(); 953 954 return getInstance( 955 props.getProperty(category.languageKey, 956 defaultLocale.getLanguage()), 957 props.getProperty(category.scriptKey, 958 defaultLocale.getScript()), 959 props.getProperty(category.countryKey, 960 defaultLocale.getCountry()), 961 props.getProperty(category.variantKey, 962 defaultLocale.getVariant())); 963 } 964 965 966 /** 967 * Sets the default locale for this instance of the Java Virtual Machine. 968 * This does not affect the host locale. 969 * <p> 970 * If there is a security manager, its <code>checkPermission</code> 971 * method is called with a <code>PropertyPermission("user.language", "write")</code> 972 * permission before the default locale is changed. 973 * <p> 974 * The Java Virtual Machine sets the default locale during startup 975 * based on the host environment. It is used by many locale-sensitive 976 * methods if no locale is explicitly specified. 977 * <p> 978 * Since changing the default locale may affect many different areas 979 * of functionality, this method should only be used if the caller 980 * is prepared to reinitialize locale-sensitive code running 981 * within the same Java Virtual Machine. 982 * <p> 983 * By setting the default locale with this method, all of the default 984 * locales for each Category are also set to the specified default locale. 985 * 986 * @throws SecurityException 987 * if a security manager exists and its 988 * <code>checkPermission</code> method doesn't allow the operation. 989 * @throws NullPointerException if <code>newLocale</code> is null 990 * @param newLocale the new default locale 991 * @see SecurityManager#checkPermission 992 * @see java.util.PropertyPermission 993 */ 994 static void setDefault(Locale newLocale) { 995 setDefault(LocaleCategory.DISPLAY, newLocale); 996 setDefault(LocaleCategory.FORMAT, newLocale); 997 defaultLocale = newLocale; 998 } 999 1000 /** 1001 * Sets the default locale for the specified Category for this instance 1002 * of the Java Virtual Machine. This does not affect the host locale. 1003 * <p> 1004 * If there is a security manager, its checkPermission method is called 1005 * with a PropertyPermission("user.language", "write") permission before 1006 * the default locale is changed. 1007 * <p> 1008 * The Java Virtual Machine sets the default locale during startup based 1009 * on the host environment. It is used by many locale-sensitive methods 1010 * if no locale is explicitly specified. 1011 * <p> 1012 * Since changing the default locale may affect many different areas of 1013 * functionality, this method should only be used if the caller is 1014 * prepared to reinitialize locale-sensitive code running within the 1015 * same Java Virtual Machine. 1016 * 1017 * @param category - the specified category to set the default locale 1018 * @param newLocale - the new default locale 1019 * @throws SecurityException if a security manager exists and its 1020 * checkPermission method doesn't allow the operation. 1021 * @throws NullPointerException if category and/or newLocale is null 1022 * @see SecurityManager#checkPermission(java.security.Permission) 1023 * @see PropertyPermission 1024 * @see #getDefault(LocaleCategory) 1025 */ 1026 static void setDefault(LocaleCategory category, Locale newLocale) { 1027 implementationMissing(false); 1028 // if (category is null) 1029 // throw new NullPointerException("Category cannot be NULL"); 1030 // if (newLocale is null) 1031 // throw new NullPointerException("Can't set default locale to NULL"); 1032 1033 // SecurityManager sm = System.getSecurityManager(); 1034 // if (sm !is null) sm.checkPermission(new PropertyPermission 1035 // ("user.language", "write")); 1036 // switch (category) { 1037 // case DISPLAY: 1038 // defaultDisplayLocale = newLocale; 1039 // break; 1040 // case FORMAT: 1041 // defaultFormatLocale = newLocale; 1042 // break; 1043 // default: 1044 // assert false: "Unknown Category"; 1045 // } 1046 } 1047 1048 // /** 1049 // * Returns an array of all installed locales. 1050 // * The returned array represents the union of locales supported 1051 // * by the Java runtime environment and by installed 1052 // * {@link java.util.spi.LocaleServiceProvider LocaleServiceProvider} 1053 // * implementations. It must contain at least a <code>Locale</code> 1054 // * instance equal to {@link java.util.Locale#US Locale.US}. 1055 // * 1056 // * @return An array of installed locales. 1057 // */ 1058 // static Locale[] getAvailableLocales() { 1059 // return LocaleServiceProviderPool.getAllAvailableLocales(); 1060 // } 1061 1062 // /** 1063 // * Returns a list of all 2-letter country codes defined in ISO 3166. 1064 // * Can be used to create Locales. 1065 // * This method is equivalent to {@link #getISOCountries(Locale.IsoCountryCode type)} 1066 // * with {@code type} {@link IsoCountryCode#PART1_ALPHA2}. 1067 // * <p> 1068 // * <b>Note:</b> The <code>Locale</code> class also supports other codes for 1069 // * country (region), such as 3-letter numeric UN M.49 area codes. 1070 // * Therefore, the list returned by this method does not contain ALL valid 1071 // * codes that can be used to create Locales. 1072 // * <p> 1073 // * Note that this method does not return obsolete 2-letter country codes. 1074 // * ISO3166-3 codes which designate country codes for those obsolete codes, 1075 // * can be retrieved from {@link #getISOCountries(Locale.IsoCountryCode type)} with 1076 // * {@code type} {@link IsoCountryCode#PART3}. 1077 // * @return An array of ISO 3166 two-letter country codes. 1078 // */ 1079 // static string[] getISOCountries() { 1080 // if (isoCountries is null) { 1081 // isoCountries = getISO2Table(LocaleISOData.isoCountryTable); 1082 // } 1083 // string[] result = new string[isoCountries.length]; 1084 // System.arraycopy(isoCountries, 0, result, 0, isoCountries.length); 1085 // return result; 1086 // } 1087 1088 // /** 1089 // * Returns a {@code Set} of ISO3166 country codes for the specified type. 1090 // * 1091 // * @param type {@link Locale.IsoCountryCode} specified ISO code type. 1092 // * @see java.util.Locale.IsoCountryCode 1093 // * @throws NullPointerException if type is null 1094 // * @return a {@code Set} of ISO country codes for the specified type. 1095 // */ 1096 // static Set!(string) getISOCountries(IsoCountryCode type) { 1097 // assert(type); 1098 // return IsoCountryCode.retrieveISOCountryCodes(type); 1099 // } 1100 1101 // /** 1102 // * Returns a list of all 2-letter language codes defined in ISO 639. 1103 // * Can be used to create Locales. 1104 // * <p> 1105 // * <b>Note:</b> 1106 // * <ul> 1107 // * <li>ISO 639 is not a stable standard— some languages' codes have changed. 1108 // * The list this function returns includes both the new and the old codes for the 1109 // * languages whose codes have changed. 1110 // * <li>The <code>Locale</code> class also supports language codes up to 1111 // * 8 characters in length. Therefore, the list returned by this method does 1112 // * not contain ALL valid codes that can be used to create Locales. 1113 // * </ul> 1114 // * 1115 // * @return An array of ISO 639 two-letter language codes. 1116 // */ 1117 // static string[] getISOLanguages() { 1118 // if (isoLanguages is null) { 1119 // isoLanguages = getISO2Table(LocaleISOData.isoLanguageTable); 1120 // } 1121 // string[] result = new string[isoLanguages.length]; 1122 // System.arraycopy(isoLanguages, 0, result, 0, isoLanguages.length); 1123 // return result; 1124 // } 1125 1126 // private static string[] getISO2Table(string table) { 1127 // int len = table.length() / 5; 1128 // string[] isoTable = new string[len]; 1129 // for (int i = 0, j = 0; i < len; i++, j += 5) { 1130 // isoTable[i] = table.substring(j, j + 2); 1131 // } 1132 // return isoTable; 1133 // } 1134 1135 /** 1136 * Returns the language code of this Locale. 1137 * 1138 * <p><b>Note:</b> ISO 639 is not a stable standard— some languages' codes have changed. 1139 * Locale's constructor recognizes both the new and the old codes for the languages 1140 * whose codes have changed, but this function always returns the old code. If you 1141 * want to check for a specific language whose code has changed, don't do 1142 * <pre> 1143 * if (locale.getLanguage().equals("he")) // BAD! 1144 * ... 1145 * </pre> 1146 * Instead, do 1147 * <pre> 1148 * if (locale.getLanguage().equals(new Locale("he").getLanguage())) 1149 * ... 1150 * </pre> 1151 * @return The language code, or the empty string if none is defined. 1152 * @see #getDisplayLanguage 1153 */ 1154 string getLanguage() { 1155 return language; 1156 } 1157 1158 /** 1159 * Returns the script for this locale, which should 1160 * either be the empty string or an ISO 15924 4-letter script 1161 * code. The first letter is uppercase and the rest are 1162 * lowercase, for example, 'Latn', 'Cyrl'. 1163 * 1164 * @return The script code, or the empty string if none is defined. 1165 * @see #getDisplayScript 1166 */ 1167 string getScript() { 1168 return script; 1169 } 1170 1171 /** 1172 * Returns the country/region code for this locale, which should 1173 * either be the empty string, an uppercase ISO 3166 2-letter code, 1174 * or a UN M.49 3-digit code. 1175 * 1176 * @return The country/region code, or the empty string if none is defined. 1177 * @see #getDisplayCountry 1178 */ 1179 string getCountry() { 1180 return region; 1181 } 1182 1183 /** 1184 * Returns the variant code for this locale. 1185 * 1186 * @return The variant code, or the empty string if none is defined. 1187 * @see #getDisplayVariant 1188 */ 1189 string getVariant() { 1190 return variant; 1191 } 1192 1193 /** 1194 * Returns {@code true} if this {@code Locale} has any <a href="#def_extensions"> 1195 * extensions</a>. 1196 * 1197 * @return {@code true} if this {@code Locale} has any extensions 1198 */ 1199 bool hasExtensions() { 1200 return false; 1201 } 1202 1203 // /** 1204 // * Returns a copy of this {@code Locale} with no <a href="#def_extensions"> 1205 // * extensions</a>. If this {@code Locale} has no extensions, this {@code Locale} 1206 // * is returned. 1207 // * 1208 // * @return a copy of this {@code Locale} with no extensions, or {@code this} 1209 // * if {@code this} has no extensions 1210 // */ 1211 // Locale stripExtensions() { 1212 // return hasExtensions() ? Locale.getInstance(baseLocale, null) : this; 1213 // } 1214 1215 // /** 1216 // * Returns the extension (or private use) value associated with 1217 // * the specified key, or null if there is no extension 1218 // * associated with the key. To be well-formed, the key must be one 1219 // * of <code>[0-9A-Za-z]</code>. Keys are case-insensitive, so 1220 // * for example 'z' and 'Z' represent the same extension. 1221 // * 1222 // * @param key the extension key 1223 // * @return The extension, or null if this locale defines no 1224 // * extension for the specified key. 1225 // * @throws IllegalArgumentException if key is not well-formed 1226 // * @see #PRIVATE_USE_EXTENSION 1227 // * @see #UNICODE_LOCALE_EXTENSION 1228 // */ 1229 // string getExtension(char key) { 1230 // if (!LocaleExtensions.isValidKey(key)) { 1231 // throw new IllegalArgumentException("Ill-formed extension key: " ~ key); 1232 // } 1233 // return hasExtensions() ? localeExtensions.getExtensionValue(key) : null; 1234 // } 1235 1236 // /** 1237 // * Returns the set of extension keys associated with this locale, or the 1238 // * empty set if it has no extensions. The returned set is unmodifiable. 1239 // * The keys will all be lower-case. 1240 // * 1241 // * @return The set of extension keys, or the empty set if this locale has 1242 // * no extensions. 1243 // */ 1244 // Set<Character> getExtensionKeys() { 1245 // if (!hasExtensions()) { 1246 // return Collections.emptySet(); 1247 // } 1248 // return localeExtensions.getKeys(); 1249 // } 1250 1251 // /** 1252 // * Returns the set of unicode locale attributes associated with 1253 // * this locale, or the empty set if it has no attributes. The 1254 // * returned set is unmodifiable. 1255 // * 1256 // * @return The set of attributes. 1257 // */ 1258 // Set!(string) getUnicodeLocaleAttributes() { 1259 // if (!hasExtensions()) { 1260 // return Collections.emptySet(); 1261 // } 1262 // return localeExtensions.getUnicodeLocaleAttributes(); 1263 // } 1264 1265 // /** 1266 // * Returns the Unicode locale type associated with the specified Unicode locale key 1267 // * for this locale. Returns the empty string for keys that are defined with no type. 1268 // * Returns null if the key is not defined. Keys are case-insensitive. The key must 1269 // * be two alphanumeric characters ([0-9a-zA-Z]), or an IllegalArgumentException is 1270 // * thrown. 1271 // * 1272 // * @param key the Unicode locale key 1273 // * @return The Unicode locale type associated with the key, or null if the 1274 // * locale does not define the key. 1275 // * @throws IllegalArgumentException if the key is not well-formed 1276 // * @throws NullPointerException if <code>key</code> is null 1277 // */ 1278 string getUnicodeLocaleType(string key) { 1279 /* if (!isUnicodeExtensionKey(key)) { 1280 throw new IllegalArgumentException("Ill-formed Unicode locale key: " ~ key); 1281 } 1282 return hasExtensions() ? localeExtensions.getUnicodeLocaleType(key) : null; */ 1283 return "iso"; 1284 } 1285 1286 // /** 1287 // * Returns the set of Unicode locale keys defined by this locale, or the empty set if 1288 // * this locale has none. The returned set is immutable. Keys are all lower case. 1289 // * 1290 // * @return The set of Unicode locale keys, or the empty set if this locale has 1291 // * no Unicode locale keywords. 1292 // */ 1293 // Set!(string) getUnicodeLocaleKeys() { 1294 // if (localeExtensions is null) { 1295 // return Collections.emptySet(); 1296 // } 1297 // return localeExtensions.getUnicodeLocaleKeys(); 1298 // } 1299 1300 // /** 1301 // * Package locale method returning the Locale's BaseLocale, 1302 // * used by ResourceBundle 1303 // * @return base locale of this Locale 1304 // */ 1305 // BaseLocale getBaseLocale() { 1306 // return baseLocale; 1307 // } 1308 1309 // /** 1310 // * Package private method returning the Locale's LocaleExtensions, 1311 // * used by ResourceBundle. 1312 // * @return locale extensions of this Locale, 1313 // * or {@code null} if no extensions are defined 1314 // */ 1315 // LocaleExtensions getLocaleExtensions() { 1316 // return localeExtensions; 1317 // } 1318 1319 /** 1320 * Returns a string representation of this <code>Locale</code> 1321 * object, consisting of language, country, variant, script, 1322 * and extensions as below: 1323 * <blockquote> 1324 * language ~ "_" ~ country ~ "_" ~ (variant ~ "_#" | "#") + script ~ "_" ~ extensions 1325 * </blockquote> 1326 * 1327 * Language is always lower case, country is always upper case, script is always title 1328 * case, and extensions are always lower case. Extensions and private use subtags 1329 * will be in canonical order as explained in {@link #toLanguageTag}. 1330 * 1331 * <p>When the locale has neither script nor extensions, the result is the same as in 1332 * Java 6 and prior. 1333 * 1334 * <p>If both the language and country fields are missing, this function will return 1335 * the empty string, even if the variant, script, or extensions field is present (you 1336 * can't have a locale with just a variant, the variant must accompany a well-formed 1337 * language or country code). 1338 * 1339 * <p>If script or extensions are present and variant is missing, no underscore is 1340 * added before the "#". 1341 * 1342 * <p>This behavior is designed to support debugging and to be compatible with 1343 * previous uses of <code>toString</code> that expected language, country, and variant 1344 * fields only. To represent a Locale as a string for interchange purposes, use 1345 * {@link #toLanguageTag}. 1346 * 1347 * <p>Examples: <ul> 1348 * <li>{@code en}</li> 1349 * <li>{@code de_DE}</li> 1350 * <li>{@code _GB}</li> 1351 * <li>{@code en_US_WIN}</li> 1352 * <li>{@code de__POSIX}</li> 1353 * <li>{@code zh_CN_#Hans}</li> 1354 * <li>{@code zh_TW_#Hant_x-java}</li> 1355 * <li>{@code th_TH_TH_#u-nu-thai}</li></ul> 1356 * 1357 * @return A string representation of the Locale, for debugging. 1358 * @see #getDisplayName 1359 * @see #toLanguageTag 1360 */ 1361 override string toString() { 1362 1363 bool l = (language.length != 0); 1364 bool s = (script.length != 0); 1365 bool r = (region.length != 0); 1366 bool v = (variant.length != 0); 1367 // bool e = (localeExtensions !is null && localeExtensions.getID().length() != 0); 1368 1369 StringBuilder result = new StringBuilder(language); 1370 if (r || (l && (v || s ))) { 1371 result.append('_') 1372 .append(region); // This may just append '_' 1373 } 1374 if (v && (l || r)) { 1375 result.append('_') 1376 .append(variant); 1377 } 1378 1379 if (s && (l || r)) { 1380 result.append("_#") 1381 .append(script); 1382 } 1383 1384 return result.toString(); 1385 } 1386 1387 // /** 1388 // * Returns a well-formed IETF BCP 47 language tag representing 1389 // * this locale. 1390 // * 1391 // * <p>If this <code>Locale</code> has a language, country, or 1392 // * variant that does not satisfy the IETF BCP 47 language tag 1393 // * syntax requirements, this method handles these fields as 1394 // * described below: 1395 // * 1396 // * <p><b>Language:</b> If language is empty, or not <a 1397 // * href="#def_language" >well-formed</a> (for example "a" or 1398 // * "e2"), it will be emitted as "und" (Undetermined). 1399 // * 1400 // * <p><b>Country:</b> If country is not <a 1401 // * href="#def_region">well-formed</a> (for example "12" or "USA"), 1402 // * it will be omitted. 1403 // * 1404 // * <p><b>Variant:</b> If variant <b>is</b> <a 1405 // * href="#def_variant">well-formed</a>, each sub-segment 1406 // * (delimited by '-' or '_') is emitted as a subtag. Otherwise: 1407 // * <ul> 1408 // * 1409 // * <li>if all sub-segments match <code>[0-9a-zA-Z]{1,8}</code> 1410 // * (for example "WIN" or "Oracle_JDK_Standard_Edition"), the first 1411 // * ill-formed sub-segment and all following will be appended to 1412 // * the private use subtag. The first appended subtag will be 1413 // * "lvariant", followed by the sub-segments in order, separated by 1414 // * hyphen. For example, "x-lvariant-WIN", 1415 // * "Oracle-x-lvariant-JDK-Standard-Edition". 1416 // * 1417 // * <li>if any sub-segment does not match 1418 // * <code>[0-9a-zA-Z]{1,8}</code>, the variant will be truncated 1419 // * and the problematic sub-segment and all following sub-segments 1420 // * will be omitted. If the remainder is non-empty, it will be 1421 // * emitted as a private use subtag as above (even if the remainder 1422 // * turns out to be well-formed). For example, 1423 // * "Solaris_isjustthecoolestthing" is emitted as 1424 // * "x-lvariant-Solaris", not as "solaris".</li></ul> 1425 // * 1426 // * <p><b>Special Conversions:</b> Java supports some old locale 1427 // * representations, including deprecated ISO language codes, 1428 // * for compatibility. This method performs the following 1429 // * conversions: 1430 // * <ul> 1431 // * 1432 // * <li>Deprecated ISO language codes "iw", "ji", and "in" are 1433 // * converted to "he", "yi", and "id", respectively. 1434 // * 1435 // * <li>A locale with language "no", country "NO", and variant 1436 // * "NY", representing Norwegian Nynorsk (Norway), is converted 1437 // * to a language tag "nn-NO".</li></ul> 1438 // * 1439 // * <p><b>Note:</b> Although the language tag created by this 1440 // * method is well-formed (satisfies the syntax requirements 1441 // * defined by the IETF BCP 47 specification), it is not 1442 // * necessarily a valid BCP 47 language tag. For example, 1443 // * <pre> 1444 // * new Locale("xx", "YY").toLanguageTag();</pre> 1445 // * 1446 // * will return "xx-YY", but the language subtag "xx" and the 1447 // * region subtag "YY" are invalid because they are not registered 1448 // * in the IANA Language Subtag Registry. 1449 // * 1450 // * @return a BCP47 language tag representing the locale 1451 // * @see #forLanguageTag(string) 1452 // */ 1453 // string toLanguageTag() { 1454 // if (languageTag !is null) { 1455 // return languageTag; 1456 // } 1457 1458 // LanguageTag tag = LanguageTag.parseLocale(baseLocale, localeExtensions); 1459 // StringBuilder buf = new StringBuilder(); 1460 1461 // string subtag = tag.getLanguage(); 1462 // if (subtag.length() > 0) { 1463 // buf.append(LanguageTag.canonicalizeLanguage(subtag)); 1464 // } 1465 1466 // subtag = tag.getScript(); 1467 // if (subtag.length() > 0) { 1468 // buf.append(LanguageTag.SEP); 1469 // buf.append(LanguageTag.canonicalizeScript(subtag)); 1470 // } 1471 1472 // subtag = tag.getRegion(); 1473 // if (subtag.length() > 0) { 1474 // buf.append(LanguageTag.SEP); 1475 // buf.append(LanguageTag.canonicalizeRegion(subtag)); 1476 // } 1477 1478 // List<string>subtags = tag.getVariants(); 1479 // for (string s : subtags) { 1480 // buf.append(LanguageTag.SEP); 1481 // // preserve casing 1482 // buf.append(s); 1483 // } 1484 1485 // subtags = tag.getExtensions(); 1486 // for (string s : subtags) { 1487 // buf.append(LanguageTag.SEP); 1488 // buf.append(LanguageTag.canonicalizeExtension(s)); 1489 // } 1490 1491 // subtag = tag.getPrivateuse(); 1492 // if (subtag.length() > 0) { 1493 // if (buf.length() > 0) { 1494 // buf.append(LanguageTag.SEP); 1495 // } 1496 // buf.append(LanguageTag.PRIVATEUSE).append(LanguageTag.SEP); 1497 // // preserve casing 1498 // buf.append(subtag); 1499 // } 1500 1501 // string langTag = buf.toString(); 1502 // synchronized (this) { 1503 // if (languageTag is null) { 1504 // languageTag = langTag; 1505 // } 1506 // } 1507 // return languageTag; 1508 // } 1509 1510 // /** 1511 // * Returns a locale for the specified IETF BCP 47 language tag string. 1512 // * 1513 // * <p>If the specified language tag contains any ill-formed subtags, 1514 // * the first such subtag and all following subtags are ignored. Compare 1515 // * to {@link Locale.Builder#setLanguageTag} which throws an exception 1516 // * in this case. 1517 // * 1518 // * <p>The following <b>conversions</b> are performed:<ul> 1519 // * 1520 // * <li>The language code "und" is mapped to language "". 1521 // * 1522 // * <li>The language codes "he", "yi", and "id" are mapped to "iw", 1523 // * "ji", and "in" respectively. (This is the same canonicalization 1524 // * that's done in Locale's constructors.) 1525 // * 1526 // * <li>The portion of a private use subtag prefixed by "lvariant", 1527 // * if any, is removed and appended to the variant field in the 1528 // * result locale (without case normalization). If it is then 1529 // * empty, the private use subtag is discarded: 1530 // * 1531 // * <pre> 1532 // * Locale loc; 1533 // * loc = Locale.forLanguageTag("en-US-x-lvariant-POSIX"); 1534 // * loc.getVariant(); // returns "POSIX" 1535 // * loc.getExtension('x'); // returns null 1536 // * 1537 // * loc = Locale.forLanguageTag("de-POSIX-x-URP-lvariant-Abc-Def"); 1538 // * loc.getVariant(); // returns "POSIX_Abc_Def" 1539 // * loc.getExtension('x'); // returns "urp" 1540 // * </pre> 1541 // * 1542 // * <li>When the languageTag argument contains an extlang subtag, 1543 // * the first such subtag is used as the language, and the primary 1544 // * language subtag and other extlang subtags are ignored: 1545 // * 1546 // * <pre> 1547 // * Locale.forLanguageTag("ar-aao").getLanguage(); // returns "aao" 1548 // * Locale.forLanguageTag("en-abc-def-us").toString(); // returns "abc_US" 1549 // * </pre> 1550 // * 1551 // * <li>Case is normalized except for variant tags, which are left 1552 // * unchanged. Language is normalized to lower case, script to 1553 // * title case, country to upper case, and extensions to lower 1554 // * case. 1555 // * 1556 // * <li>If, after processing, the locale would exactly match either 1557 // * ja_JP_JP or th_TH_TH with no extensions, the appropriate 1558 // * extensions are added as though the constructor had been called: 1559 // * 1560 // * <pre> 1561 // * Locale.forLanguageTag("ja-JP-x-lvariant-JP").toLanguageTag(); 1562 // * // returns "ja-JP-u-ca-japanese-x-lvariant-JP" 1563 // * Locale.forLanguageTag("th-TH-x-lvariant-TH").toLanguageTag(); 1564 // * // returns "th-TH-u-nu-thai-x-lvariant-TH" 1565 // * </pre></ul> 1566 // * 1567 // * <p>This implements the 'Language-Tag' production of BCP47, and 1568 // * so supports grandfathered (regular and irregular) as well as 1569 // * private use language tags. Stand alone private use tags are 1570 // * represented as empty language and extension 'x-whatever', 1571 // * and grandfathered tags are converted to their canonical replacements 1572 // * where they exist. 1573 // * 1574 // * <p>Grandfathered tags with canonical replacements are as follows: 1575 // * 1576 // * <table class="striped"> 1577 // * <caption style="display:none">Grandfathered tags with canonical replacements</caption> 1578 // * <thead style="text-align:center"> 1579 // * <tr><th scope="col" style="padding: 0 2px">grandfathered tag</th><th scope="col" style="padding: 0 2px">modern replacement</th></tr> 1580 // * </thead> 1581 // * <tbody style="text-align:center"> 1582 // * <tr><th scope="row">art-lojban</th><td>jbo</td></tr> 1583 // * <tr><th scope="row">i-ami</th><td>ami</td></tr> 1584 // * <tr><th scope="row">i-bnn</th><td>bnn</td></tr> 1585 // * <tr><th scope="row">i-hak</th><td>hak</td></tr> 1586 // * <tr><th scope="row">i-klingon</th><td>tlh</td></tr> 1587 // * <tr><th scope="row">i-lux</th><td>lb</td></tr> 1588 // * <tr><th scope="row">i-navajo</th><td>nv</td></tr> 1589 // * <tr><th scope="row">i-pwn</th><td>pwn</td></tr> 1590 // * <tr><th scope="row">i-tao</th><td>tao</td></tr> 1591 // * <tr><th scope="row">i-tay</th><td>tay</td></tr> 1592 // * <tr><th scope="row">i-tsu</th><td>tsu</td></tr> 1593 // * <tr><th scope="row">no-bok</th><td>nb</td></tr> 1594 // * <tr><th scope="row">no-nyn</th><td>nn</td></tr> 1595 // * <tr><th scope="row">sgn-BE-FR</th><td>sfb</td></tr> 1596 // * <tr><th scope="row">sgn-BE-NL</th><td>vgt</td></tr> 1597 // * <tr><th scope="row">sgn-CH-DE</th><td>sgg</td></tr> 1598 // * <tr><th scope="row">zh-guoyu</th><td>cmn</td></tr> 1599 // * <tr><th scope="row">zh-hakka</th><td>hak</td></tr> 1600 // * <tr><th scope="row">zh-min-nan</th><td>nan</td></tr> 1601 // * <tr><th scope="row">zh-xiang</th><td>hsn</td></tr> 1602 // * </tbody> 1603 // * </table> 1604 // * 1605 // * <p>Grandfathered tags with no modern replacement will be 1606 // * converted as follows: 1607 // * 1608 // * <table class="striped"> 1609 // * <caption style="display:none">Grandfathered tags with no modern replacement</caption> 1610 // * <thead style="text-align:center"> 1611 // * <tr><th scope="col" style="padding: 0 2px">grandfathered tag</th><th scope="col" style="padding: 0 2px">converts to</th></tr> 1612 // * </thead> 1613 // * <tbody style="text-align:center"> 1614 // * <tr><th scope="row">cel-gaulish</th><td>xtg-x-cel-gaulish</td></tr> 1615 // * <tr><th scope="row">en-GB-oed</th><td>en-GB-x-oed</td></tr> 1616 // * <tr><th scope="row">i-default</th><td>en-x-i-default</td></tr> 1617 // * <tr><th scope="row">i-enochian</th><td>und-x-i-enochian</td></tr> 1618 // * <tr><th scope="row">i-mingo</th><td>see-x-i-mingo</td></tr> 1619 // * <tr><th scope="row">zh-min</th><td>nan-x-zh-min</td></tr> 1620 // * </tbody> 1621 // * </table> 1622 // * 1623 // * <p>For a list of all grandfathered tags, see the 1624 // * IANA Language Subtag Registry (search for "Type: grandfathered"). 1625 // * 1626 // * <p><b>Note</b>: there is no guarantee that <code>toLanguageTag</code> 1627 // * and <code>forLanguageTag</code> will round-trip. 1628 // * 1629 // * @param languageTag the language tag 1630 // * @return The locale that best represents the language tag. 1631 // * @throws NullPointerException if <code>languageTag</code> is <code>null</code> 1632 // * @see #toLanguageTag() 1633 // * @see java.util.Locale.Builder#setLanguageTag(string) 1634 // */ 1635 // static Locale forLanguageTag(string languageTag) { 1636 // LanguageTag tag = LanguageTag.parse(languageTag, null); 1637 // InternalLocaleBuilder bldr = new InternalLocaleBuilder(); 1638 // bldr.setLanguageTag(tag); 1639 // BaseLocale base = bldr.getBaseLocale(); 1640 // LocaleExtensions exts = bldr.getLocaleExtensions(); 1641 // if (exts is null && base.getVariant().length() > 0) { 1642 // exts = getCompatibilityExtensions(base.getLanguage(), base.getScript(), 1643 // base.getRegion(), base.getVariant()); 1644 // } 1645 // return getInstance(base, exts); 1646 // } 1647 1648 // /** 1649 // * Returns a three-letter abbreviation of this locale's language. 1650 // * If the language matches an ISO 639-1 two-letter code, the 1651 // * corresponding ISO 639-2/T three-letter lowercase code is 1652 // * returned. The ISO 639-2 language codes can be found on-line, 1653 // * see "Codes for the Representation of Names of Languages Part 2: 1654 // * Alpha-3 Code". If the locale specifies a three-letter 1655 // * language, the language is returned as is. If the locale does 1656 // * not specify a language the empty string is returned. 1657 // * 1658 // * @return A three-letter abbreviation of this locale's language. 1659 // * @exception MissingResourceException Throws MissingResourceException if 1660 // * three-letter language abbreviation is not available for this locale. 1661 // */ 1662 // string getISO3Language() throws MissingResourceException { 1663 // string lang = language; 1664 // if (lang.length() == 3) { 1665 // return lang; 1666 // } 1667 1668 // string language3 = getISO3Code(lang, LocaleISOData.isoLanguageTable); 1669 // if (language3 is null) { 1670 // throw new MissingResourceException("Couldn't find 3-letter language code for " 1671 // + lang, "FormatData_" ~ toString(), "ShortLanguage"); 1672 // } 1673 // return language3; 1674 // } 1675 1676 // /** 1677 // * Returns a three-letter abbreviation for this locale's country. 1678 // * If the country matches an ISO 3166-1 alpha-2 code, the 1679 // * corresponding ISO 3166-1 alpha-3 uppercase code is returned. 1680 // * If the locale doesn't specify a country, this will be the empty 1681 // * string. 1682 // * 1683 // * <p>The ISO 3166-1 codes can be found on-line. 1684 // * 1685 // * @return A three-letter abbreviation of this locale's country. 1686 // * @exception MissingResourceException Throws MissingResourceException if the 1687 // * three-letter country abbreviation is not available for this locale. 1688 // */ 1689 // string getISO3Country() throws MissingResourceException { 1690 // string country3 = getISO3Code(region, LocaleISOData.isoCountryTable); 1691 // if (country3 is null) { 1692 // throw new MissingResourceException("Couldn't find 3-letter country code for " 1693 // + region, "FormatData_" ~ toString(), "ShortCountry"); 1694 // } 1695 // return country3; 1696 // } 1697 1698 // private static string getISO3Code(string iso2Code, string table) { 1699 // int codeLength = iso2Code.length(); 1700 // if (codeLength == 0) { 1701 // return ""; 1702 // } 1703 1704 // int tableLength = table.length(); 1705 // int index = tableLength; 1706 // if (codeLength == 2) { 1707 // char c1 = iso2Code[0]; 1708 // char c2 = iso2Code[1]; 1709 // for (index = 0; index < tableLength; index += 5) { 1710 // if (table[index] == c1 1711 // && table[index + 1] == c2) { 1712 // break; 1713 // } 1714 // } 1715 // } 1716 // return index < tableLength ? table.substring(index + 2, index + 5) : null; 1717 // } 1718 1719 // /** 1720 // * Returns a name for the locale's language that is appropriate for display to the 1721 // * user. 1722 // * If possible, the name returned will be localized for the default 1723 // * {@link LocaleCategory#DISPLAY DISPLAY} locale. 1724 // * For example, if the locale is fr_FR and the default 1725 // * {@link LocaleCategory#DISPLAY DISPLAY} locale 1726 // * is en_US, getDisplayLanguage() will return "French"; if the locale is en_US and 1727 // * the default {@link LocaleCategory#DISPLAY DISPLAY} locale is fr_FR, 1728 // * getDisplayLanguage() will return "anglais". 1729 // * If the name returned cannot be localized for the default 1730 // * {@link LocaleCategory#DISPLAY DISPLAY} locale, 1731 // * (say, we don't have a Japanese name for Croatian), 1732 // * this function falls back on the English name, and uses the ISO code as a last-resort 1733 // * value. If the locale doesn't specify a language, this function returns the empty string. 1734 // * 1735 // * @return The name of the display language. 1736 // */ 1737 // final string getDisplayLanguage() { 1738 // return getDisplayLanguage(getDefault(Category.DISPLAY)); 1739 // } 1740 1741 // /** 1742 // * Returns a name for the locale's language that is appropriate for display to the 1743 // * user. 1744 // * If possible, the name returned will be localized according to inLocale. 1745 // * For example, if the locale is fr_FR and inLocale 1746 // * is en_US, getDisplayLanguage() will return "French"; if the locale is en_US and 1747 // * inLocale is fr_FR, getDisplayLanguage() will return "anglais". 1748 // * If the name returned cannot be localized according to inLocale, 1749 // * (say, we don't have a Japanese name for Croatian), 1750 // * this function falls back on the English name, and finally 1751 // * on the ISO code as a last-resort value. If the locale doesn't specify a language, 1752 // * this function returns the empty string. 1753 // * 1754 // * @param inLocale The locale for which to retrieve the display language. 1755 // * @return The name of the display language appropriate to the given locale. 1756 // * @exception NullPointerException if <code>inLocale</code> is <code>null</code> 1757 // */ 1758 // string getDisplayLanguage(Locale inLocale) { 1759 // return getDisplaystring(language, null, inLocale, DISPLAY_LANGUAGE); 1760 // } 1761 1762 // /** 1763 // * Returns a name for the locale's script that is appropriate for display to 1764 // * the user. If possible, the name will be localized for the default 1765 // * {@link LocaleCategory#DISPLAY DISPLAY} locale. Returns 1766 // * the empty string if this locale doesn't specify a script code. 1767 // * 1768 // * @return the display name of the script code for the current default 1769 // * {@link LocaleCategory#DISPLAY DISPLAY} locale 1770 // */ 1771 // string getDisplayScript() { 1772 // return getDisplayScript(getDefault(Category.DISPLAY)); 1773 // } 1774 1775 // /** 1776 // * Returns a name for the locale's script that is appropriate 1777 // * for display to the user. If possible, the name will be 1778 // * localized for the given locale. Returns the empty string if 1779 // * this locale doesn't specify a script code. 1780 // * 1781 // * @param inLocale The locale for which to retrieve the display script. 1782 // * @return the display name of the script code for the current default 1783 // * {@link LocaleCategory#DISPLAY DISPLAY} locale 1784 // * @throws NullPointerException if <code>inLocale</code> is <code>null</code> 1785 // */ 1786 // string getDisplayScript(Locale inLocale) { 1787 // return getDisplaystring(script, null, inLocale, DISPLAY_SCRIPT); 1788 // } 1789 1790 // /** 1791 // * Returns a name for the locale's country that is appropriate for display to the 1792 // * user. 1793 // * If possible, the name returned will be localized for the default 1794 // * {@link LocaleCategory#DISPLAY DISPLAY} locale. 1795 // * For example, if the locale is fr_FR and the default 1796 // * {@link LocaleCategory#DISPLAY DISPLAY} locale 1797 // * is en_US, getDisplayCountry() will return "France"; if the locale is en_US and 1798 // * the default {@link LocaleCategory#DISPLAY DISPLAY} locale is fr_FR, 1799 // * getDisplayCountry() will return "Etats-Unis". 1800 // * If the name returned cannot be localized for the default 1801 // * {@link LocaleCategory#DISPLAY DISPLAY} locale, 1802 // * (say, we don't have a Japanese name for Croatia), 1803 // * this function falls back on the English name, and uses the ISO code as a last-resort 1804 // * value. If the locale doesn't specify a country, this function returns the empty string. 1805 // * 1806 // * @return The name of the country appropriate to the locale. 1807 // */ 1808 // final string getDisplayCountry() { 1809 // return getDisplayCountry(getDefault(Category.DISPLAY)); 1810 // } 1811 1812 // /** 1813 // * Returns a name for the locale's country that is appropriate for display to the 1814 // * user. 1815 // * If possible, the name returned will be localized according to inLocale. 1816 // * For example, if the locale is fr_FR and inLocale 1817 // * is en_US, getDisplayCountry() will return "France"; if the locale is en_US and 1818 // * inLocale is fr_FR, getDisplayCountry() will return "Etats-Unis". 1819 // * If the name returned cannot be localized according to inLocale. 1820 // * (say, we don't have a Japanese name for Croatia), 1821 // * this function falls back on the English name, and finally 1822 // * on the ISO code as a last-resort value. If the locale doesn't specify a country, 1823 // * this function returns the empty string. 1824 // * 1825 // * @param inLocale The locale for which to retrieve the display country. 1826 // * @return The name of the country appropriate to the given locale. 1827 // * @exception NullPointerException if <code>inLocale</code> is <code>null</code> 1828 // */ 1829 // string getDisplayCountry(Locale inLocale) { 1830 // return getDisplaystring(region, null, inLocale, DISPLAY_COUNTRY); 1831 // } 1832 1833 // private string getDisplaystring(string code, string cat, Locale inLocale, int type) { 1834 // assert(inLocale); 1835 // assert(code); 1836 1837 // if (code.isEmpty()) { 1838 // return ""; 1839 // } 1840 1841 // LocaleServiceProviderPool pool = 1842 // LocaleServiceProviderPool.getPool(LocaleNameProvider.class); 1843 // string rbKey = (type == DISPLAY_VARIANT ? "%%"+code : code); 1844 // string result = pool.getLocalizedObject( 1845 // LocaleNameGetter.INSTANCE, 1846 // inLocale, rbKey, type, code, cat); 1847 // return result !is null ? result : code; 1848 // } 1849 1850 // /** 1851 // * Returns a name for the locale's variant code that is appropriate for display to the 1852 // * user. If possible, the name will be localized for the default 1853 // * {@link LocaleCategory#DISPLAY DISPLAY} locale. If the locale 1854 // * doesn't specify a variant code, this function returns the empty string. 1855 // * 1856 // * @return The name of the display variant code appropriate to the locale. 1857 // */ 1858 // final string getDisplayVariant() { 1859 // return getDisplayVariant(getDefault(Category.DISPLAY)); 1860 // } 1861 1862 // /** 1863 // * Returns a name for the locale's variant code that is appropriate for display to the 1864 // * user. If possible, the name will be localized for inLocale. If the locale 1865 // * doesn't specify a variant code, this function returns the empty string. 1866 // * 1867 // * @param inLocale The locale for which to retrieve the display variant code. 1868 // * @return The name of the display variant code appropriate to the given locale. 1869 // * @exception NullPointerException if <code>inLocale</code> is <code>null</code> 1870 // */ 1871 // string getDisplayVariant(Locale inLocale) { 1872 // if (variant.length() == 0) 1873 // return ""; 1874 1875 // LocaleResources lr = LocaleProviderAdapter 1876 // .getResourceBundleBased() 1877 // .getLocaleResources(inLocale); 1878 1879 // string names[] = getDisplayVariantArray(inLocale); 1880 1881 // // Get the localized patterns for formatting a list, and use 1882 // // them to format the list. 1883 // return formatList(names, 1884 // lr.getLocaleName("ListCompositionPattern")); 1885 // } 1886 1887 // /** 1888 // * Returns a name for the locale that is appropriate for display to the 1889 // * user. This will be the values returned by getDisplayLanguage(), 1890 // * getDisplayScript(), getDisplayCountry(), getDisplayVariant() and 1891 // * optional <a href="./Locale.html#def_locale_extension">Unicode extensions</a> 1892 // * assembled into a single string. The non-empty values are used in order, with 1893 // * the second and subsequent names in parentheses. For example: 1894 // * <blockquote> 1895 // * language (script, country, variant(, extension)*)<br> 1896 // * language (country(, extension)*)<br> 1897 // * language (variant(, extension)*)<br> 1898 // * script (country(, extension)*)<br> 1899 // * country (extension)*<br> 1900 // * </blockquote> 1901 // * depending on which fields are specified in the locale. The field 1902 // * separator in the above parentheses, denoted as a comma character, may 1903 // * be localized depending on the locale. If the language, script, country, 1904 // * and variant fields are all empty, this function returns the empty string. 1905 // * 1906 // * @return The name of the locale appropriate to display. 1907 // */ 1908 // final string getDisplayName() { 1909 // return getDisplayName(getDefault(Category.DISPLAY)); 1910 // } 1911 1912 // /** 1913 // * Returns a name for the locale that is appropriate for display 1914 // * to the user. This will be the values returned by 1915 // * getDisplayLanguage(), getDisplayScript(),getDisplayCountry() 1916 // * getDisplayVariant(), and optional <a href="./Locale.html#def_locale_extension"> 1917 // * Unicode extensions</a> assembled into a single string. The non-empty 1918 // * values are used in order, with the second and subsequent names in 1919 // * parentheses. For example: 1920 // * <blockquote> 1921 // * language (script, country, variant(, extension)*)<br> 1922 // * language (country(, extension)*)<br> 1923 // * language (variant(, extension)*)<br> 1924 // * script (country(, extension)*)<br> 1925 // * country (extension)*<br> 1926 // * </blockquote> 1927 // * depending on which fields are specified in the locale. The field 1928 // * separator in the above parentheses, denoted as a comma character, may 1929 // * be localized depending on the locale. If the language, script, country, 1930 // * and variant fields are all empty, this function returns the empty string. 1931 // * 1932 // * @param inLocale The locale for which to retrieve the display name. 1933 // * @return The name of the locale appropriate to display. 1934 // * @throws NullPointerException if <code>inLocale</code> is <code>null</code> 1935 // */ 1936 // string getDisplayName(Locale inLocale) { 1937 // LocaleResources lr = LocaleProviderAdapter 1938 // .getResourceBundleBased() 1939 // .getLocaleResources(inLocale); 1940 1941 // string languageName = getDisplayLanguage(inLocale); 1942 // string scriptName = getDisplayScript(inLocale); 1943 // string countryName = getDisplayCountry(inLocale); 1944 // string[] variantNames = getDisplayVariantArray(inLocale); 1945 1946 // // Get the localized patterns for formatting a display name. 1947 // string displayNamePattern = lr.getLocaleName("DisplayNamePattern"); 1948 // string listCompositionPattern = lr.getLocaleName("ListCompositionPattern"); 1949 1950 // // The display name consists of a main name, followed by qualifiers. 1951 // // Typically, the format is "MainName (Qualifier, Qualifier)" but this 1952 // // depends on what pattern is stored in the display locale. 1953 // string mainName = null; 1954 // string[] qualifierNames = null; 1955 1956 // // The main name is the language, or if there is no language, the script, 1957 // // then if no script, the country. If there is no language/script/country 1958 // // (an anomalous situation) then the display name is simply the variant's 1959 // // display name. 1960 // if (languageName.length() == 0 && scriptName.length() == 0 && countryName.length() == 0) { 1961 // if (variantNames.length == 0) { 1962 // return ""; 1963 // } else { 1964 // return formatList(variantNames, listCompositionPattern); 1965 // } 1966 // } 1967 // ArrayList<string> names = new ArrayList<>(4); 1968 // if (languageName.length() != 0) { 1969 // names.add(languageName); 1970 // } 1971 // if (scriptName.length() != 0) { 1972 // names.add(scriptName); 1973 // } 1974 // if (countryName.length() != 0) { 1975 // names.add(countryName); 1976 // } 1977 // if (variantNames.length != 0) { 1978 // names.addAll(Arrays.asList(variantNames)); 1979 // } 1980 1981 // // add Unicode extensions 1982 // if (localeExtensions !is null) { 1983 // localeExtensions.getUnicodeLocaleAttributes().stream() 1984 // .map(key -> getDisplaystring(key, null, inLocale, DISPLAY_UEXT_KEY)) 1985 // .forEach(names::add); 1986 // localeExtensions.getUnicodeLocaleKeys().stream() 1987 // .map(key -> getDisplayKeyTypeExtensionstring(key, lr, inLocale)) 1988 // .forEach(names::add); 1989 // } 1990 1991 // // The first one in the main name 1992 // mainName = names.get(0); 1993 1994 // // Others are qualifiers 1995 // int numNames = names.size(); 1996 // qualifierNames = (numNames > 1) ? 1997 // names.subList(1, numNames).toArray(new string[numNames - 1]) : new string[0]; 1998 1999 // // Create an array whose first element is the number of remaining 2000 // // elements. This serves as a selector into a ChoiceFormat pattern from 2001 // // the resource. The second and third elements are the main name and 2002 // // the qualifier; if there are no qualifiers, the third element is 2003 // // unused by the format pattern. 2004 // Object[] displayNames = { 2005 // qualifierNames.length != 0 ? 2 : 1, 2006 // mainName, 2007 // // We could also just call formatList() and have it handle the empty 2008 // // list case, but this is more efficient, and we want it to be 2009 // // efficient since all the language-only locales will not have any 2010 // // qualifiers. 2011 // qualifierNames.length != 0 ? formatList(qualifierNames, listCompositionPattern) : null 2012 // }; 2013 2014 // if (displayNamePattern !is null) { 2015 // return new MessageFormat(displayNamePattern).format(displayNames); 2016 // } 2017 // else { 2018 // // If we cannot get the message format pattern, then we use a simple 2019 // // hard-coded pattern. This should not occur in practice unless the 2020 // // installation is missing some core files (FormatData etc.). 2021 // StringBuilder result = new StringBuilder(); 2022 // result.append((string)displayNames[1]); 2023 // if (displayNames.length > 2) { 2024 // result.append(" ("); 2025 // result.append((string)displayNames[2]); 2026 // result.append(')'); 2027 // } 2028 // return result.toString(); 2029 // } 2030 // } 2031 2032 // /** 2033 // * Overrides Cloneable. 2034 // */ 2035 // override 2036 // Object clone() 2037 // { 2038 // try { 2039 // Locale that = (Locale)super.clone(); 2040 // return that; 2041 // } catch (CloneNotSupportedException e) { 2042 // throw new InternalError(e); 2043 // } 2044 // } 2045 2046 // /** 2047 // * Override hashCode. 2048 // * Since Locales are often used in hashtables, caches the value 2049 // * for speed. 2050 // */ 2051 // override 2052 // int hashCode() { 2053 // int hc = hashCodeValue; 2054 // if (hc == 0) { 2055 // hc = baseLocale.hashCode(); 2056 // if (localeExtensions !is null) { 2057 // hc ^= localeExtensions.hashCode(); 2058 // } 2059 // hashCodeValue = hc; 2060 // } 2061 // return hc; 2062 // } 2063 2064 // // Overrides 2065 2066 // /** 2067 // * Returns true if this Locale is equal to another object. A Locale is 2068 // * deemed equal to another Locale with identical language, script, country, 2069 // * variant and extensions, and unequal to all other objects. 2070 // * 2071 // * @return true if this Locale is equal to the specified object. 2072 // */ 2073 // override 2074 // bool equals(Object obj) { 2075 // if (this is obj) // quick check 2076 // return true; 2077 // if (!(obj instanceof Locale)) 2078 // return false; 2079 // BaseLocale otherBase = ((Locale)obj).baseLocale; 2080 // if (!baseLocale.equals(otherBase)) { 2081 // return false; 2082 // } 2083 // if (localeExtensions is null) { 2084 // return ((Locale)obj).localeExtensions is null; 2085 // } 2086 // return localeExtensions.equals(((Locale)obj).localeExtensions); 2087 // } 2088 2089 // // ================= privates ===================================== 2090 2091 // private BaseLocale baseLocale; 2092 // private LocaleExtensions localeExtensions; 2093 2094 /** 2095 * Calculated hashcode 2096 */ 2097 private int hashCodeValue; 2098 2099 // private __gshared Locale _defaultLocale; 2100 private __gshared Locale defaultDisplayLocale; 2101 private __gshared Locale defaultFormatLocale; 2102 2103 private string languageTag; 2104 2105 // /** 2106 // * Return an array of the display names of the variant. 2107 // * @param bundle the ResourceBundle to use to get the display names 2108 // * @return an array of display names, possible of zero length. 2109 // */ 2110 // private string[] getDisplayVariantArray(Locale inLocale) { 2111 // // Split the variant name into tokens separated by '_'. 2112 // stringTokenizer tokenizer = new stringTokenizer(variant, "_"); 2113 // string[] names = new string[tokenizer.countTokens()]; 2114 2115 // // For each variant token, lookup the display name. If 2116 // // not found, use the variant name itself. 2117 // for (int i=0; i<names.length; ++i) { 2118 // names[i] = getDisplaystring(tokenizer.nextToken(), null, 2119 // inLocale, DISPLAY_VARIANT); 2120 // } 2121 2122 // return names; 2123 // } 2124 2125 // private string getDisplayKeyTypeExtensionstring(string key, LocaleResources lr, Locale inLocale) { 2126 // string type = localeExtensions.getUnicodeLocaleType(key); 2127 // string ret = getDisplaystring(type, key, inLocale, DISPLAY_UEXT_TYPE); 2128 2129 // if (ret is null || ret.equals(type)) { 2130 // // no localization for this type. try combining key/type separately 2131 // string displayType = type; 2132 // switch (key) { 2133 // case "cu": 2134 // displayType = lr.getCurrencyName(type.toLowerCase(Locale.ROOT)); 2135 // break; 2136 // case "rg": 2137 // if (type !is null && 2138 // // UN M.49 code should not be allowed here 2139 // type.matches("^[a-zA-Z]{2}[zZ]{4}$")) { 2140 // displayType = lr.getLocaleName(type.substring(0, 2).toUpperCase(Locale.ROOT)); 2141 // } 2142 // break; 2143 // case "tz": 2144 // displayType = TimeZoneNameUtility.convertLDMLShortID(type) 2145 // .map(id -> TimeZoneNameUtility.retrieveGenericDisplayName(id, TimeZone.LONG, inLocale)) 2146 // .orElse(type); 2147 // break; 2148 // } 2149 // ret = MessageFormat.format(lr.getLocaleName("ListKeyTypePattern"), 2150 // getDisplaystring(key, null, inLocale, DISPLAY_UEXT_KEY), 2151 // Optional.ofNullable(displayType).orElse(type)); 2152 // } 2153 2154 // return ret; 2155 // } 2156 2157 // /** 2158 // * Format a list using given pattern strings. 2159 // * If either of the patterns is null, then a the list is 2160 // * formatted by concatenation with the delimiter ','. 2161 // * @param stringList the list of strings to be formatted. 2162 // * and formatting them into a list. 2163 // * @param pattern should take 2 arguments for reduction 2164 // * @return a string representing the list. 2165 // */ 2166 // private static string formatList(string[] stringList, string pattern) { 2167 // // If we have no list patterns, compose the list in a simple, 2168 // // non-localized way. 2169 // if (pattern is null) { 2170 // return Arrays.stream(stringList).collect(Collectors.joining(",")); 2171 // } 2172 2173 // switch (stringList.length) { 2174 // case 0: 2175 // return ""; 2176 // case 1: 2177 // return stringList[0]; 2178 // default: 2179 // return Arrays.stream(stringList).reduce("", 2180 // (s1, s2) -> { 2181 // if (s1.equals("")) { 2182 // return s2; 2183 // } 2184 // if (s2.equals("")) { 2185 // return s1; 2186 // } 2187 // return MessageFormat.format(pattern, s1, s2); 2188 // }); 2189 // } 2190 // } 2191 2192 // // Duplicate of sun.util.locale.UnicodeLocaleExtension.isKey in order to 2193 // // avoid its class loading. 2194 // private static bool isUnicodeExtensionKey(string s) { 2195 // // 2alphanum 2196 // return (s.length() == 2) && LocaleUtils.isAlphaNumericstring(s); 2197 // } 2198 2199 // /** 2200 // * @serialField language string 2201 // * language subtag in lower case. 2202 // * (See <a href="java.base/java/util/Locale.html#getLanguage()">getLanguage()</a>) 2203 // * @serialField country string 2204 // * country subtag in upper case. 2205 // * (See <a href="java.base/java/util/Locale.html#getCountry()">getCountry()</a>) 2206 // * @serialField variant string 2207 // * variant subtags separated by LOWLINE characters. 2208 // * (See <a href="java.base/java/util/Locale.html#getVariant()">getVariant()</a>) 2209 // * @serialField hashcode int 2210 // * deprecated, for forward compatibility only 2211 // * @serialField script string 2212 // * script subtag in title case 2213 // * (See <a href="java.base/java/util/Locale.html#getScript()">getScript()</a>) 2214 // * @serialField extensions string 2215 // * canonical representation of extensions, that is, 2216 // * BCP47 extensions in alphabetical order followed by 2217 // * BCP47 private use subtags, all in lower case letters 2218 // * separated by HYPHEN-MINUS characters. 2219 // * (See <a href="java.base/java/util/Locale.html#getExtensionKeys()">getExtensionKeys()</a>, 2220 // * <a href="java.base/java/util/Locale.html#getExtension(char)">getExtension(char)</a>) 2221 // */ 2222 // private static final ObjectStreamField[] serialPersistentFields = { 2223 // new ObjectStreamField("language", string.class), 2224 // new ObjectStreamField("country", string.class), 2225 // new ObjectStreamField("variant", string.class), 2226 // new ObjectStreamField("hashcode", int.class), 2227 // new ObjectStreamField("script", string.class), 2228 // new ObjectStreamField("extensions", string.class), 2229 // }; 2230 2231 // /** 2232 // * Serializes this <code>Locale</code> to the specified <code>ObjectOutputStream</code>. 2233 // * @param out the <code>ObjectOutputStream</code> to write 2234 // * @throws IOException 2235 // */ 2236 // private void writeObject(ObjectOutputStream out) throws IOException { 2237 // ObjectOutputStream.PutField fields = out.putFields(); 2238 // fields.put("language", language); 2239 // fields.put("script", script); 2240 // fields.put("country", region); 2241 // fields.put("variant", variant); 2242 // fields.put("extensions", localeExtensions is null ? "" : localeExtensions.getID()); 2243 // fields.put("hashcode", -1); // place holder just for backward support 2244 // out.writeFields(); 2245 // } 2246 2247 // /** 2248 // * Deserializes this <code>Locale</code>. 2249 // * @param in the <code>ObjectInputStream</code> to read 2250 // * @throws IOException 2251 // * @throws ClassNotFoundException 2252 // * @throws IllformedLocaleException 2253 // */ 2254 // private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { 2255 // ObjectInputStream.GetField fields = in.readFields(); 2256 // string language = (string)fields.get("language", ""); 2257 // string script = (string)fields.get("script", ""); 2258 // string country = (string)fields.get("country", ""); 2259 // string variant = (string)fields.get("variant", ""); 2260 // string extStr = (string)fields.get("extensions", ""); 2261 // baseLocale = BaseLocale.getInstance(convertOldISOCodes(language), script, country, variant); 2262 // if (extStr.length() > 0) { 2263 // try { 2264 // InternalLocaleBuilder bldr = new InternalLocaleBuilder(); 2265 // bldr.setExtensions(extStr); 2266 // localeExtensions = bldr.getLocaleExtensions(); 2267 // } catch (LocaleSyntaxException e) { 2268 // throw new IllformedLocaleException(e.getMessage()); 2269 // } 2270 // } else { 2271 // localeExtensions = null; 2272 // } 2273 // } 2274 2275 // /** 2276 // * Returns a cached <code>Locale</code> instance equivalent to 2277 // * the deserialized <code>Locale</code>. When serialized 2278 // * language, country and variant fields read from the object data stream 2279 // * are exactly "ja", "JP", "JP" or "th", "TH", "TH" and script/extensions 2280 // * fields are empty, this method supplies <code>UNICODE_LOCALE_EXTENSION</code> 2281 // * "ca"/"japanese" (calendar type is "japanese") or "nu"/"thai" (number script 2282 // * type is "thai"). See <a href="Locale.html#special_cases_constructor">Special Cases</a> 2283 // * for more information. 2284 // * 2285 // * @return an instance of <code>Locale</code> equivalent to 2286 // * the deserialized <code>Locale</code>. 2287 // * @throws java.io.ObjectStreamException 2288 // */ 2289 // private Object readResolve() throws java.io.ObjectStreamException { 2290 // return getInstance(language, script, 2291 // region, variant, localeExtensions); 2292 // } 2293 2294 // private static string[] isoLanguages; 2295 2296 // private static string[] isoCountries; 2297 2298 private static string convertOldISOCodes(string language) { 2299 // we accept both the old and the new ISO codes for the languages whose ISO 2300 // codes have changed, but we always store the OLD code, for backward compatibility 2301 language = language.toLower(); 2302 if (language == "he") { 2303 return "iw"; 2304 } else if (language == "yi") { 2305 return "ji"; 2306 } else if (language == "id") { 2307 return "in"; 2308 } else { 2309 return language; 2310 } 2311 } 2312 2313 // private static LocaleExtensions getCompatibilityExtensions(string language, 2314 // string script, 2315 // string country, 2316 // string variant) { 2317 // LocaleExtensions extensions = null; 2318 // // Special cases for backward compatibility support 2319 // if (LocaleUtils.caseIgnoreMatch(language, "ja") 2320 // && script.length() == 0 2321 // && LocaleUtils.caseIgnoreMatch(country, "jp") 2322 // && "JP".equals(variant)) { 2323 // // ja_JP_JP -> u-ca-japanese (calendar = japanese) 2324 // extensions = LocaleExtensions.CALENDAR_JAPANESE; 2325 // } else if (LocaleUtils.caseIgnoreMatch(language, "th") 2326 // && script.length() == 0 2327 // && LocaleUtils.caseIgnoreMatch(country, "th") 2328 // && "TH".equals(variant)) { 2329 // // th_TH_TH -> u-nu-thai (numbersystem = thai) 2330 // extensions = LocaleExtensions.NUMBER_THAI; 2331 // } 2332 // return extensions; 2333 // } 2334 2335 // /** 2336 // * Obtains a localized locale names from a LocaleNameProvider 2337 // * implementation. 2338 // */ 2339 // private static class LocaleNameGetter 2340 // implements LocaleServiceProviderPool.LocalizedObjectGetter<LocaleNameProvider, string> { 2341 // private static final LocaleNameGetter INSTANCE = new LocaleNameGetter(); 2342 2343 // override 2344 // string getObject(LocaleNameProvider localeNameProvider, 2345 // Locale locale, 2346 // string key, 2347 // Object... params) { 2348 // assert params.length == 3; 2349 // int type = (Integer)params[0]; 2350 // string code = (string)params[1]; 2351 // string cat = (string)params[2]; 2352 2353 // switch(type) { 2354 // case DISPLAY_LANGUAGE: 2355 // return localeNameProvider.getDisplayLanguage(code, locale); 2356 // case DISPLAY_COUNTRY: 2357 // return localeNameProvider.getDisplayCountry(code, locale); 2358 // case DISPLAY_VARIANT: 2359 // return localeNameProvider.getDisplayVariant(code, locale); 2360 // case DISPLAY_SCRIPT: 2361 // return localeNameProvider.getDisplayScript(code, locale); 2362 // case DISPLAY_UEXT_KEY: 2363 // return localeNameProvider.getDisplayUnicodeExtensionKey(code, locale); 2364 // case DISPLAY_UEXT_TYPE: 2365 // return localeNameProvider.getDisplayUnicodeExtensionType(code, cat, locale); 2366 // default: 2367 // assert false; // shouldn't happen 2368 // } 2369 2370 // return null; 2371 // } 2372 // } 2373 2374 2375 // /** 2376 // * <code>Builder</code> is used to build instances of <code>Locale</code> 2377 // * from values configured by the setters. Unlike the <code>Locale</code> 2378 // * constructors, the <code>Builder</code> checks if a value configured by a 2379 // * setter satisfies the syntax requirements defined by the <code>Locale</code> 2380 // * class. A <code>Locale</code> object created by a <code>Builder</code> is 2381 // * well-formed and can be transformed to a well-formed IETF BCP 47 language tag 2382 // * without losing information. 2383 // * 2384 // * <p><b>Note:</b> The <code>Locale</code> class does not provide any 2385 // * syntactic restrictions on variant, while BCP 47 requires each variant 2386 // * subtag to be 5 to 8 alphanumerics or a single numeric followed by 3 2387 // * alphanumerics. The method <code>setVariant</code> throws 2388 // * <code>IllformedLocaleException</code> for a variant that does not satisfy 2389 // * this restriction. If it is necessary to support such a variant, use a 2390 // * Locale constructor. However, keep in mind that a <code>Locale</code> 2391 // * object created this way might lose the variant information when 2392 // * transformed to a BCP 47 language tag. 2393 // * 2394 // * <p>The following example shows how to create a <code>Locale</code> object 2395 // * with the <code>Builder</code>. 2396 // * <blockquote> 2397 // * <pre> 2398 // * Locale aLocale = new Builder().setLanguage("sr").setScript("Latn").setRegion("RS").build(); 2399 // * </pre> 2400 // * </blockquote> 2401 // * 2402 // * <p>Builders can be reused; <code>clear()</code> resets all 2403 // * fields to their default values. 2404 // * 2405 // * @see Locale#forLanguageTag 2406 // */ 2407 // static final class Builder { 2408 // private final InternalLocaleBuilder localeBuilder; 2409 2410 // /** 2411 // * Constructs an empty Builder. The default value of all 2412 // * fields, extensions, and private use information is the 2413 // * empty string. 2414 // */ 2415 // Builder() { 2416 // localeBuilder = new InternalLocaleBuilder(); 2417 // } 2418 2419 // /** 2420 // * Resets the <code>Builder</code> to match the provided 2421 // * <code>locale</code>. Existing state is discarded. 2422 // * 2423 // * <p>All fields of the locale must be well-formed, see {@link Locale}. 2424 // * 2425 // * <p>Locales with any ill-formed fields cause 2426 // * <code>IllformedLocaleException</code> to be thrown, except for the 2427 // * following three cases which are accepted for compatibility 2428 // * reasons:<ul> 2429 // * <li>Locale("ja", "JP", "JP") is treated as "ja-JP-u-ca-japanese" 2430 // * <li>Locale("th", "TH", "TH") is treated as "th-TH-u-nu-thai" 2431 // * <li>Locale("no", "NO", "NY") is treated as "nn-NO"</ul> 2432 // * 2433 // * @param locale the locale 2434 // * @return This builder. 2435 // * @throws IllformedLocaleException if <code>locale</code> has 2436 // * any ill-formed fields. 2437 // * @throws NullPointerException if <code>locale</code> is null. 2438 // */ 2439 // Builder setLocale(Locale locale) { 2440 // try { 2441 // localeBuilder.setLocale(locale.baseLocale, locale.localeExtensions); 2442 // } catch (LocaleSyntaxException e) { 2443 // throw new IllformedLocaleException(e.getMessage(), e.getErrorIndex()); 2444 // } 2445 // return this; 2446 // } 2447 2448 // /** 2449 // * Resets the Builder to match the provided IETF BCP 47 2450 // * language tag. Discards the existing state. Null and the 2451 // * empty string cause the builder to be reset, like {@link 2452 // * #clear}. Grandfathered tags (see {@link 2453 // * Locale#forLanguageTag}) are converted to their canonical 2454 // * form before being processed. Otherwise, the language tag 2455 // * must be well-formed (see {@link Locale}) or an exception is 2456 // * thrown (unlike <code>Locale.forLanguageTag</code>, which 2457 // * just discards ill-formed and following portions of the 2458 // * tag). 2459 // * 2460 // * @param languageTag the language tag 2461 // * @return This builder. 2462 // * @throws IllformedLocaleException if <code>languageTag</code> is ill-formed 2463 // * @see Locale#forLanguageTag(string) 2464 // */ 2465 // Builder setLanguageTag(string languageTag) { 2466 // ParseStatus sts = new ParseStatus(); 2467 // LanguageTag tag = LanguageTag.parse(languageTag, sts); 2468 // if (sts.isError()) { 2469 // throw new IllformedLocaleException(sts.getErrorMessage(), sts.getErrorIndex()); 2470 // } 2471 // localeBuilder.setLanguageTag(tag); 2472 // return this; 2473 // } 2474 2475 // /** 2476 // * Sets the language. If <code>language</code> is the empty string or 2477 // * null, the language in this <code>Builder</code> is removed. Otherwise, 2478 // * the language must be <a href="./Locale.html#def_language">well-formed</a> 2479 // * or an exception is thrown. 2480 // * 2481 // * <p>The typical language value is a two or three-letter language 2482 // * code as defined in ISO639. 2483 // * 2484 // * @param language the language 2485 // * @return This builder. 2486 // * @throws IllformedLocaleException if <code>language</code> is ill-formed 2487 // */ 2488 // Builder setLanguage(string language) { 2489 // try { 2490 // localeBuilder.setLanguage(language); 2491 // } catch (LocaleSyntaxException e) { 2492 // throw new IllformedLocaleException(e.getMessage(), e.getErrorIndex()); 2493 // } 2494 // return this; 2495 // } 2496 2497 // /** 2498 // * Sets the script. If <code>script</code> is null or the empty string, 2499 // * the script in this <code>Builder</code> is removed. 2500 // * Otherwise, the script must be <a href="./Locale.html#def_script">well-formed</a> or an 2501 // * exception is thrown. 2502 // * 2503 // * <p>The typical script value is a four-letter script code as defined by ISO 15924. 2504 // * 2505 // * @param script the script 2506 // * @return This builder. 2507 // * @throws IllformedLocaleException if <code>script</code> is ill-formed 2508 // */ 2509 // Builder setScript(string script) { 2510 // try { 2511 // localeBuilder.setScript(script); 2512 // } catch (LocaleSyntaxException e) { 2513 // throw new IllformedLocaleException(e.getMessage(), e.getErrorIndex()); 2514 // } 2515 // return this; 2516 // } 2517 2518 // /** 2519 // * Sets the region. If region is null or the empty string, the region 2520 // * in this <code>Builder</code> is removed. Otherwise, 2521 // * the region must be <a href="./Locale.html#def_region">well-formed</a> or an 2522 // * exception is thrown. 2523 // * 2524 // * <p>The typical region value is a two-letter ISO 3166 code or a 2525 // * three-digit UN M.49 area code. 2526 // * 2527 // * <p>The country value in the <code>Locale</code> created by the 2528 // * <code>Builder</code> is always normalized to upper case. 2529 // * 2530 // * @param region the region 2531 // * @return This builder. 2532 // * @throws IllformedLocaleException if <code>region</code> is ill-formed 2533 // */ 2534 // Builder setRegion(string region) { 2535 // try { 2536 // localeBuilder.setRegion(region); 2537 // } catch (LocaleSyntaxException e) { 2538 // throw new IllformedLocaleException(e.getMessage(), e.getErrorIndex()); 2539 // } 2540 // return this; 2541 // } 2542 2543 // /** 2544 // * Sets the variant. If variant is null or the empty string, the 2545 // * variant in this <code>Builder</code> is removed. Otherwise, it 2546 // * must consist of one or more <a href="./Locale.html#def_variant">well-formed</a> 2547 // * subtags, or an exception is thrown. 2548 // * 2549 // * <p><b>Note:</b> This method checks if <code>variant</code> 2550 // * satisfies the IETF BCP 47 variant subtag's syntax requirements, 2551 // * and normalizes the value to lowercase letters. However, 2552 // * the <code>Locale</code> class does not impose any syntactic 2553 // * restriction on variant, and the variant value in 2554 // * <code>Locale</code> is case sensitive. To set such a variant, 2555 // * use a Locale constructor. 2556 // * 2557 // * @param variant the variant 2558 // * @return This builder. 2559 // * @throws IllformedLocaleException if <code>variant</code> is ill-formed 2560 // */ 2561 // Builder setVariant(string variant) { 2562 // try { 2563 // localeBuilder.setVariant(variant); 2564 // } catch (LocaleSyntaxException e) { 2565 // throw new IllformedLocaleException(e.getMessage(), e.getErrorIndex()); 2566 // } 2567 // return this; 2568 // } 2569 2570 // /** 2571 // * Sets the extension for the given key. If the value is null or the 2572 // * empty string, the extension is removed. Otherwise, the extension 2573 // * must be <a href="./Locale.html#def_extensions">well-formed</a> or an exception 2574 // * is thrown. 2575 // * 2576 // * <p><b>Note:</b> The key {@link Locale#UNICODE_LOCALE_EXTENSION 2577 // * UNICODE_LOCALE_EXTENSION} ('u') is used for the Unicode locale extension. 2578 // * Setting a value for this key replaces any existing Unicode locale key/type 2579 // * pairs with those defined in the extension. 2580 // * 2581 // * <p><b>Note:</b> The key {@link Locale#PRIVATE_USE_EXTENSION 2582 // * PRIVATE_USE_EXTENSION} ('x') is used for the private use code. To be 2583 // * well-formed, the value for this key needs only to have subtags of one to 2584 // * eight alphanumeric characters, not two to eight as in the general case. 2585 // * 2586 // * @param key the extension key 2587 // * @param value the extension value 2588 // * @return This builder. 2589 // * @throws IllformedLocaleException if <code>key</code> is illegal 2590 // * or <code>value</code> is ill-formed 2591 // * @see #setUnicodeLocaleKeyword(string, string) 2592 // */ 2593 // Builder setExtension(char key, string value) { 2594 // try { 2595 // localeBuilder.setExtension(key, value); 2596 // } catch (LocaleSyntaxException e) { 2597 // throw new IllformedLocaleException(e.getMessage(), e.getErrorIndex()); 2598 // } 2599 // return this; 2600 // } 2601 2602 // /** 2603 // * Sets the Unicode locale keyword type for the given key. If the type 2604 // * is null, the Unicode keyword is removed. Otherwise, the key must be 2605 // * non-null and both key and type must be <a 2606 // * href="./Locale.html#def_locale_extension">well-formed</a> or an exception 2607 // * is thrown. 2608 // * 2609 // * <p>Keys and types are converted to lower case. 2610 // * 2611 // * <p><b>Note</b>:Setting the 'u' extension via {@link #setExtension} 2612 // * replaces all Unicode locale keywords with those defined in the 2613 // * extension. 2614 // * 2615 // * @param key the Unicode locale key 2616 // * @param type the Unicode locale type 2617 // * @return This builder. 2618 // * @throws IllformedLocaleException if <code>key</code> or <code>type</code> 2619 // * is ill-formed 2620 // * @throws NullPointerException if <code>key</code> is null 2621 // * @see #setExtension(char, string) 2622 // */ 2623 // Builder setUnicodeLocaleKeyword(string key, string type) { 2624 // try { 2625 // localeBuilder.setUnicodeLocaleKeyword(key, type); 2626 // } catch (LocaleSyntaxException e) { 2627 // throw new IllformedLocaleException(e.getMessage(), e.getErrorIndex()); 2628 // } 2629 // return this; 2630 // } 2631 2632 // /** 2633 // * Adds a unicode locale attribute, if not already present, otherwise 2634 // * has no effect. The attribute must not be null and must be <a 2635 // * href="./Locale.html#def_locale_extension">well-formed</a> or an exception 2636 // * is thrown. 2637 // * 2638 // * @param attribute the attribute 2639 // * @return This builder. 2640 // * @throws NullPointerException if <code>attribute</code> is null 2641 // * @throws IllformedLocaleException if <code>attribute</code> is ill-formed 2642 // * @see #setExtension(char, string) 2643 // */ 2644 // Builder addUnicodeLocaleAttribute(string attribute) { 2645 // try { 2646 // localeBuilder.addUnicodeLocaleAttribute(attribute); 2647 // } catch (LocaleSyntaxException e) { 2648 // throw new IllformedLocaleException(e.getMessage(), e.getErrorIndex()); 2649 // } 2650 // return this; 2651 // } 2652 2653 // /** 2654 // * Removes a unicode locale attribute, if present, otherwise has no 2655 // * effect. The attribute must not be null and must be <a 2656 // * href="./Locale.html#def_locale_extension">well-formed</a> or an exception 2657 // * is thrown. 2658 // * 2659 // * <p>Attribute comparison for removal is case-insensitive. 2660 // * 2661 // * @param attribute the attribute 2662 // * @return This builder. 2663 // * @throws NullPointerException if <code>attribute</code> is null 2664 // * @throws IllformedLocaleException if <code>attribute</code> is ill-formed 2665 // * @see #setExtension(char, string) 2666 // */ 2667 // Builder removeUnicodeLocaleAttribute(string attribute) { 2668 // assert(attribute); 2669 // try { 2670 // localeBuilder.removeUnicodeLocaleAttribute(attribute); 2671 // } catch (LocaleSyntaxException e) { 2672 // throw new IllformedLocaleException(e.getMessage(), e.getErrorIndex()); 2673 // } 2674 // return this; 2675 // } 2676 2677 // /** 2678 // * Resets the builder to its initial, empty state. 2679 // * 2680 // * @return This builder. 2681 // */ 2682 // Builder clear() { 2683 // localeBuilder.clear(); 2684 // return this; 2685 // } 2686 2687 // /** 2688 // * Resets the extensions to their initial, empty state. 2689 // * Language, script, region and variant are unchanged. 2690 // * 2691 // * @return This builder. 2692 // * @see #setExtension(char, string) 2693 // */ 2694 // Builder clearExtensions() { 2695 // localeBuilder.clearExtensions(); 2696 // return this; 2697 // } 2698 2699 // /** 2700 // * Returns an instance of <code>Locale</code> created from the fields set 2701 // * on this builder. 2702 // * 2703 // * <p>This applies the conversions listed in {@link Locale#forLanguageTag} 2704 // * when constructing a Locale. (Grandfathered tags are handled in 2705 // * {@link #setLanguageTag}.) 2706 // * 2707 // * @return A Locale. 2708 // */ 2709 // Locale build() { 2710 // BaseLocale baseloc = localeBuilder.getBaseLocale(); 2711 // LocaleExtensions extensions = localeBuilder.getLocaleExtensions(); 2712 // if (extensions is null && baseloc.getVariant().length() > 0) { 2713 // extensions = getCompatibilityExtensions(baseloc.getLanguage(), baseloc.getScript(), 2714 // baseloc.getRegion(), baseloc.getVariant()); 2715 // } 2716 // return Locale.getInstance(baseloc, extensions); 2717 // } 2718 // } 2719 2720 // /** 2721 // * This enum provides constants to select a filtering mode for locale 2722 // * matching. Refer to <a href="http://tools.ietf.org/html/rfc4647">RFC 4647 2723 // * Matching of Language Tags</a> for details. 2724 // * 2725 // * <p>As an example, think of two Language Priority Lists each of which 2726 // * includes only one language range and a set of following language tags: 2727 // * 2728 // * <pre> 2729 // * de (German) 2730 // * de-DE (German, Germany) 2731 // * de-Deva (German, in Devanagari script) 2732 // * de-Deva-DE (German, in Devanagari script, Germany) 2733 // * de-DE-1996 (German, Germany, orthography of 1996) 2734 // * de-Latn-DE (German, in Latin script, Germany) 2735 // * de-Latn-DE-1996 (German, in Latin script, Germany, orthography of 1996) 2736 // * </pre> 2737 // * 2738 // * The filtering method will behave as follows: 2739 // * 2740 // * <table class="striped"> 2741 // * <caption>Filtering method behavior</caption> 2742 // * <thead> 2743 // * <tr> 2744 // * <th scope="col">Filtering Mode</th> 2745 // * <th scope="col">Language Priority List: {@code "de-DE"}</th> 2746 // * <th scope="col">Language Priority List: {@code "de-*-DE"}</th> 2747 // * </tr> 2748 // * </thead> 2749 // * <tbody> 2750 // * <tr> 2751 // * <th scope="row" style="vertical-align:top"> 2752 // * {@link FilteringMode#AUTOSELECT_FILTERING AUTOSELECT_FILTERING} 2753 // * </th> 2754 // * <td style="vertical-align:top"> 2755 // * Performs <em>basic</em> filtering and returns {@code "de-DE"} and 2756 // * {@code "de-DE-1996"}. 2757 // * </td> 2758 // * <td style="vertical-align:top"> 2759 // * Performs <em>extended</em> filtering and returns {@code "de-DE"}, 2760 // * {@code "de-Deva-DE"}, {@code "de-DE-1996"}, {@code "de-Latn-DE"}, and 2761 // * {@code "de-Latn-DE-1996"}. 2762 // * </td> 2763 // * </tr> 2764 // * <tr> 2765 // * <th scope="row" style="vertical-align:top"> 2766 // * {@link FilteringMode#EXTENDED_FILTERING EXTENDED_FILTERING} 2767 // * </th> 2768 // * <td style="vertical-align:top"> 2769 // * Performs <em>extended</em> filtering and returns {@code "de-DE"}, 2770 // * {@code "de-Deva-DE"}, {@code "de-DE-1996"}, {@code "de-Latn-DE"}, and 2771 // * {@code "de-Latn-DE-1996"}. 2772 // * </td> 2773 // * <td style="vertical-align:top">Same as above.</td> 2774 // * </tr> 2775 // * <tr> 2776 // * <th scope="row" style="vertical-align:top"> 2777 // * {@link FilteringMode#IGNORE_EXTENDED_RANGES IGNORE_EXTENDED_RANGES} 2778 // * </th> 2779 // * <td style="vertical-align:top"> 2780 // * Performs <em>basic</em> filtering and returns {@code "de-DE"} and 2781 // * {@code "de-DE-1996"}. 2782 // * </td> 2783 // * <td style="vertical-align:top"> 2784 // * Performs <em>basic</em> filtering and returns {@code null} because 2785 // * nothing matches. 2786 // * </td> 2787 // * </tr> 2788 // * <tr> 2789 // * <th scope="row" style="vertical-align:top"> 2790 // * {@link FilteringMode#MAP_EXTENDED_RANGES MAP_EXTENDED_RANGES} 2791 // * </th> 2792 // * <td style="vertical-align:top">Same as above.</td> 2793 // * <td style="vertical-align:top"> 2794 // * Performs <em>basic</em> filtering and returns {@code "de-DE"} and 2795 // * {@code "de-DE-1996"} because {@code "de-*-DE"} is mapped to 2796 // * {@code "de-DE"}. 2797 // * </td> 2798 // * </tr> 2799 // * <tr> 2800 // * <th scope="row" style="vertical-align:top"> 2801 // * {@link FilteringMode#REJECT_EXTENDED_RANGES REJECT_EXTENDED_RANGES} 2802 // * </th> 2803 // * <td style="vertical-align:top">Same as above.</td> 2804 // * <td style="vertical-align:top"> 2805 // * Throws {@link IllegalArgumentException} because {@code "de-*-DE"} is 2806 // * not a valid basic language range. 2807 // * </td> 2808 // * </tr> 2809 // * </tbody> 2810 // * </table> 2811 // * 2812 // * @see #filter(List, Collection, FilteringMode) 2813 // * @see #filterTags(List, Collection, FilteringMode) 2814 // * 2815 // */ 2816 // static enum FilteringMode { 2817 // /** 2818 // * Specifies automatic filtering mode based on the given Language 2819 // * Priority List consisting of language ranges. If all of the ranges 2820 // * are basic, basic filtering is selected. Otherwise, extended 2821 // * filtering is selected. 2822 // */ 2823 // AUTOSELECT_FILTERING, 2824 2825 // /** 2826 // * Specifies extended filtering. 2827 // */ 2828 // EXTENDED_FILTERING, 2829 2830 // /** 2831 // * Specifies basic filtering: Note that any extended language ranges 2832 // * included in the given Language Priority List are ignored. 2833 // */ 2834 // IGNORE_EXTENDED_RANGES, 2835 2836 // /** 2837 // * Specifies basic filtering: If any extended language ranges are 2838 // * included in the given Language Priority List, they are mapped to the 2839 // * basic language range. Specifically, a language range starting with a 2840 // * subtag {@code "*"} is treated as a language range {@code "*"}. For 2841 // * example, {@code "*-US"} is treated as {@code "*"}. If {@code "*"} is 2842 // * not the first subtag, {@code "*"} and extra {@code "-"} are removed. 2843 // * For example, {@code "ja-*-JP"} is mapped to {@code "ja-JP"}. 2844 // */ 2845 // MAP_EXTENDED_RANGES, 2846 2847 // /** 2848 // * Specifies basic filtering: If any extended language ranges are 2849 // * included in the given Language Priority List, the list is rejected 2850 // * and the filtering method throws {@link IllegalArgumentException}. 2851 // */ 2852 // REJECT_EXTENDED_RANGES 2853 // }; 2854 2855 // /** 2856 // * This class expresses a <em>Language Range</em> defined in 2857 // * <a href="http://tools.ietf.org/html/rfc4647">RFC 4647 Matching of 2858 // * Language Tags</a>. A language range is an identifier which is used to 2859 // * select language tag(s) meeting specific requirements by using the 2860 // * mechanisms described in <a href="Locale.html#LocaleMatching">Locale 2861 // * Matching</a>. A list which represents a user's preferences and consists 2862 // * of language ranges is called a <em>Language Priority List</em>. 2863 // * 2864 // * <p>There are two types of language ranges: basic and extended. In RFC 2865 // * 4647, the syntax of language ranges is expressed in 2866 // * <a href="http://tools.ietf.org/html/rfc4234">ABNF</a> as follows: 2867 // * <blockquote> 2868 // * <pre> 2869 // * basic-language-range = (1*8ALPHA *("-" 1*8alphanum)) / "*" 2870 // * extended-language-range = (1*8ALPHA / "*") 2871 // * *("-" (1*8alphanum / "*")) 2872 // * alphanum = ALPHA / DIGIT 2873 // * </pre> 2874 // * </blockquote> 2875 // * For example, {@code "en"} (English), {@code "ja-JP"} (Japanese, Japan), 2876 // * {@code "*"} (special language range which matches any language tag) are 2877 // * basic language ranges, whereas {@code "*-CH"} (any languages, 2878 // * Switzerland), {@code "es-*"} (Spanish, any regions), and 2879 // * {@code "zh-Hant-*"} (Traditional Chinese, any regions) are extended 2880 // * language ranges. 2881 // * 2882 // * @see #filter 2883 // * @see #filterTags 2884 // * @see #lookup 2885 // * @see #lookupTag 2886 // * 2887 // */ 2888 // static final class LanguageRange { 2889 2890 // /** 2891 // * A constant holding the maximum value of weight, 1.0, which indicates 2892 // * that the language range is a good fit for the user. 2893 // */ 2894 // static final double MAX_WEIGHT = 1.0; 2895 2896 // /** 2897 // * A constant holding the minimum value of weight, 0.0, which indicates 2898 // * that the language range is not a good fit for the user. 2899 // */ 2900 // static final double MIN_WEIGHT = 0.0; 2901 2902 // private final string range; 2903 // private final double weight; 2904 2905 // private int hash; 2906 2907 // /** 2908 // * Constructs a {@code LanguageRange} using the given {@code range}. 2909 // * Note that no validation is done against the IANA Language Subtag 2910 // * Registry at time of construction. 2911 // * 2912 // * <p>This is equivalent to {@code LanguageRange(range, MAX_WEIGHT)}. 2913 // * 2914 // * @param range a language range 2915 // * @throws NullPointerException if the given {@code range} is 2916 // * {@code null} 2917 // * @throws IllegalArgumentException if the given {@code range} does not 2918 // * comply with the syntax of the language range mentioned in RFC 4647 2919 // */ 2920 // LanguageRange(string range) { 2921 // this(range, MAX_WEIGHT); 2922 // } 2923 2924 // /** 2925 // * Constructs a {@code LanguageRange} using the given {@code range} and 2926 // * {@code weight}. Note that no validation is done against the IANA 2927 // * Language Subtag Registry at time of construction. 2928 // * 2929 // * @param range a language range 2930 // * @param weight a weight value between {@code MIN_WEIGHT} and 2931 // * {@code MAX_WEIGHT} 2932 // * @throws NullPointerException if the given {@code range} is 2933 // * {@code null} 2934 // * @throws IllegalArgumentException if the given {@code range} does not 2935 // * comply with the syntax of the language range mentioned in RFC 4647 2936 // * or if the given {@code weight} is less than {@code MIN_WEIGHT} 2937 // * or greater than {@code MAX_WEIGHT} 2938 // */ 2939 // LanguageRange(string range, double weight) { 2940 // if (range is null) { 2941 // throw new NullPointerException(); 2942 // } 2943 // if (weight < MIN_WEIGHT || weight > MAX_WEIGHT) { 2944 // throw new IllegalArgumentException("weight=" ~ weight); 2945 // } 2946 2947 // range = range.toLowerCase(Locale.ROOT); 2948 2949 // // Do syntax check. 2950 // bool isIllFormed = false; 2951 // string[] subtags = range.split("-"); 2952 // if (isSubtagIllFormed(subtags[0], true) 2953 // || range.endsWith("-")) { 2954 // isIllFormed = true; 2955 // } else { 2956 // for (int i = 1; i < subtags.length; i++) { 2957 // if (isSubtagIllFormed(subtags[i], false)) { 2958 // isIllFormed = true; 2959 // break; 2960 // } 2961 // } 2962 // } 2963 // if (isIllFormed) { 2964 // throw new IllegalArgumentException("range=" ~ range); 2965 // } 2966 2967 // this.range = range; 2968 // this.weight = weight; 2969 // } 2970 2971 // private static bool isSubtagIllFormed(string subtag, 2972 // bool isFirstSubtag) { 2973 // if (subtag.equals("") || subtag.length() > 8) { 2974 // return true; 2975 // } else if (subtag.equals("*")) { 2976 // return false; 2977 // } 2978 // char[] charArray = subtag.toCharArray(); 2979 // if (isFirstSubtag) { // ALPHA 2980 // for (char c : charArray) { 2981 // if (c < 'a' || c > 'z') { 2982 // return true; 2983 // } 2984 // } 2985 // } else { // ALPHA / DIGIT 2986 // for (char c : charArray) { 2987 // if (c < '0' || (c > '9' && c < 'a') || c > 'z') { 2988 // return true; 2989 // } 2990 // } 2991 // } 2992 // return false; 2993 // } 2994 2995 // /** 2996 // * Returns the language range of this {@code LanguageRange}. 2997 // * 2998 // * @return the language range. 2999 // */ 3000 // string getRange() { 3001 // return range; 3002 // } 3003 3004 // /** 3005 // * Returns the weight of this {@code LanguageRange}. 3006 // * 3007 // * @return the weight value. 3008 // */ 3009 // double getWeight() { 3010 // return weight; 3011 // } 3012 3013 // /** 3014 // * Parses the given {@code ranges} to generate a Language Priority List. 3015 // * 3016 // * <p>This method performs a syntactic check for each language range in 3017 // * the given {@code ranges} but doesn't do validation using the IANA 3018 // * Language Subtag Registry. 3019 // * 3020 // * <p>The {@code ranges} to be given can take one of the following 3021 // * forms: 3022 // * 3023 // * <pre> 3024 // * "Accept-Language: ja,en;q=0.4" (weighted list with Accept-Language prefix) 3025 // * "ja,en;q=0.4" (weighted list) 3026 // * "ja,en" (prioritized list) 3027 // * </pre> 3028 // * 3029 // * In a weighted list, each language range is given a weight value. 3030 // * The weight value is identical to the "quality value" in 3031 // * <a href="http://tools.ietf.org/html/rfc2616">RFC 2616</a>, and it 3032 // * expresses how much the user prefers the language. A weight value is 3033 // * specified after a corresponding language range followed by 3034 // * {@code ";q="}, and the default weight value is {@code MAX_WEIGHT} 3035 // * when it is omitted. 3036 // * 3037 // * <p>Unlike a weighted list, language ranges in a prioritized list 3038 // * are sorted in the descending order based on its priority. The first 3039 // * language range has the highest priority and meets the user's 3040 // * preference most. 3041 // * 3042 // * <p>In either case, language ranges are sorted in descending order in 3043 // * the Language Priority List based on priority or weight. If a 3044 // * language range appears in the given {@code ranges} more than once, 3045 // * only the first one is included on the Language Priority List. 3046 // * 3047 // * <p>The returned list consists of language ranges from the given 3048 // * {@code ranges} and their equivalents found in the IANA Language 3049 // * Subtag Registry. For example, if the given {@code ranges} is 3050 // * {@code "Accept-Language: iw,en-us;q=0.7,en;q=0.3"}, the elements in 3051 // * the list to be returned are: 3052 // * 3053 // * <pre> 3054 // * <b>Range</b> <b>Weight</b> 3055 // * "iw" (older tag for Hebrew) 1.0 3056 // * "he" (new preferred code for Hebrew) 1.0 3057 // * "en-us" (English, United States) 0.7 3058 // * "en" (English) 0.3 3059 // * </pre> 3060 // * 3061 // * Two language ranges, {@code "iw"} and {@code "he"}, have the same 3062 // * highest priority in the list. By adding {@code "he"} to the user's 3063 // * Language Priority List, locale-matching method can find Hebrew as a 3064 // * matching locale (or language tag) even if the application or system 3065 // * offers only {@code "he"} as a supported locale (or language tag). 3066 // * 3067 // * @param ranges a list of comma-separated language ranges or a list of 3068 // * language ranges in the form of the "Accept-Language" header 3069 // * defined in <a href="http://tools.ietf.org/html/rfc2616">RFC 3070 // * 2616</a> 3071 // * @return a Language Priority List consisting of language ranges 3072 // * included in the given {@code ranges} and their equivalent 3073 // * language ranges if available. The list is modifiable. 3074 // * @throws NullPointerException if {@code ranges} is null 3075 // * @throws IllegalArgumentException if a language range or a weight 3076 // * found in the given {@code ranges} is ill-formed 3077 // */ 3078 // static List<LanguageRange> parse(string ranges) { 3079 // return LocaleMatcher.parse(ranges); 3080 // } 3081 3082 // /** 3083 // * Parses the given {@code ranges} to generate a Language Priority 3084 // * List, and then customizes the list using the given {@code map}. 3085 // * This method is equivalent to 3086 // * {@code mapEquivalents(parse(ranges), map)}. 3087 // * 3088 // * @param ranges a list of comma-separated language ranges or a list 3089 // * of language ranges in the form of the "Accept-Language" header 3090 // * defined in <a href="http://tools.ietf.org/html/rfc2616">RFC 3091 // * 2616</a> 3092 // * @param map a map containing information to customize language ranges 3093 // * @return a Language Priority List with customization. The list is 3094 // * modifiable. 3095 // * @throws NullPointerException if {@code ranges} is null 3096 // * @throws IllegalArgumentException if a language range or a weight 3097 // * found in the given {@code ranges} is ill-formed 3098 // * @see #parse(string) 3099 // * @see #mapEquivalents 3100 // */ 3101 // static List<LanguageRange> parse(string ranges, 3102 // Map<string, List<string>> map) { 3103 // return mapEquivalents(parse(ranges), map); 3104 // } 3105 3106 // /** 3107 // * Generates a new customized Language Priority List using the given 3108 // * {@code priorityList} and {@code map}. If the given {@code map} is 3109 // * empty, this method returns a copy of the given {@code priorityList}. 3110 // * 3111 // * <p>In the map, a key represents a language range whereas a value is 3112 // * a list of equivalents of it. {@code '*'} cannot be used in the map. 3113 // * Each equivalent language range has the same weight value as its 3114 // * original language range. 3115 // * 3116 // * <pre> 3117 // * An example of map: 3118 // * <b>Key</b> <b>Value</b> 3119 // * "zh" (Chinese) "zh", 3120 // * "zh-Hans"(Simplified Chinese) 3121 // * "zh-HK" (Chinese, Hong Kong) "zh-HK" 3122 // * "zh-TW" (Chinese, Taiwan) "zh-TW" 3123 // * </pre> 3124 // * 3125 // * The customization is performed after modification using the IANA 3126 // * Language Subtag Registry. 3127 // * 3128 // * <p>For example, if a user's Language Priority List consists of five 3129 // * language ranges ({@code "zh"}, {@code "zh-CN"}, {@code "en"}, 3130 // * {@code "zh-TW"}, and {@code "zh-HK"}), the newly generated Language 3131 // * Priority List which is customized using the above map example will 3132 // * consists of {@code "zh"}, {@code "zh-Hans"}, {@code "zh-CN"}, 3133 // * {@code "zh-Hans-CN"}, {@code "en"}, {@code "zh-TW"}, and 3134 // * {@code "zh-HK"}. 3135 // * 3136 // * <p>{@code "zh-HK"} and {@code "zh-TW"} aren't converted to 3137 // * {@code "zh-Hans-HK"} nor {@code "zh-Hans-TW"} even if they are 3138 // * included in the Language Priority List. In this example, mapping 3139 // * is used to clearly distinguish Simplified Chinese and Traditional 3140 // * Chinese. 3141 // * 3142 // * <p>If the {@code "zh"}-to-{@code "zh"} mapping isn't included in the 3143 // * map, a simple replacement will be performed and the customized list 3144 // * won't include {@code "zh"} and {@code "zh-CN"}. 3145 // * 3146 // * @param priorityList user's Language Priority List 3147 // * @param map a map containing information to customize language ranges 3148 // * @return a new Language Priority List with customization. The list is 3149 // * modifiable. 3150 // * @throws NullPointerException if {@code priorityList} is {@code null} 3151 // * @see #parse(string, Map) 3152 // */ 3153 // static List<LanguageRange> mapEquivalents( 3154 // List<LanguageRange>priorityList, 3155 // Map<string, List<string>> map) { 3156 // return LocaleMatcher.mapEquivalents(priorityList, map); 3157 // } 3158 3159 // /** 3160 // * Returns a hash code value for the object. 3161 // * 3162 // * @return a hash code value for this object. 3163 // */ 3164 // override 3165 // int hashCode() { 3166 // int h = hash; 3167 // if (h == 0) { 3168 // h = 17; 3169 // h = 37*h + range.hashCode(); 3170 // long bitsWeight = Double.doubleToLongBits(weight); 3171 // h = 37*h + (int)(bitsWeight ^ (bitsWeight >>> 32)); 3172 // if (h != 0) { 3173 // hash = h; 3174 // } 3175 // } 3176 // return h; 3177 // } 3178 3179 // /** 3180 // * Compares this object to the specified object. The result is true if 3181 // * and only if the argument is not {@code null} and is a 3182 // * {@code LanguageRange} object that contains the same {@code range} 3183 // * and {@code weight} values as this object. 3184 // * 3185 // * @param obj the object to compare with 3186 // * @return {@code true} if this object's {@code range} and 3187 // * {@code weight} are the same as the {@code obj}'s; {@code false} 3188 // * otherwise. 3189 // */ 3190 // override 3191 // bool equals(Object obj) { 3192 // if (this is obj) { 3193 // return true; 3194 // } 3195 // if (!(obj instanceof LanguageRange)) { 3196 // return false; 3197 // } 3198 // LanguageRange other = (LanguageRange)obj; 3199 // return hash == other.hash 3200 // && range.equals(other.range) 3201 // && weight == other.weight; 3202 // } 3203 3204 // /** 3205 // * Returns an informative string representation of this {@code LanguageRange} 3206 // * object, consisting of language range and weight if the range is 3207 // * weighted and the weight is less than the max weight. 3208 // * 3209 // * @return a string representation of this {@code LanguageRange} object. 3210 // */ 3211 // override 3212 // string toString() { 3213 // return (weight == MAX_WEIGHT) ? range : range ~ ";q=" ~ weight; 3214 // } 3215 // } 3216 3217 // /** 3218 // * Returns a list of matching {@code Locale} instances using the filtering 3219 // * mechanism defined in RFC 4647. 3220 // * 3221 // * This filter operation on the given {@code locales} ensures that only 3222 // * unique matching locale(s) are returned. 3223 // * 3224 // * @param priorityList user's Language Priority List in which each language 3225 // * tag is sorted in descending order based on priority or weight 3226 // * @param locales {@code Locale} instances used for matching 3227 // * @param mode filtering mode 3228 // * @return a list of {@code Locale} instances for matching language tags 3229 // * sorted in descending order based on priority or weight, or an empty 3230 // * list if nothing matches. The list is modifiable. 3231 // * @throws NullPointerException if {@code priorityList} or {@code locales} 3232 // * is {@code null} 3233 // * @throws IllegalArgumentException if one or more extended language ranges 3234 // * are included in the given list when 3235 // * {@link FilteringMode#REJECT_EXTENDED_RANGES} is specified 3236 // * 3237 // */ 3238 // static List<Locale> filter(List<LanguageRange> priorityList, 3239 // Collection<Locale> locales, 3240 // FilteringMode mode) { 3241 // return LocaleMatcher.filter(priorityList, locales, mode); 3242 // } 3243 3244 // /** 3245 // * Returns a list of matching {@code Locale} instances using the filtering 3246 // * mechanism defined in RFC 4647. This is equivalent to 3247 // * {@link #filter(List, Collection, FilteringMode)} when {@code mode} is 3248 // * {@link FilteringMode#AUTOSELECT_FILTERING}. 3249 // * 3250 // * This filter operation on the given {@code locales} ensures that only 3251 // * unique matching locale(s) are returned. 3252 // * 3253 // * @param priorityList user's Language Priority List in which each language 3254 // * tag is sorted in descending order based on priority or weight 3255 // * @param locales {@code Locale} instances used for matching 3256 // * @return a list of {@code Locale} instances for matching language tags 3257 // * sorted in descending order based on priority or weight, or an empty 3258 // * list if nothing matches. The list is modifiable. 3259 // * @throws NullPointerException if {@code priorityList} or {@code locales} 3260 // * is {@code null} 3261 // * 3262 // */ 3263 // static List<Locale> filter(List<LanguageRange> priorityList, 3264 // Collection<Locale> locales) { 3265 // return filter(priorityList, locales, FilteringMode.AUTOSELECT_FILTERING); 3266 // } 3267 3268 // /** 3269 // * Returns a list of matching languages tags using the basic filtering 3270 // * mechanism defined in RFC 4647. 3271 // * 3272 // * This filter operation on the given {@code tags} ensures that only 3273 // * unique matching tag(s) are returned with preserved case. In case of 3274 // * duplicate matching tags with the case difference, the first matching 3275 // * tag with preserved case is returned. 3276 // * For example, "de-ch" is returned out of the duplicate matching tags 3277 // * "de-ch" and "de-CH", if "de-ch" is checked first for matching in the 3278 // * given {@code tags}. Note that if the given {@code tags} is an unordered 3279 // * {@code Collection}, the returned matching tag out of duplicate tags is 3280 // * subject to change, depending on the implementation of the 3281 // * {@code Collection}. 3282 // * 3283 // * @param priorityList user's Language Priority List in which each language 3284 // * tag is sorted in descending order based on priority or weight 3285 // * @param tags language tags 3286 // * @param mode filtering mode 3287 // * @return a list of matching language tags sorted in descending order 3288 // * based on priority or weight, or an empty list if nothing matches. 3289 // * The list is modifiable. 3290 // * @throws NullPointerException if {@code priorityList} or {@code tags} is 3291 // * {@code null} 3292 // * @throws IllegalArgumentException if one or more extended language ranges 3293 // * are included in the given list when 3294 // * {@link FilteringMode#REJECT_EXTENDED_RANGES} is specified 3295 // * 3296 // */ 3297 // static List<string> filterTags(List<LanguageRange> priorityList, 3298 // Collection<string> tags, 3299 // FilteringMode mode) { 3300 // return LocaleMatcher.filterTags(priorityList, tags, mode); 3301 // } 3302 3303 // /** 3304 // * Returns a list of matching languages tags using the basic filtering 3305 // * mechanism defined in RFC 4647. This is equivalent to 3306 // * {@link #filterTags(List, Collection, FilteringMode)} when {@code mode} 3307 // * is {@link FilteringMode#AUTOSELECT_FILTERING}. 3308 // * 3309 // * This filter operation on the given {@code tags} ensures that only 3310 // * unique matching tag(s) are returned with preserved case. In case of 3311 // * duplicate matching tags with the case difference, the first matching 3312 // * tag with preserved case is returned. 3313 // * For example, "de-ch" is returned out of the duplicate matching tags 3314 // * "de-ch" and "de-CH", if "de-ch" is checked first for matching in the 3315 // * given {@code tags}. Note that if the given {@code tags} is an unordered 3316 // * {@code Collection}, the returned matching tag out of duplicate tags is 3317 // * subject to change, depending on the implementation of the 3318 // * {@code Collection}. 3319 // * 3320 // * @param priorityList user's Language Priority List in which each language 3321 // * tag is sorted in descending order based on priority or weight 3322 // * @param tags language tags 3323 // * @return a list of matching language tags sorted in descending order 3324 // * based on priority or weight, or an empty list if nothing matches. 3325 // * The list is modifiable. 3326 // * @throws NullPointerException if {@code priorityList} or {@code tags} is 3327 // * {@code null} 3328 // * 3329 // */ 3330 // static List<string> filterTags(List<LanguageRange> priorityList, 3331 // Collection<string> tags) { 3332 // return filterTags(priorityList, tags, FilteringMode.AUTOSELECT_FILTERING); 3333 // } 3334 3335 // /** 3336 // * Returns a {@code Locale} instance for the best-matching language 3337 // * tag using the lookup mechanism defined in RFC 4647. 3338 // * 3339 // * @param priorityList user's Language Priority List in which each language 3340 // * tag is sorted in descending order based on priority or weight 3341 // * @param locales {@code Locale} instances used for matching 3342 // * @return the best matching <code>Locale</code> instance chosen based on 3343 // * priority or weight, or {@code null} if nothing matches. 3344 // * @throws NullPointerException if {@code priorityList} or {@code tags} is 3345 // * {@code null} 3346 // * 3347 // */ 3348 // static Locale lookup(List<LanguageRange> priorityList, 3349 // Collection<Locale> locales) { 3350 // return LocaleMatcher.lookup(priorityList, locales); 3351 // } 3352 3353 // /** 3354 // * Returns the best-matching language tag using the lookup mechanism 3355 // * defined in RFC 4647. 3356 // * 3357 // * This lookup operation on the given {@code tags} ensures that the 3358 // * first matching tag with preserved case is returned. 3359 // * 3360 // * @param priorityList user's Language Priority List in which each language 3361 // * tag is sorted in descending order based on priority or weight 3362 // * @param tags language tangs used for matching 3363 // * @return the best matching language tag chosen based on priority or 3364 // * weight, or {@code null} if nothing matches. 3365 // * @throws NullPointerException if {@code priorityList} or {@code tags} is 3366 // * {@code null} 3367 // * 3368 // */ 3369 // static string lookupTag(List<LanguageRange> priorityList, 3370 // Collection<string> tags) { 3371 // return LocaleMatcher.lookupTag(priorityList, tags); 3372 // } 3373 3374 } 3375 3376 /** 3377 * Enum for locale categories. These locale categories are used to get/set 3378 * the default locale for the specific functionality represented by the 3379 * category. 3380 * 3381 * @see #getDefault(LocaleCategory) 3382 * @see #setDefault(LocaleCategory, Locale) 3383 */ 3384 struct LocaleCategory { 3385 3386 /** 3387 * Category used to represent the default locale for 3388 * displaying user interfaces. 3389 */ 3390 enum LocaleCategory DISPLAY = LocaleCategory("user.language.display", 3391 "user.script.display", 3392 "user.country.display", 3393 "user.variant.display", 3394 "user.extensions.display"); 3395 3396 /** 3397 * Category used to represent the default locale for 3398 * formatting dates, numbers, and/or currencies. 3399 */ 3400 enum LocaleCategory FORMAT = LocaleCategory("user.language.format", 3401 "user.script.format", 3402 "user.country.format", 3403 "user.variant.format", 3404 "user.extensions.format"); 3405 3406 this(string languageKey, string scriptKey, string countryKey, 3407 string variantKey, string extensionsKey) { 3408 this.languageKey = languageKey; 3409 this.scriptKey = scriptKey; 3410 this.countryKey = countryKey; 3411 this.variantKey = variantKey; 3412 this.extensionsKey = extensionsKey; 3413 } 3414 3415 string languageKey; 3416 string scriptKey; 3417 string countryKey; 3418 string variantKey; 3419 string extensionsKey; 3420 3421 // bool opEquals(const LocaleCategory h) nothrow { 3422 // return this.languageKey == h.languageKey && 3423 // // this.scriptKey == h.scriptKey && 3424 // this.countryKey == h.countryKey; 3425 // } 3426 3427 // bool opEquals(ref const LocaleCategory h) nothrow { 3428 // return this.languageKey == h.languageKey && 3429 // // this.scriptKey == h.scriptKey && 3430 // this.countryKey == h.countryKey; 3431 // } 3432 }