1 /* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package libcore.java.util; 18 19 import static java.util.Locale.FilteringMode.AUTOSELECT_FILTERING; 20 import static java.util.Locale.FilteringMode.EXTENDED_FILTERING; 21 import static java.util.Locale.FilteringMode.IGNORE_EXTENDED_RANGES; 22 import static java.util.Locale.FilteringMode.MAP_EXTENDED_RANGES; 23 import static java.util.Locale.FilteringMode.REJECT_EXTENDED_RANGES; 24 25 import java.io.ObjectInputStream; 26 import java.text.BreakIterator; 27 import java.text.Collator; 28 import java.text.DateFormat; 29 import java.text.DateFormatSymbols; 30 import java.text.DecimalFormatSymbols; 31 import java.text.NumberFormat; 32 import java.util.ArrayList; 33 import java.util.Calendar; 34 import java.util.Collections; 35 import java.util.IllformedLocaleException; 36 import java.util.List; 37 import java.util.Locale; 38 import java.util.Locale.LanguageRange; 39 import java.util.MissingResourceException; 40 41 public class LocaleTest extends junit.framework.TestCase { 42 43 public void test_extension_absent() throws Exception { 44 Locale locale = Locale.forLanguageTag("en-US"); 45 assertFalse(locale.hasExtensions()); 46 assertEquals(locale, locale.stripExtensions()); 47 } 48 49 public void test_extension_builder() throws Exception { 50 Locale.Builder b = new Locale.Builder(); 51 Locale localeWithoutExtension = b.build(); 52 b.setExtension('g', "FO_ba-BR_bg"); 53 Locale locale = b.build(); 54 assertTrue(locale.hasExtensions()); 55 assertFalse(locale.stripExtensions().hasExtensions()); 56 assertEquals(localeWithoutExtension, locale.stripExtensions()); 57 } 58 59 public void test_extension_languageTag() throws Exception { 60 Locale lA = Locale.forLanguageTag("en-Latn-US-x-foo"); 61 Locale lB = Locale.forLanguageTag("en-Latn-US"); 62 assertTrue(lA.hasExtensions()); 63 assertFalse(lB.hasExtensions()); 64 assertEquals(lB, lA.stripExtensions()); 65 } 66 67 // http://b/2611311; if there's no display language/country/variant, use the raw codes. 68 public void test_getDisplayName_invalid() throws Exception { 69 Locale invalid = new Locale("AaBbCc", "DdEeFf", "GgHhIi"); 70 71 assertEquals("aabbcc", invalid.getLanguage()); 72 assertEquals("DDEEFF", invalid.getCountry()); 73 assertEquals("GgHhIi", invalid.getVariant()); 74 75 // Android using icu4c < 49.2 returned empty strings for display language, country, 76 // and variant, but a display name made up of the raw strings. 77 // Newer releases return slightly different results, but no less unreasonable. 78 assertEquals("aabbcc", invalid.getDisplayLanguage()); 79 assertEquals("DDEEFF", invalid.getDisplayCountry()); 80 assertEquals("GGHHII", invalid.getDisplayVariant()); 81 assertEquals("aabbcc (DDEEFF,GGHHII)", invalid.getDisplayName()); 82 } 83 84 public void test_getDisplayName_emptyCodes() { 85 Locale emptyLanguage = new Locale("", "DdeEFf"); 86 assertEquals("", emptyLanguage.getDisplayLanguage()); 87 88 Locale emptyCountry = new Locale("AaBbCc", ""); 89 assertEquals("", emptyCountry.getDisplayCountry()); 90 91 Locale emptyCountryAndLanguage = new Locale("", "", "Farl"); 92 assertEquals("", emptyCountryAndLanguage.getDisplayLanguage()); 93 assertEquals("", emptyCountryAndLanguage.getDisplayCountry()); 94 assertEquals("Farl", emptyCountryAndLanguage.getDisplayVariant()); 95 } 96 97 // http://b/2611311; if there's no display language/country/variant, use the raw codes. 98 public void test_getDisplayName_unknown() throws Exception { 99 Locale unknown = new Locale("xx", "YY", "Traditional"); 100 assertEquals("xx", unknown.getLanguage()); 101 assertEquals("YY", unknown.getCountry()); 102 assertEquals("Traditional", unknown.getVariant()); 103 104 assertEquals("xx", unknown.getDisplayLanguage()); 105 assertEquals("YY", unknown.getDisplayCountry()); 106 assertEquals("Traditional", unknown.getDisplayVariant()); 107 assertEquals("xx (YY,Traditional)", unknown.getDisplayName()); 108 } 109 110 public void test_getDisplayName_easy() throws Exception { 111 assertEquals("English", Locale.ENGLISH.getDisplayLanguage(Locale.ENGLISH)); 112 assertEquals("German", Locale.GERMAN.getDisplayLanguage(Locale.ENGLISH)); 113 assertEquals("Englisch", Locale.ENGLISH.getDisplayLanguage(Locale.GERMAN)); 114 assertEquals("Deutsch", Locale.GERMAN.getDisplayLanguage(Locale.GERMAN)); 115 } 116 117 // https://b/issue?id=13790528 118 public void test_getDisplayName_withScriptsAndVariants() throws Exception { 119 // Script + Country. 120 assertEquals("Chinese (Traditional Han,China)", 121 Locale.forLanguageTag("zh-Hant-CN").getDisplayName(Locale.US)); 122 // Script + Variant. 123 assertEquals("Chinese (Traditional Han,VARIANT)", 124 Locale.forLanguageTag("zh-Hant-VARIANT").getDisplayName(Locale.US)); 125 // Country + Variant. 126 assertEquals("Chinese (China,VARIANT)", 127 Locale.forLanguageTag("zh-CN-VARIANT").getDisplayName(Locale.US)); 128 // Script + Country + variant. 129 assertEquals("Chinese (Traditional Han,China,VARIANT)", 130 Locale.forLanguageTag("zh-Hant-CN-VARIANT").getDisplayName(Locale.US)); 131 } 132 133 public void test_getDisplayCountry_8870289() throws Exception { 134 assertEquals("Hong Kong", new Locale("", "HK").getDisplayCountry(Locale.US)); 135 assertEquals("Macau", new Locale("", "MO").getDisplayCountry(Locale.US)); 136 assertEquals("Palestine", new Locale("", "PS").getDisplayCountry(Locale.US)); 137 138 assertEquals("Cocos (Keeling) Islands", new Locale("", "CC").getDisplayCountry(Locale.US)); 139 assertEquals("Falkland Islands (Islas Malvinas)", new Locale("", "FK").getDisplayCountry(Locale.US)); 140 assertEquals("Macedonia (FYROM)", new Locale("", "MK").getDisplayCountry(Locale.US)); 141 assertEquals("Myanmar (Burma)", new Locale("", "MM").getDisplayCountry(Locale.US)); 142 assertEquals("Taiwan", new Locale("", "TW").getDisplayCountry(Locale.US)); 143 } 144 145 public void test_tl_and_fil() throws Exception { 146 // In jb-mr1, we had a last-minute hack to always return "Filipino" because 147 // icu4c 4.8 didn't have any localizations for fil. (http://b/7291355). 148 // 149 // After the icu4c 4.9 upgrade, we could localize "fil" correctly, though we 150 // needed another hack to supply "fil" instead of "tl" to icu4c. (http://b/8023288). 151 // 152 // These hacks have now been reverted, so "tl" really does represent 153 // tagalog and not filipino. 154 Locale tl = new Locale("tl"); 155 Locale tl_PH = new Locale("tl", "PH"); 156 assertEquals("Tagalog", tl.getDisplayLanguage(Locale.ENGLISH)); 157 assertEquals("Tagalog", tl_PH.getDisplayLanguage(Locale.ENGLISH)); 158 assertEquals("Tagalog", tl.getDisplayLanguage(tl)); 159 assertEquals("Tagalog", tl_PH.getDisplayLanguage(tl_PH)); 160 161 Locale es_MX = new Locale("es", "MX"); 162 assertEquals("tagalo", tl.getDisplayLanguage(es_MX)); 163 assertEquals("tagalo", tl_PH.getDisplayLanguage(es_MX)); 164 165 // Assert that we can deal with "fil" correctly, since we've switched 166 // to using "fil" for Filipino, and not "tl". (http://b/15873165). 167 Locale fil = new Locale("fil"); 168 Locale fil_PH = new Locale("fil", "PH"); 169 assertEquals("Filipino", fil.getDisplayLanguage(Locale.ENGLISH)); 170 assertEquals("Filipino", fil_PH.getDisplayLanguage(Locale.ENGLISH)); 171 assertEquals("Filipino", fil.getDisplayLanguage(fil)); 172 assertEquals("Filipino", fil_PH.getDisplayLanguage(fil_PH)); 173 174 assertEquals("filipino", fil.getDisplayLanguage(es_MX)); 175 assertEquals("filipino", fil_PH.getDisplayLanguage(es_MX)); 176 } 177 178 // http://b/3452611; Locale.getDisplayLanguage fails for the obsolete language codes. 179 public void test_getDisplayName_obsolete() throws Exception { 180 // he (new) -> iw (obsolete) 181 assertObsolete("he", "iw", ""); 182 // id (new) -> in (obsolete) 183 assertObsolete("id", "in", "Indonesia"); 184 } 185 186 private static void assertObsolete(String newCode, String oldCode, String displayName) { 187 // Either code should get you the same locale. 188 Locale newLocale = new Locale(newCode); 189 Locale oldLocale = new Locale(oldCode); 190 assertEquals(newLocale, oldLocale); 191 192 // No matter what code you used to create the locale, you should get the old code back. 193 assertEquals(oldCode, newLocale.getLanguage()); 194 assertEquals(oldCode, oldLocale.getLanguage()); 195 196 // Check we get the right display name. 197 assertEquals(displayName, newLocale.getDisplayLanguage(newLocale)); 198 assertEquals(displayName, oldLocale.getDisplayLanguage(newLocale)); 199 assertEquals(displayName, newLocale.getDisplayLanguage(oldLocale)); 200 assertEquals(displayName, oldLocale.getDisplayLanguage(oldLocale)); 201 202 // Check that none of the 'getAvailableLocales' methods are accidentally returning two 203 // equal locales (because to ICU they're different, but we mangle one into the other). 204 assertOnce(newLocale, BreakIterator.getAvailableLocales()); 205 assertOnce(newLocale, Calendar.getAvailableLocales()); 206 assertOnce(newLocale, Collator.getAvailableLocales()); 207 assertOnce(newLocale, DateFormat.getAvailableLocales()); 208 assertOnce(newLocale, DateFormatSymbols.getAvailableLocales()); 209 assertOnce(newLocale, NumberFormat.getAvailableLocales()); 210 assertOnce(newLocale, Locale.getAvailableLocales()); 211 } 212 213 private static void assertOnce(Locale element, Locale[] array) { 214 int count = 0; 215 for (Locale l : array) { 216 if (l.equals(element)) { 217 ++count; 218 } 219 } 220 assertEquals(1, count); 221 } 222 223 public void test_getISO3Country() { 224 // Empty country code. 225 assertEquals("", new Locale("en", "").getISO3Country()); 226 227 // Invalid country code. 228 try { 229 assertEquals("", new Locale("en", "XX").getISO3Country()); 230 fail(); 231 } catch (MissingResourceException expected) { 232 assertEquals("FormatData_en_XX", expected.getClassName()); 233 assertEquals("ShortCountry", expected.getKey()); 234 } 235 236 // Valid country code. 237 assertEquals("CAN", new Locale("", "CA").getISO3Country()); 238 assertEquals("CAN", new Locale("en", "CA").getISO3Country()); 239 assertEquals("CAN", new Locale("xx", "CA").getISO3Country()); 240 241 // 3 letter country codes. 242 assertEquals("CAN", new Locale("en", "CAN").getISO3Country()); 243 assertEquals("CAN", new Locale("frankenderp", "CAN").getISO3Country()); 244 } 245 246 public void test_getISO3Language() { 247 // Empty language code. 248 assertEquals("", new Locale("", "US").getISO3Language()); 249 250 // Invalid language code. 251 try { 252 assertEquals("", new Locale("xx", "US").getISO3Language()); 253 fail(); 254 } catch (MissingResourceException expected) { 255 assertEquals("FormatData_xx_US", expected.getClassName()); 256 assertEquals("ShortLanguage", expected.getKey()); 257 } 258 259 // Valid language code. 260 assertEquals("eng", new Locale("en", "").getISO3Language()); 261 assertEquals("eng", new Locale("en", "CA").getISO3Language()); 262 assertEquals("eng", new Locale("en", "XX").getISO3Language()); 263 264 // 3 letter language code. 265 assertEquals("eng", new Locale("eng", "USA").getISO3Language()); 266 assertEquals("eng", new Locale("eng", "US").getISO3Language()); 267 } 268 269 public void test_Builder_setLanguage() { 270 Locale.Builder b = new Locale.Builder(); 271 272 // Should normalize to lower case. 273 b.setLanguage("EN"); 274 assertEquals("en", b.build().getLanguage()); 275 276 b = new Locale.Builder(); 277 278 // Too short. 279 try { 280 b.setLanguage("e"); 281 fail(); 282 } catch (IllformedLocaleException expected) { 283 } 284 285 // Too long 286 try { 287 // note: pre-openJdk Locale assumed that language will be between 288 // 2-3 characters. openJdk accepts 2-8 character languages. 289 b.setLanguage("foobarbar"); 290 fail(); 291 } catch (IllformedLocaleException expected) { 292 } 293 294 // Contains non ASCII characters 295 try { 296 b.setLanguage(""); 297 fail(); 298 } catch (IllformedLocaleException expected) { 299 } 300 301 // Null or empty languages must clear state. 302 b = new Locale.Builder(); 303 b.setLanguage("en"); 304 b.setLanguage(null); 305 assertEquals("", b.build().getLanguage()); 306 307 b = new Locale.Builder(); 308 b.setLanguage("en"); 309 b.setLanguage(""); 310 assertEquals("", b.build().getLanguage()); 311 } 312 313 public void test_Builder_setRegion() { 314 Locale.Builder b = new Locale.Builder(); 315 316 // Should normalize to upper case. 317 b.setRegion("us"); 318 assertEquals("US", b.build().getCountry()); 319 320 b = new Locale.Builder(); 321 322 // Too short. 323 try { 324 b.setRegion("e"); 325 fail(); 326 } catch (IllformedLocaleException expected) { 327 } 328 329 // Too long 330 try { 331 b.setRegion("USA"); 332 fail(); 333 } catch (IllformedLocaleException expected) { 334 } 335 336 // Contains non ASCII characters 337 try { 338 b.setLanguage(""); 339 fail(); 340 } catch (IllformedLocaleException expected) { 341 } 342 343 // Null or empty regions must clear state. 344 b = new Locale.Builder(); 345 b.setRegion("US"); 346 b.setRegion(null); 347 assertEquals("", b.build().getCountry()); 348 349 b = new Locale.Builder(); 350 b.setRegion("US"); 351 b.setRegion(""); 352 assertEquals("", b.build().getCountry()); 353 } 354 355 public void test_Builder_setVariant() { 356 Locale.Builder b = new Locale.Builder(); 357 358 // Should normalize "_" to "-" 359 b = new Locale.Builder(); 360 b.setVariant("vArIaNt-VaRiAnT-VARIANT"); 361 assertEquals("vArIaNt_VaRiAnT_VARIANT", b.build().getVariant()); 362 363 b = new Locale.Builder(); 364 // Too short 365 try { 366 b.setVariant("shor"); 367 fail(); 368 } catch (IllformedLocaleException expected) { 369 } 370 371 // Too long 372 try { 373 b.setVariant("waytoolong"); 374 fail(); 375 } catch (IllformedLocaleException expected) { 376 } 377 378 try { 379 b.setVariant("foooo-foooo-fo"); 380 fail(); 381 } catch (IllformedLocaleException expected) { 382 } 383 384 // Special case. Variants of length 4 are allowed when the first 385 // character is a digit. 386 b.setVariant("0ABC"); 387 assertEquals("0ABC", b.build().getVariant()); 388 389 b = new Locale.Builder(); 390 b.setVariant("variant"); 391 b.setVariant(null); 392 assertEquals("", b.build().getVariant()); 393 394 b = new Locale.Builder(); 395 b.setVariant("variant"); 396 b.setVariant(""); 397 assertEquals("", b.build().getVariant()); 398 } 399 400 public void test_Builder_setLocale() { 401 // Default case. 402 Locale.Builder b = new Locale.Builder(); 403 b.setLocale(Locale.US); 404 assertEquals("en", b.build().getLanguage()); 405 assertEquals("US", b.build().getCountry()); 406 407 // Should throw when locale is malformed. 408 // - Bad language 409 Locale bad = new Locale("e", "US"); 410 b = new Locale.Builder(); 411 try { 412 b.setLocale(bad); 413 fail(); 414 } catch (IllformedLocaleException expected) { 415 } 416 // - Bad country 417 bad = new Locale("en", "USA"); 418 try { 419 b.setLocale(bad); 420 fail(); 421 } catch (IllformedLocaleException expected) { 422 } 423 424 // - Bad variant 425 bad = new Locale("en", "US", "c"); 426 try { 427 b.setLocale(bad); 428 fail(); 429 } catch (IllformedLocaleException expected) { 430 } 431 432 // Test values are normalized as they should be 433 b = new Locale.Builder(); 434 Locale good = new Locale("EN", "us", "variant-VARIANT"); 435 b.setLocale(good); 436 Locale l = b.build(); 437 assertEquals("en", l.getLanguage()); 438 assertEquals("US", l.getCountry()); 439 assertEquals("variant_VARIANT", l.getVariant()); 440 441 // Test that none of the existing fields are messed with 442 // if the locale update fails. 443 b = new Locale.Builder(); 444 b.setLanguage("fr").setRegion("FR"); 445 446 try { 447 b.setLocale(bad); 448 fail(); 449 } catch (IllformedLocaleException expected) { 450 } 451 452 l = b.build(); 453 assertEquals("fr", l.getLanguage()); 454 assertEquals("FR", l.getCountry()); 455 } 456 457 public void test_Builder_setScript() { 458 Locale.Builder b = new Locale.Builder(); 459 460 // Should normalize variants to lower case. 461 b.setScript("lAtN"); 462 assertEquals("Latn", b.build().getScript()); 463 464 b = new Locale.Builder(); 465 // Too short 466 try { 467 b.setScript("lat"); 468 fail(); 469 } catch (IllformedLocaleException expected) { 470 } 471 472 // Too long 473 try { 474 b.setScript("latin"); 475 fail(); 476 } catch (IllformedLocaleException expected) { 477 } 478 479 b = new Locale.Builder(); 480 b.setScript("Latn"); 481 b.setScript(null); 482 assertEquals("", b.build().getScript()); 483 484 b = new Locale.Builder(); 485 b.setScript("Latn"); 486 b.setScript(""); 487 assertEquals("", b.build().getScript()); 488 } 489 490 public void test_Builder_clear() { 491 Locale.Builder b = new Locale.Builder(); 492 b.setLanguage("en").setScript("Latn").setRegion("US") 493 .setVariant("POSIX").setExtension('g', "foo") 494 .setUnicodeLocaleKeyword("fo", "baar") 495 .addUnicodeLocaleAttribute("baaaaz"); 496 497 Locale l = b.clear().build(); 498 assertEquals("", l.getLanguage()); 499 assertEquals("", l.getCountry()); 500 assertEquals("", l.getVariant()); 501 assertEquals("", l.getScript()); 502 assertTrue(l.getExtensionKeys().isEmpty()); 503 } 504 505 public void test_Builder_setExtension() { 506 Locale.Builder b = new Locale.Builder(); 507 b.setExtension('g', "FO_ba-BR_bg"); 508 509 Locale l = b.build(); 510 assertEquals("fo-ba-br-bg", l.getExtension('g')); 511 512 b = new Locale.Builder(); 513 514 // Too short 515 try { 516 b.setExtension('g', "fo-ba-br-x"); 517 fail(); 518 } catch (IllformedLocaleException expected) { 519 } 520 521 // Too long 522 try { 523 b.setExtension('g', "fo-ba-br-extension"); 524 fail(); 525 } catch (IllformedLocaleException expected) { 526 } 527 528 // Special case, the private use extension allows single char subtags. 529 b.setExtension(Locale.PRIVATE_USE_EXTENSION, "fo-ba-br-m"); 530 l = b.build(); 531 assertEquals("fo-ba-br-m", l.getExtension('x')); 532 533 // Special case, the unicode locale extension must be parsed into 534 // its individual components. The correctness of the parse is tested 535 // in test_parseUnicodeExtension. 536 b.setExtension(Locale.UNICODE_LOCALE_EXTENSION, "foooo_BaaaR-BA_Baz-bI_BIZ"); 537 l = b.build(); 538 // Note that attributes and keywords are sorted alphabetically. 539 assertEquals("baaar-foooo-ba-baz-bi-biz", l.getExtension('u')); 540 541 assertTrue(l.getUnicodeLocaleAttributes().contains("foooo")); 542 assertTrue(l.getUnicodeLocaleAttributes().contains("baaar")); 543 assertEquals("baz", l.getUnicodeLocaleType("ba")); 544 assertEquals("biz", l.getUnicodeLocaleType("bi")); 545 } 546 547 public void test_Builder_clearExtensions() { 548 Locale.Builder b = new Locale.Builder(); 549 b.setExtension('g', "FO_ba-BR_bg"); 550 b.setExtension(Locale.PRIVATE_USE_EXTENSION, "fo-ba-br-m"); 551 b.clearExtensions(); 552 553 assertTrue(b.build().getExtensionKeys().isEmpty()); 554 } 555 556 private static Locale fromLanguageTag(String languageTag, boolean useBuilder) { 557 if (useBuilder) { 558 return (new Locale.Builder().setLanguageTag(languageTag).build()); 559 } else { 560 return Locale.forLanguageTag(languageTag); 561 } 562 } 563 564 private void test_setLanguageTag_wellFormedsingleSubtag(boolean useBuilder) { 565 Locale l = fromLanguageTag("en", useBuilder); 566 assertEquals("en", l.getLanguage()); 567 568 l = fromLanguageTag("eng", useBuilder); 569 assertEquals("eng", l.getLanguage()); 570 } 571 572 private void test_setLanguageTag_twoWellFormedSubtags(boolean useBuilder) { 573 Locale l = fromLanguageTag("en-US", useBuilder); 574 assertEquals("en", l.getLanguage()); 575 assertEquals("US", l.getCountry()); 576 577 l = fromLanguageTag("eng-419", useBuilder); 578 assertEquals("eng", l.getLanguage()); 579 assertEquals("419", l.getCountry()); 580 581 // Script tags shouldn't be mis-recognized as regions. 582 l = fromLanguageTag("en-Latn", useBuilder); 583 assertEquals("en", l.getLanguage()); 584 assertEquals("", l.getCountry()); 585 assertEquals("Latn", l.getScript()); 586 587 // Neither should variant tags. 588 l = fromLanguageTag("en-POSIX", useBuilder); 589 assertEquals("en", l.getLanguage()); 590 assertEquals("", l.getCountry()); 591 assertEquals("", l.getScript()); 592 assertEquals("POSIX", l.getVariant()); 593 } 594 595 public void test_Builder_setLanguageTag_malformedTags() { 596 try { 597 fromLanguageTag("a", true); 598 fail(); 599 } catch (IllformedLocaleException ifle) { 600 } 601 602 // Three subtags 603 // lang-region-illformedvariant 604 try { 605 fromLanguageTag("en-US-BA", true); 606 fail(); 607 } catch (IllformedLocaleException expected) { 608 } 609 610 // lang-variant-illformedvariant 611 try { 612 fromLanguageTag("en-FOOOO-BA", true); 613 fail(); 614 } catch (IllformedLocaleException expected) { 615 } 616 617 // Four or more sub tags 618 try { 619 fromLanguageTag("en-US-POSIX-P2", true); 620 fail(); 621 } catch (IllformedLocaleException expected) { 622 } 623 624 try { 625 fromLanguageTag("en-Latn-US-P2", true); 626 fail(); 627 } catch (IllformedLocaleException expected) { 628 } 629 630 // Extensions 631 // Ill-formed empty extension. 632 try { 633 fromLanguageTag("en-f-f", true); 634 fail(); 635 } catch (IllformedLocaleException expected) { 636 } 637 638 // Ill-formed empty extension. 639 try { 640 fromLanguageTag("en-f", true); 641 fail(); 642 } catch (IllformedLocaleException expected) { 643 } 644 645 // Two extension keys in a row (i.e, another case of an ill-formed 646 // empty exception). 647 try { 648 fromLanguageTag("en-f-g-fo-baar", true); 649 fail(); 650 } catch (IllformedLocaleException expected) { 651 } 652 653 // Dangling empty key after a well formed extension. 654 try { 655 fromLanguageTag("en-f-fo-baar-g", true); 656 fail(); 657 } catch (IllformedLocaleException expected) { 658 } 659 660 // Ill-formed extension with long subtag. 661 try { 662 fromLanguageTag("en-f-fooobaaaz", true); 663 fail(); 664 } catch (IllformedLocaleException expected) { 665 } 666 } 667 668 private void test_setLanguageTag_threeWellFormedSubtags(boolean useBuilder) { 669 // lang-region-variant 670 Locale l = fromLanguageTag("en-US-FOOOO", useBuilder); 671 assertEquals("en", l.getLanguage()); 672 assertEquals("US", l.getCountry()); 673 assertEquals("", l.getScript()); 674 assertEquals("FOOOO", l.getVariant()); 675 676 // lang-script-variant 677 l = fromLanguageTag("en-Latn-FOOOO", useBuilder); 678 assertEquals("en", l.getLanguage()); 679 assertEquals("", l.getCountry()); 680 assertEquals("Latn", l.getScript()); 681 assertEquals("FOOOO", l.getVariant()); 682 683 // lang-script-region 684 l = fromLanguageTag("en-Latn-US", useBuilder); 685 assertEquals("en", l.getLanguage()); 686 assertEquals("US", l.getCountry()); 687 assertEquals("Latn", l.getScript()); 688 assertEquals("", l.getVariant()); 689 690 // lang-variant-variant 691 l = fromLanguageTag("en-FOOOO-BAAAR", useBuilder); 692 assertEquals("en", l.getLanguage()); 693 assertEquals("", l.getCountry()); 694 assertEquals("", l.getScript()); 695 assertEquals("FOOOO_BAAAR", l.getVariant()); 696 } 697 698 private void test_setLanguageTag_fourOrMoreWellFormedSubtags(boolean useBuilder) { 699 // lang-script-region-variant. 700 Locale l = fromLanguageTag("en-Latn-US-foooo", useBuilder); 701 assertEquals("en", l.getLanguage()); 702 assertEquals("Latn", l.getScript()); 703 assertEquals("US", l.getCountry()); 704 assertEquals("foooo", l.getVariant()); 705 706 // Variant with multiple subtags. 707 l = fromLanguageTag("en-Latn-US-foooo-gfffh", useBuilder); 708 assertEquals("en", l.getLanguage()); 709 assertEquals("Latn", l.getScript()); 710 assertEquals("US", l.getCountry()); 711 assertEquals("foooo_gfffh", l.getVariant()); 712 713 // Variant with 3 subtags. POSIX shouldn't be recognized 714 // as a region or a script. 715 l = fromLanguageTag("en-POSIX-P2003-P2004", useBuilder); 716 assertEquals("en", l.getLanguage()); 717 assertEquals("", l.getScript()); 718 assertEquals("", l.getCountry()); 719 assertEquals("POSIX_P2003_P2004", l.getVariant()); 720 721 // lang-script-variant-variant. 722 l = fromLanguageTag("en-Latn-POSIX-P2003", useBuilder); 723 assertEquals("en", l.getLanguage()); 724 assertEquals("Latn", l.getScript()); 725 assertEquals("", l.getCountry()); 726 assertEquals("POSIX_P2003", l.getVariant()); 727 728 // lang-region-variant-variant 729 l = fromLanguageTag("en-US-POSIX-P2003", useBuilder); 730 assertEquals("en", l.getLanguage()); 731 assertEquals("", l.getScript()); 732 assertEquals("US", l.getCountry()); 733 assertEquals("POSIX_P2003", l.getVariant()); 734 } 735 736 private void test_setLanguageTag_withWellFormedExtensions(boolean useBuilder) { 737 Locale l = fromLanguageTag("en-Latn-GB-foooo-g-fo-bar-baaz", useBuilder); 738 assertEquals("en", l.getLanguage()); 739 assertEquals("Latn", l.getScript()); 740 assertEquals("GB", l.getCountry()); 741 assertEquals("foooo", l.getVariant()); 742 assertEquals("fo-bar-baaz", l.getExtension('g')); 743 744 // Multiple extensions 745 l = fromLanguageTag("en-Latn-US-foooo-g-fo-bar-h-go-gaz", useBuilder); 746 assertEquals("en", l.getLanguage()); 747 assertEquals("Latn", l.getScript()); 748 assertEquals("US", l.getCountry()); 749 assertEquals("foooo", l.getVariant()); 750 assertEquals("fo-bar", l.getExtension('g')); 751 assertEquals("go-gaz", l.getExtension('h')); 752 753 // Unicode locale extension. 754 l = fromLanguageTag("en-Latn-US-foooo-u-koooo-fo-bar", useBuilder); 755 assertEquals("en", l.getLanguage()); 756 assertEquals("Latn", l.getScript()); 757 assertEquals("US", l.getCountry()); 758 assertEquals("koooo-fo-bar", l.getExtension('u')); 759 assertTrue(l.getUnicodeLocaleAttributes().contains("koooo")); 760 assertEquals("bar", l.getUnicodeLocaleType("fo")); 761 762 // Extensions without variants 763 l = fromLanguageTag("en-Latn-US-f-fo", useBuilder); 764 assertEquals("en", l.getLanguage()); 765 assertEquals("Latn", l.getScript()); 766 assertEquals("US", l.getCountry()); 767 assertEquals("fo", l.getExtension('f')); 768 769 l = fromLanguageTag("en-Latn-f-fo", useBuilder); 770 assertEquals("en", l.getLanguage()); 771 assertEquals("Latn", l.getScript()); 772 assertEquals("fo", l.getExtension('f')); 773 774 l = fromLanguageTag("en-f-fo", useBuilder); 775 assertEquals("en", l.getLanguage()); 776 assertEquals("", l.getScript()); 777 assertEquals("", l.getCountry()); 778 assertEquals("fo", l.getExtension('f')); 779 780 l = fromLanguageTag("en-f-fo-x-a-b-c-d-e-fo", useBuilder); 781 assertEquals("en", l.getLanguage()); 782 assertEquals("", l.getScript()); 783 assertEquals("", l.getCountry()); 784 assertEquals("fo", l.getExtension('f')); 785 assertEquals("a-b-c-d-e-fo", l.getExtension('x')); 786 } 787 788 /** 789 * Tests filtering locales using basic language ranges (without "*"). 790 */ 791 public void test_filter_basic() { 792 List<String> tags = tagsOf( 793 "en-US", 794 "en-Latn-US", 795 "zh-Hant-TW", 796 "es-419", 797 "fr-FR", 798 "ja-JP" 799 ); 800 List<LanguageRange> ranges = new ArrayList<>(); 801 ranges.add(new LanguageRange("en-US")); 802 803 // By default, basic filtering is used for basic language ranges 804 assertFilter(tagsOf("en-US"), ranges, tags); 805 806 // Since no extended ranges are given, these should produce the same result 807 assertFilter(tagsOf("en-US"), ranges, tags, AUTOSELECT_FILTERING); 808 assertFilter(tagsOf("en-US"), ranges, tags, REJECT_EXTENDED_RANGES); 809 assertFilter(tagsOf("en-US"), ranges, tags, IGNORE_EXTENDED_RANGES); 810 811 // EXTENDED_FILTERING can be enabled explicitly even when the priority 812 // list only contains basic; then, en-US also matches en-Latn-US. 813 assertFilter(tagsOf("en-US", "en-Latn-US"), ranges, tags, EXTENDED_FILTERING); 814 815 ranges.add(new LanguageRange("zh-Hant-TW")); 816 assertFilter(tagsOf("en-US", "zh-Hant-TW"), ranges, tags); 817 } 818 819 /** 820 * Tests that filtering is case insensitive. 821 */ 822 public void test_filter_caseInsensitive() { 823 List<String> tags = tagsOf("de-DE", "de-Latn-DE", "ja-jp"); 824 825 assertFilter(tagsOf("de-DE"), languageRangesOf("dE-De"), tags); 826 assertFilter(tagsOf("ja-jp"), languageRangesOf("ja-JP"), tags); 827 assertFilter(tagsOf("ja-jp"), languageRangesOf("JA-jp"), tags); 828 } 829 830 /** 831 * Tests filtering locales using extended language ranges (with "*"), per 832 * the example from RFC 4647 section 3.3.2 833 */ 834 public void test_filter_extended() { 835 List<LanguageRange> priorityList = languageRangesOf("de-DE", "de-*-DE"); 836 List<String> tags = tagsOf( 837 "de", // not matched: missing 'DE' 838 "de-DE", // German, as used in Germany 839 "de-de", // German, as used in Germany 840 "de-Latn-DE", // Latin script 841 "de-Latf-DE", // Fraktur variant of Latin script 842 "de-DE-x-goethe", // private-use subtag 843 "de-Latn-DE-1996", 844 "de-Deva", // not matched: 'Deva' not equal to 'DE' 845 "de-Deva-DE", // Devanagari script 846 "de-x-DE" // not matched: singleton 'x' occurs before 'DE' 847 ); 848 849 List<String> filteredTags = tagsOf( 850 "de-DE", // German, as used in Germany 851 "de-Latn-DE", // Latin script 852 "de-Latf-DE", // Fraktur variant of Latin script 853 "de-DE-x-goethe", // private-use subtag 854 "de-Latn-DE-1996", 855 "de-Deva-DE" // Devanagari script 856 ); 857 858 assertFilter(filteredTags, priorityList, tags, EXTENDED_FILTERING); 859 860 // Because the priority list contains an extended language range, filtering 861 // should default to extended, so default filtering should yield the same results: 862 assertFilter(filteredTags, priorityList, tags); 863 assertFilter(filteredTags, priorityList, tags, AUTOSELECT_FILTERING); 864 865 // Ignoring the extended range (de-*-DE) matches only a single language tag, "de-DE" 866 assertFilter(tagsOf("de-DE", "de-DE-x-goethe"), priorityList, tags, IGNORE_EXTENDED_RANGES); 867 } 868 869 /** 870 * Tests that filtering with {@link Locale.FilteringMode#REJECT_EXTENDED_RANGES} 871 * throws IllegalArgumentException if passed an extended tag / language range. 872 */ 873 public void test_filter_extended_reject() { 874 try { 875 Locale.filter( 876 languageRangesOf("de-DE", "de-*-DE"), 877 localesOf("de-DE", "fr-FR"), 878 REJECT_EXTENDED_RANGES); 879 fail(); 880 } catch (IllegalArgumentException expected) { 881 } 882 883 try { 884 Locale.filterTags( 885 languageRangesOf("de-DE", "de-*-DE"), 886 tagsOf("de-DE", "fr-FR"), 887 REJECT_EXTENDED_RANGES); 888 fail(); 889 } catch (IllegalArgumentException expected) { 890 } 891 } 892 893 /** 894 * Checks that a '*' occurring in a LanguageRange is interpreted in compliance 895 * with RFC 4647 section 3.2: if the first subtag is a '*' then the entire range 896 * is treated as "*", otherwise each wildcard subtag is removed. 897 */ 898 public void test_filter_extended_wildcardInLanguageRange() { 899 List<String> tags = tagsOf("en-US", "de-DE", "en-AU", "en-Latn-US"); 900 // en-*-US is treated as "en-US", so only en-US matches 901 assertFilter(tagsOf("en-US"), languageRangesOf("en-*-US"), tags, MAP_EXTENDED_RANGES); 902 903 // *-US is treated as "*", so all locales match 904 assertFilter(tags, languageRangesOf("*-US"), tags, MAP_EXTENDED_RANGES); 905 906 // Same behavior with just "*" 907 assertFilter(tags, languageRangesOf("*"), tags, MAP_EXTENDED_RANGES); 908 } 909 910 /** 911 * Tests that a '*' in a Locale in the priority list matches a subtag only 912 * when extended filtering is used; note that this is different from a 913 * '*' occuring in a LanguageRange, where it is ignored. 914 */ 915 public void test_filter_extended_wildcardInPriorityList() { 916 List<String> tags = tagsOf("de-DE", "de-Latn-DE", "ja-JP"); 917 assertFilter(tagsOf("de-DE", "de-Latn-DE"), 918 languageRangesOf("dE-*-De"), tags); 919 assertFilter(tagsOf("de-DE", "de-Latn-DE"), 920 languageRangesOf("dE-De"), tags, EXTENDED_FILTERING); 921 } 922 923 public void test_filter_noMatch() { 924 List<String> noTag = Collections.emptyList(); 925 926 List<String> tags = tagsOf("en-US", "fr-Fr", "de-DE"); 927 928 assertFilter(noTag, languageRangesOf("en-AU"), tags); 929 assertFilter(noTag, languageRangesOf("es-419"), tags); 930 assertFilter(noTag, languageRangesOf("zh-*-TW"), tags); 931 } 932 933 /** 934 * Tests that various methods throw NullPointerException when given {@code null} 935 * as an argument. 936 */ 937 public void test_filter_nullArguments() { 938 List<String> tags = tagsOf("de-DE", "de-Latn-DE"); 939 List<Locale> locales = localesOf(tags); 940 List<LanguageRange> languageRanges = languageRangesOf("en-*-US", "de-DE"); 941 942 assertThrowsNpe(() -> { Locale.filter(null, locales); }); 943 assertThrowsNpe(() -> { Locale.filter(languageRanges, null); }); 944 945 assertThrowsNpe(() -> { Locale.filterTags(null, tags); }); 946 assertThrowsNpe(() -> { Locale.filterTags(languageRanges, null); }); 947 948 // The documentation doesn't say whether FilteringMode is allowed to be 949 // null or what the sematnics of that null are; currently it is allowed. 950 // This test ensures that we are aware if we change this behavior in future. 951 List<Locale> filteredLocales = Locale.filter(languageRanges, locales, null); 952 List<String> filteredTags = Locale.filterTags(languageRanges, tags, null); 953 assertEquals(localesOf("de-DE"), filteredLocales); 954 assertEquals(tagsOf("de-DE"), filteredTags); 955 } 956 957 /** 958 * Tests that filtered locales are returned in priority order. 959 */ 960 public void test_filter_priorityOrder() { 961 List<LanguageRange> priorityList = languageRangesOf("zh-Hant-TW", "en-US"); 962 963 List<String> tags = tagsOf( 964 "en-US", 965 "zh-Hant-TW", 966 "es-419", 967 "fr-FR" 968 ); 969 assertFilter(tagsOf("zh-Hant-TW", "en-US"), languageRangesOf("zh-Hant-TW", "en-US"), tags); 970 assertFilter(tagsOf("en-US", "zh-Hant-TW"), languageRangesOf("en-US", "zh-Hant-TW"), tags); 971 } 972 973 /** 974 * Tests that the List returned by the various {@code filter} methods is modifiable, 975 * as specified by the documentation. 976 */ 977 public void test_filter_resultIsModifiable_locales() { 978 List<LanguageRange> priorityList = languageRangesOf("de-DE", "de-*-DE"); 979 List<Locale> locales = localesOf("de-DE", "de-Latn-DE", "ja-JP"); 980 981 Locale dummy = Locale.FRANCE; 982 // should not throw 983 Locale.filter(priorityList, locales).add(dummy); 984 Locale.filter(priorityList, locales, AUTOSELECT_FILTERING).add(dummy); 985 Locale.filter(priorityList, locales, EXTENDED_FILTERING).add(dummy); 986 Locale.filter(priorityList, locales, IGNORE_EXTENDED_RANGES).add(dummy); 987 Locale.filter(priorityList, locales, MAP_EXTENDED_RANGES).add(dummy); 988 Locale.filter(languageRangesOf("de-DE"), locales, REJECT_EXTENDED_RANGES).add(dummy); 989 } 990 991 public void test_filter_resultIsModifiable_tags() { 992 List<LanguageRange> priorityList = languageRangesOf("de-DE", "de-*-DE"); 993 List<String> tags = tagsOf("de-DE", "de-Latn-DE", "ja-JP"); 994 995 String dummy = "fr-FR"; 996 // should not throw 997 Locale.filterTags(priorityList, tags).add(dummy); 998 Locale.filterTags(priorityList, tags, AUTOSELECT_FILTERING).add(dummy); 999 Locale.filterTags(priorityList, tags, EXTENDED_FILTERING).add(dummy); 1000 Locale.filterTags(priorityList, tags, IGNORE_EXTENDED_RANGES).add(dummy); 1001 Locale.filterTags(priorityList, tags, MAP_EXTENDED_RANGES).add(dummy); 1002 Locale.filterTags(languageRangesOf("de-DE"), tags, REJECT_EXTENDED_RANGES).add(dummy); 1003 } 1004 1005 public void test_forLanguageTag() { 1006 test_setLanguageTag_wellFormedsingleSubtag(false); 1007 test_setLanguageTag_twoWellFormedSubtags(false); 1008 test_setLanguageTag_threeWellFormedSubtags(false); 1009 test_setLanguageTag_fourOrMoreWellFormedSubtags(false); 1010 test_setLanguageTag_withWellFormedExtensions(false); 1011 } 1012 1013 public void test_Builder_setLanguageTag() { 1014 test_setLanguageTag_wellFormedsingleSubtag(true); 1015 test_setLanguageTag_twoWellFormedSubtags(true); 1016 test_setLanguageTag_threeWellFormedSubtags(true); 1017 test_setLanguageTag_fourOrMoreWellFormedSubtags(true); 1018 test_setLanguageTag_withWellFormedExtensions(true); 1019 } 1020 1021 public void test_getDisplayScript() { 1022 Locale.Builder b = new Locale.Builder(); 1023 b.setLanguage("en").setRegion("US").setScript("Latn"); 1024 1025 Locale l = b.build(); 1026 1027 // getAndSetDefaultForTest(uncategorizedLocale, displayLocale, formatLocale) 1028 Locales locales = Locales.getAndSetDefaultForTest(Locale.US, Locale.GERMANY, Locale.FRANCE); 1029 try { 1030 // Check that getDisplayScript() uses the default DISPLAY Locale. 1031 assertEquals("Lateinisch", l.getDisplayScript()); // the German word for "Latin" 1032 1033 assertEquals("latino", l.getDisplayScript(Locale.ITALY)); 1034 1035 // Fallback for navajo, a language for which we don't have data. 1036 assertEquals("Latin", l.getDisplayScript(new Locale("nv", "US"))); 1037 1038 b = new Locale.Builder(); 1039 b.setLanguage("en").setRegion("US").setScript("Fooo"); 1040 1041 // Will be equivalent to getScriptCode for scripts that aren't 1042 // registered with ISO-15429 (but are otherwise well formed). 1043 l = b.build(); 1044 assertEquals("Fooo", l.getDisplayScript()); 1045 } finally { 1046 locales.setAsDefault(); 1047 } 1048 } 1049 1050 public void test_setLanguageTag_malformedTags() { 1051 Locale l = fromLanguageTag("a", false); 1052 assertEquals("", l.getLanguage()); 1053 assertEquals("", l.getCountry()); 1054 assertEquals("", l.getVariant()); 1055 assertEquals("", l.getScript()); 1056 1057 l = fromLanguageTag("en-US-BA", false); 1058 assertEquals("en", l.getLanguage()); 1059 assertEquals("US", l.getCountry()); 1060 assertEquals("", l.getVariant()); 1061 assertEquals("", l.getScript()); 1062 1063 l = fromLanguageTag("en-FOOOO-BA", false); 1064 assertEquals("en", l.getLanguage()); 1065 assertEquals("", l.getCountry()); 1066 assertEquals("FOOOO", l.getVariant()); 1067 assertEquals("", l.getScript()); 1068 1069 l = fromLanguageTag("en-US-POSIX-P2", false); 1070 assertEquals("en", l.getLanguage()); 1071 assertEquals("US", l.getCountry()); 1072 assertEquals("POSIX", l.getVariant()); 1073 assertEquals("", l.getScript()); 1074 1075 l = fromLanguageTag("en-Latn-US-P2", false); 1076 assertEquals("en", l.getLanguage()); 1077 assertEquals("US", l.getCountry()); 1078 assertEquals("Latn", l.getScript()); 1079 1080 l = fromLanguageTag("en-f-f", false); 1081 assertEquals("en", l.getLanguage()); 1082 assertEquals("", l.getCountry()); 1083 assertEquals("", l.getVariant()); 1084 assertEquals("", l.getScript()); 1085 1086 l = fromLanguageTag("en-f", false); 1087 assertEquals("en", l.getLanguage()); 1088 assertEquals("", l.getCountry()); 1089 assertEquals("", l.getVariant()); 1090 assertEquals("", l.getScript()); 1091 1092 l = fromLanguageTag("en-f-fooobaaaz", false); 1093 assertEquals("en", l.getLanguage()); 1094 assertEquals("", l.getCountry()); 1095 assertEquals("", l.getVariant()); 1096 assertEquals("", l.getScript()); 1097 1098 l = fromLanguageTag("en-9-baa", false); 1099 assertEquals("en", l.getLanguage()); 1100 assertEquals("", l.getCountry()); 1101 assertEquals("", l.getVariant()); 1102 assertEquals("", l.getScript()); 1103 } 1104 1105 public void test_Builder_unicodeAttributes() { 1106 // Adding and removing attributes 1107 Locale.Builder b = new Locale.Builder(); 1108 b.setLanguage("en"); 1109 1110 // Well formed attribute. 1111 b.addUnicodeLocaleAttribute("foooo"); 1112 1113 try { 1114 b.addUnicodeLocaleAttribute("fo"); 1115 fail(); 1116 } catch (IllformedLocaleException ifle) { 1117 } 1118 1119 try { 1120 b.removeUnicodeLocaleAttribute("fo"); 1121 fail(); 1122 } catch (IllformedLocaleException ifle) { 1123 } 1124 1125 try { 1126 b.addUnicodeLocaleAttribute("greaterthaneightchars"); 1127 fail(); 1128 } catch (IllformedLocaleException ifle) { 1129 } 1130 1131 try { 1132 b.removeUnicodeLocaleAttribute("greaterthaneightchars"); 1133 fail(); 1134 } catch (IllformedLocaleException ifle) { 1135 } 1136 1137 try { 1138 b.addUnicodeLocaleAttribute(null); 1139 fail(); 1140 } catch (NullPointerException npe) { 1141 } 1142 1143 try { 1144 b.removeUnicodeLocaleAttribute(null); 1145 fail(); 1146 } catch (NullPointerException npe) { 1147 } 1148 1149 Locale l = b.build(); 1150 assertEquals("en-u-foooo", l.toLanguageTag()); 1151 assertTrue(l.getUnicodeLocaleAttributes().contains("foooo")); 1152 1153 b.addUnicodeLocaleAttribute("dAtA"); 1154 l = b.build(); 1155 assertEquals("data-foooo", l.getExtension('u')); 1156 assertTrue(l.getUnicodeLocaleAttributes().contains("data")); 1157 assertTrue(l.getUnicodeLocaleAttributes().contains("foooo")); 1158 } 1159 1160 public void test_Builder_unicodeKeywords() { 1161 // Adding and removing attributes 1162 Locale.Builder b = new Locale.Builder(); 1163 b.setLanguage("en"); 1164 1165 // Key not of length 2. 1166 try { 1167 b.setUnicodeLocaleKeyword("k", "fooo"); 1168 fail(); 1169 } catch (IllformedLocaleException ifle) { 1170 } 1171 1172 // Value too short 1173 try { 1174 b.setUnicodeLocaleKeyword("k", "fo"); 1175 fail(); 1176 } catch (IllformedLocaleException ifle) { 1177 } 1178 1179 // Value too long 1180 try { 1181 b.setUnicodeLocaleKeyword("k", "foooooooo"); 1182 fail(); 1183 } catch (IllformedLocaleException ifle) { 1184 } 1185 1186 1187 // Null should clear the key. 1188 b.setUnicodeLocaleKeyword("bo", "baaz"); 1189 Locale l = b.build(); 1190 assertEquals("bo-baaz", l.getExtension('u')); 1191 assertEquals("baaz", l.getUnicodeLocaleType("bo")); 1192 1193 b = new Locale.Builder(); 1194 b.setUnicodeLocaleKeyword("bo", "baaz"); 1195 b.setUnicodeLocaleKeyword("bo", null); 1196 l = b.build(); 1197 assertNull(l.getExtension('u')); 1198 assertNull(l.getUnicodeLocaleType("bo")); 1199 1200 // When we set attributes, they should show up before extensions. 1201 b = new Locale.Builder(); 1202 b.addUnicodeLocaleAttribute("fooo"); 1203 b.addUnicodeLocaleAttribute("gooo"); 1204 b.setUnicodeLocaleKeyword("fo", "baz"); 1205 b.setUnicodeLocaleKeyword("ka", "kaz"); 1206 l = b.build(); 1207 assertEquals("fooo-gooo-fo-baz-ka-kaz", l.getExtension('u')); 1208 assertEquals("baz", l.getUnicodeLocaleType("fo")); 1209 assertEquals("kaz", l.getUnicodeLocaleType("ka")); 1210 assertTrue(l.getUnicodeLocaleAttributes().contains("fooo")); 1211 assertTrue(l.getUnicodeLocaleAttributes().contains("gooo")); 1212 } 1213 1214 public void test_multipleExtensions() { 1215 Locale.Builder b = new Locale.Builder(); 1216 b.setLanguage("en"); 1217 b.addUnicodeLocaleAttribute("attrib"); 1218 b.addUnicodeLocaleAttribute("attrib2"); 1219 b.setExtension('f', "fo-baaz-ga-gaaz"); 1220 b.setExtension('x', "xo-baaz-ga-gaaz"); 1221 b.setExtension('z', "zo-baaz-ga-gaaz"); 1222 1223 Locale l = b.build(); 1224 // Implicitly added because we added unicode locale attributes. 1225 assertEquals("attrib-attrib2", l.getExtension('u')); 1226 assertEquals("fo-baaz-ga-gaaz", l.getExtension('f')); 1227 assertEquals("xo-baaz-ga-gaaz", l.getExtension('x')); 1228 assertEquals("zo-baaz-ga-gaaz", l.getExtension('z')); 1229 } 1230 1231 public void test_immutability() { 1232 Locale.Builder b = new Locale.Builder(); 1233 b.setExtension('g', "fooo-baaz-baar"); 1234 b.setExtension('u', "foooo-baaar-ba-baaz-ka-kaaz"); 1235 1236 Locale l = b.build(); 1237 try { 1238 l.getExtensionKeys().add('g'); 1239 fail(); 1240 } catch (UnsupportedOperationException expected) { 1241 } 1242 1243 try { 1244 l.getUnicodeLocaleAttributes().add("fooo"); 1245 fail(); 1246 } catch (UnsupportedOperationException expected) { 1247 } 1248 } 1249 1250 public void test_lookup_noMatch() { 1251 // RFC 4647 section 3.4. 1252 List<LanguageRange> languageRanges = languageRangesOf( 1253 "zh-Hant-CN-x-private1-private2", 1254 "zh-Hant-CN-x-private1", 1255 "zh-Hant-CN", 1256 "zh-Hant", 1257 "zh" 1258 ); 1259 assertNull(Locale.lookup(languageRanges, localesOf("de-DE", "fr-FR", "ja-JP"))); 1260 assertNull(Locale.lookupTag(languageRanges, tagsOf("de-DE", "fr-FR", "ja-JP"))); 1261 } 1262 1263 /** 1264 * Tests that lookup returns the tag/locale that matches the highest priority 1265 * LanguageRange. 1266 */ 1267 public void test_lookup_order() { 1268 // RFC 4647 section 3.4. 1269 List<LanguageRange> languageRanges = languageRangesOf( 1270 "de-Latn-DE-1996", 1271 "zh-Hant-CN", 1272 "de" 1273 ); 1274 1275 // de would also match, but de-Latn-DE-1997 occurs earlier in the 1276 // (sorted by descending priority) languageRanges 1277 assertLookup("de-Latn-DE-1996", 1278 languageRanges, 1279 tagsOf("de", "de-Latn-DE-1996")); 1280 1281 // de-Latn-DE-1996 also includes de-Latn-DE, de-Latn, de; therefore 1282 // de-Latn-DE is preferred over zh-Hant-CN 1283 assertLookup("de-Latn-DE", 1284 languageRanges, 1285 tagsOf("de", "de-Latn-DE", "de-DE-1996", "zh-Hant-CN")); 1286 1287 // After reversing the priority list of the LanguageRanges, "de" now has the 1288 // highest priority. 1289 assertLookup("de", 1290 languageRangesOf( 1291 "de", 1292 "zh-Hant-CN", 1293 "de-Latn-DE-1996" 1294 ), 1295 tagsOf("de", "de-Latn-DE", "de-DE-1996")); 1296 1297 // Dropping "de" from the priority list of LanguageRanges false back to de-Latn-DE 1298 assertLookup("de-Latn-DE", 1299 languageRangesOf( 1300 "zh-Hant-CN", 1301 "de-Latn-DE-1996" 1302 ), 1303 tagsOf("de", "de-Latn-DE", "de-DE-1996")); 1304 } 1305 1306 public void test_lookup_nullArguments() { 1307 List<String> tags = tagsOf("de-DE", "de-Latn-DE"); 1308 List<Locale> locales = localesOf(tags); 1309 List<LanguageRange> languageRanges = languageRangesOf("en-*-US", "de-DE"); 1310 1311 assertThrowsNpe(() -> { Locale.lookup(null, locales); }); 1312 assertThrowsNpe(() -> { Locale.lookup(languageRanges, null); }); 1313 1314 assertThrowsNpe(() -> { Locale.lookupTag(null, tags); }); 1315 assertThrowsNpe(() -> { Locale.lookupTag(languageRanges, null); }); 1316 } 1317 1318 public void test_toLanguageTag() { 1319 Locale.Builder b = new Locale.Builder(); 1320 1321 // Empty builder. 1322 Locale l = b.build(); 1323 assertEquals("und", l.toLanguageTag()); 1324 1325 // Only language. 1326 b = new Locale.Builder(); 1327 b.setLanguage("en"); 1328 assertEquals("en", b.build().toLanguageTag()); 1329 1330 // Language & Region 1331 b = new Locale.Builder(); 1332 b.setLanguage("en").setRegion("US"); 1333 assertEquals("en-US", b.build().toLanguageTag()); 1334 1335 // Language & Script 1336 b = new Locale.Builder(); 1337 b.setLanguage("en").setScript("Latn"); 1338 assertEquals("en-Latn", b.build().toLanguageTag()); 1339 1340 // Language & Variant 1341 b = new Locale.Builder(); 1342 b.setLanguage("en").setVariant("foooo"); 1343 assertEquals("en-foooo", b.build().toLanguageTag()); 1344 1345 // Language / script & country 1346 b = new Locale.Builder(); 1347 b.setLanguage("en").setScript("Latn").setRegion("US"); 1348 assertEquals("en-Latn-US", b.build().toLanguageTag()); 1349 1350 // Language / script & variant 1351 b = new Locale.Builder(); 1352 b.setLanguage("en").setScript("Latn").setVariant("foooo"); 1353 assertEquals("en-Latn-foooo", b.build().toLanguageTag()); 1354 1355 // Language / script / country / variant. 1356 b = new Locale.Builder(); 1357 b.setLanguage("en").setScript("Latn").setVariant("foooo").setRegion("US"); 1358 assertEquals("en-Latn-US-foooo", b.build().toLanguageTag()); 1359 1360 // Language / extension 1361 b = new Locale.Builder(); 1362 b.setLanguage("en").setExtension('x', "fooo-baar"); 1363 assertEquals("en-x-fooo-baar", b.build().toLanguageTag()); 1364 1365 // Language & multiple extensions (including unicode). 1366 b = new Locale.Builder(); 1367 b.setLanguage("en"); 1368 b.addUnicodeLocaleAttribute("attrib"); 1369 b.addUnicodeLocaleAttribute("attrib2"); 1370 b.setExtension('f', "fo-baaz-ga-gaaz"); 1371 b.setExtension('x', "xo-baaz-ga-gaaz"); 1372 b.setExtension('z', "zo-baaz-ga-gaaz"); 1373 1374 l = b.build(); 1375 // Implicitly added because we added unicode locale attributes. 1376 assertEquals("attrib-attrib2", l.getExtension('u')); 1377 assertEquals("fo-baaz-ga-gaaz", l.getExtension('f')); 1378 assertEquals("xo-baaz-ga-gaaz", l.getExtension('x')); 1379 assertEquals("zo-baaz-ga-gaaz", l.getExtension('z')); 1380 1381 assertEquals("en-" + 1382 "f-fo-baaz-ga-gaaz-" + // extension tags in lexical order 1383 "u-attrib-attrib2-z-zo-baaz-ga-gaaz-" + // unicode attribs & keywords in lex order 1384 "x-xo-baaz-ga-gaaz", // private use extension unmodified. 1385 l.toLanguageTag()); 1386 } 1387 1388 public void test_toString() { 1389 Locale.Builder b = new Locale.Builder(); 1390 1391 // Empty builder. 1392 Locale l = b.build(); 1393 assertEquals("", l.toString()); 1394 1395 // Only language. 1396 b = new Locale.Builder(); 1397 b.setLanguage("en"); 1398 assertEquals("en", b.build().toString()); 1399 1400 // Only region 1401 b = new Locale.Builder(); 1402 b.setRegion("US"); 1403 assertEquals("_US", b.build().toString()); 1404 1405 // Language & Region 1406 b = new Locale.Builder(); 1407 b.setLanguage("en").setRegion("US"); 1408 assertEquals("en_US", b.build().toString()); 1409 1410 // Language & Script 1411 b = new Locale.Builder(); 1412 b.setLanguage("en").setScript("Latn"); 1413 assertEquals("en__#Latn", b.build().toString()); 1414 1415 // Language & Variant 1416 b = new Locale.Builder(); 1417 b.setLanguage("en").setVariant("foooo"); 1418 assertEquals("en__foooo", b.build().toString()); 1419 1420 // Language / script & country 1421 b = new Locale.Builder(); 1422 b.setLanguage("en").setScript("Latn").setRegion("US"); 1423 assertEquals("en_US_#Latn", b.build().toString()); 1424 1425 // Language / script & variant 1426 b = new Locale.Builder(); 1427 b.setLanguage("en").setScript("Latn").setVariant("foooo"); 1428 assertEquals("en__foooo_#Latn", b.build().toString()); 1429 1430 // Language / script / country / variant. 1431 b = new Locale.Builder(); 1432 b.setLanguage("en").setScript("Latn").setVariant("foooo").setRegion("US"); 1433 assertEquals("en_US_foooo_#Latn", b.build().toString()); 1434 1435 // Language / extension 1436 b = new Locale.Builder(); 1437 b.setLanguage("en").setExtension('x', "fooo-baar"); 1438 assertEquals("en__#x-fooo-baar", b.build().toString()); 1439 } 1440 1441 // Tests cases where our "guess" for the output size is incorrect. 1442 // 1443 // https://b.corp.google.com/issue?id=13414549 1444 public void test_toLanguageTag_largerTag() { 1445 Locale posix = new Locale.Builder() 1446 .setLanguage("en").setRegion("US").setVariant("POSIX") 1447 .build(); 1448 assertEquals("en-US-POSIX", posix.toLanguageTag()); 1449 } 1450 1451 public void test_forLanguageTag_grandFatheredLocale() { 1452 // Regular grandfathered locale. 1453 Locale gaulish = Locale.forLanguageTag("cel-gaulish"); 1454 assertEquals("xtg", gaulish.getLanguage()); 1455 assertEquals("cel-gaulish", gaulish.getExtension(Locale.PRIVATE_USE_EXTENSION)); 1456 assertEquals("", gaulish.getCountry()); 1457 assertEquals("", gaulish.getScript()); 1458 assertEquals("", gaulish.getVariant()); 1459 1460 // Irregular grandfathered locale. 1461 Locale enochian = Locale.forLanguageTag("i-enochian"); 1462 assertEquals("", enochian.getLanguage()); 1463 assertEquals("i-enochian", enochian.getExtension(Locale.PRIVATE_USE_EXTENSION)); 1464 assertEquals("", enochian.getCountry()); 1465 assertEquals("", enochian.getScript()); 1466 assertEquals("", enochian.getVariant()); 1467 } 1468 1469 // Test case from http://b/16811867 1470 public void testVariantsCaseSensitive() { 1471 final Locale locale = new Locale("en", "US", "variant"); 1472 assertEquals("variant", locale.getVariant()); 1473 assertEquals(locale, Locale.forLanguageTag(locale.toLanguageTag())); 1474 } 1475 1476 public void testArabicDigits() throws Exception { 1477 // ar-DZ uses latn digits by default, but we can override that. 1478 Locale ar_DZ = Locale.forLanguageTag("ar-DZ"); 1479 Locale ar_DZ_arab = Locale.forLanguageTag("ar-DZ-u-nu-arab"); 1480 Locale ar_DZ_latn = Locale.forLanguageTag("ar-DZ-u-nu-latn"); 1481 assertEquals('0', new DecimalFormatSymbols(ar_DZ).getZeroDigit()); 1482 assertEquals('\u0660', new DecimalFormatSymbols(ar_DZ_arab).getZeroDigit()); 1483 assertEquals('0', new DecimalFormatSymbols(ar_DZ_latn).getZeroDigit()); 1484 1485 // ar-EG uses arab digits by default, but we can override that. 1486 Locale ar_EG = Locale.forLanguageTag("ar-EG"); 1487 Locale ar_EG_arab = Locale.forLanguageTag("ar-EG-u-nu-arab"); 1488 Locale ar_EG_latn = Locale.forLanguageTag("ar-EG-u-nu-latn"); 1489 assertEquals('\u0660', new DecimalFormatSymbols(ar_EG).getZeroDigit()); 1490 assertEquals('\u0660', new DecimalFormatSymbols(ar_EG_arab).getZeroDigit()); 1491 assertEquals('0', new DecimalFormatSymbols(ar_EG_latn).getZeroDigit()); 1492 } 1493 1494 public void testDefaultLocale() throws Exception { 1495 final String userLanguage = System.getProperty("user.language", ""); 1496 final String userRegion = System.getProperty("user.region", ""); 1497 final String userLocale = System.getProperty("user.locale", ""); 1498 try { 1499 // Assert that user.locale gets priority. 1500 System.setUnchangeableSystemProperty("user.locale", "de-DE"); 1501 System.setUnchangeableSystemProperty("user.language", "en"); 1502 System.setUnchangeableSystemProperty("user.region", "US"); 1503 1504 Locale l = Locale.initDefault(); 1505 assertEquals("de", l.getLanguage()); 1506 assertEquals("DE", l.getCountry()); 1507 1508 // Assert that it's parsed as a full language tag. 1509 System.setUnchangeableSystemProperty("user.locale", "de-Latn-DE"); 1510 System.setUnchangeableSystemProperty("user.language", "en"); 1511 System.setUnchangeableSystemProperty("user.region", "US"); 1512 1513 l = Locale.initDefault(); 1514 assertEquals("de", l.getLanguage()); 1515 assertEquals("DE", l.getCountry()); 1516 assertEquals("Latn", l.getScript()); 1517 1518 // Assert that we don't end up with a null default locale or an exception. 1519 System.setUnchangeableSystemProperty("user.locale", "toolonglang-Latn-DE"); 1520 1521 // Note: pre-enso Locale#fromLanguageTag parser was more error-tolerant 1522 // then the current one. Result of bad language part of tag from line above 1523 // will be an empty Locale object. 1524 l = Locale.initDefault(); 1525 assertEquals("", l.getLanguage()); 1526 assertEquals("", l.getCountry()); 1527 } finally { 1528 System.setUnchangeableSystemProperty("user.language", userLanguage); 1529 System.setUnchangeableSystemProperty("user.region", userRegion); 1530 System.setUnchangeableSystemProperty("user.locale", userLocale); 1531 } 1532 } 1533 1534 // http://b/20252611 1535 public void testLegacyLocalesWithExtensions() { 1536 Locale ja_JP_JP = new Locale("ja", "JP", "JP"); 1537 assertEquals("ca-japanese", ja_JP_JP.getExtension(Locale.UNICODE_LOCALE_EXTENSION)); 1538 assertEquals("japanese", ja_JP_JP.getUnicodeLocaleType("ca")); 1539 1540 Locale th_TH_TH = new Locale("th", "TH", "TH"); 1541 assertEquals("nu-thai", th_TH_TH.getExtension(Locale.UNICODE_LOCALE_EXTENSION)); 1542 assertEquals("thai", th_TH_TH.getUnicodeLocaleType("nu")); 1543 } 1544 1545 // http://b/20252611 1546 public void testLowerCaseExtensionKeys() { 1547 // We must lowercase extension keys in forLanguageTag.. 1548 Locale ar_EG = Locale.forLanguageTag("ar-EG-U-nu-arab"); 1549 assertEquals("nu-arab", ar_EG.getExtension(Locale.UNICODE_LOCALE_EXTENSION)); 1550 assertEquals("ar-EG-u-nu-arab", ar_EG.toLanguageTag()); 1551 1552 // ... and in builders. 1553 Locale.Builder b = new Locale.Builder(); 1554 b.setLanguage("ar"); 1555 b.setRegion("EG"); 1556 b.setExtension('U', "nu-arab"); 1557 assertEquals("ar-EG-u-nu-arab", b.build().toLanguageTag()); 1558 1559 // Corollary : extension keys are case insensitive. 1560 b = new Locale.Builder(); 1561 b.setLanguage("ar"); 1562 b.setRegion("EG"); 1563 b.setExtension('U', "nu-arab"); 1564 b.setExtension('u', "nu-thai"); 1565 assertEquals("ar-EG-u-nu-thai", b.build().toLanguageTag()); 1566 } 1567 1568 // http://b/26387905 1569 public void test_SerializationBug_26387905() throws Exception { 1570 try (ObjectInputStream oinput = new ObjectInputStream(getClass() 1571 .getResource("/serialization/org/apache/harmony/tests/java/util/Locale_Bug_26387905.ser") 1572 .openStream())) { 1573 Locale l = (Locale) oinput.readObject(); 1574 } 1575 } 1576 1577 public void test_setDefault_withCategory() { 1578 final Locale defaultLocale = Locale.getDefault(); 1579 try { 1580 // Establish a baseline for the checks further down 1581 Locale.setDefault(Locale.US); 1582 assertEquals(Locale.US, Locale.getDefault(Locale.Category.FORMAT)); 1583 assertEquals(Locale.US, Locale.getDefault(Locale.Category.DISPLAY)); 1584 assertEquals(Locale.US, Locale.getDefault()); 1585 1586 Locale.setDefault(Locale.Category.FORMAT, Locale.UK); 1587 assertEquals(Locale.UK, Locale.getDefault(Locale.Category.FORMAT)); 1588 assertEquals(Locale.US, Locale.getDefault(Locale.Category.DISPLAY)); 1589 assertEquals(Locale.US, Locale.getDefault()); 1590 1591 Locale.setDefault(Locale.Category.DISPLAY, Locale.CANADA); 1592 assertEquals(Locale.UK, Locale.getDefault(Locale.Category.FORMAT)); 1593 assertEquals(Locale.CANADA, Locale.getDefault(Locale.Category.DISPLAY)); 1594 assertEquals(Locale.US, Locale.getDefault()); 1595 1596 Locale.setDefault(Locale.FRANCE); 1597 assertEquals(Locale.FRANCE, Locale.getDefault(Locale.Category.FORMAT)); 1598 assertEquals(Locale.FRANCE, Locale.getDefault(Locale.Category.DISPLAY)); 1599 assertEquals(Locale.FRANCE, Locale.getDefault()); 1600 1601 // Check that setDefault(Locale) sets all three defaults 1602 Locale.setDefault(Locale.US); 1603 assertEquals(Locale.US, Locale.getDefault(Locale.Category.FORMAT)); 1604 assertEquals(Locale.US, Locale.getDefault(Locale.Category.DISPLAY)); 1605 assertEquals(Locale.US, Locale.getDefault()); 1606 } finally { 1607 Locale.setDefault(defaultLocale); 1608 } 1609 } 1610 1611 private static List<Locale> localesOf(String... languageTags) { 1612 return localesOf(tagsOf(languageTags)); 1613 } 1614 1615 private static List<Locale> localesOf(List<String> languageTags) { 1616 List<Locale> result = new ArrayList<>(); 1617 for (String languageTag : languageTags) { 1618 result.add(Locale.forLanguageTag(languageTag)); 1619 } 1620 return Collections.unmodifiableList(result); 1621 } 1622 1623 private static List<String> tagsOf(String... tags) { 1624 List<String> result = new ArrayList<>(); 1625 for (String tag : tags) { 1626 result.add(tag.toLowerCase()); 1627 } 1628 return Collections.unmodifiableList(result); 1629 } 1630 1631 private static List<LanguageRange> languageRangesOf(String... languageRanges) { 1632 List<LanguageRange> result = new ArrayList<>(); 1633 for (String languageRange : languageRanges) { 1634 result.add(new LanguageRange(languageRange)); 1635 } 1636 return Collections.unmodifiableList(result); 1637 } 1638 1639 private static void assertFilter(List<String> filteredTags, List<LanguageRange> languageRanges, 1640 List<String> tags) { 1641 assertEquals(filteredTags, Locale.filterTags(languageRanges, tags)); 1642 1643 List<Locale> locales = localesOf(tags); 1644 List<Locale> filteredLocales = localesOf(filteredTags); 1645 assertEquals(filteredLocales, Locale.filter(languageRanges, locales)); 1646 } 1647 1648 private static void assertFilter(List<String> filteredTags, List<LanguageRange> languageRanges, 1649 List<String> tags, Locale.FilteringMode filteringMode) { 1650 assertEquals(filteredTags, 1651 Locale.filterTags(languageRanges, tags, filteringMode)); 1652 1653 List<Locale> locales = localesOf(tags); 1654 List<Locale> filteredLocales = localesOf(filteredTags); 1655 assertEquals(filteredLocales, Locale.filter(languageRanges, locales, filteringMode)); 1656 } 1657 1658 private static void assertThrowsNpe(Runnable runnable) { 1659 try { 1660 runnable.run(); 1661 fail("Should have thrown NullPointerException"); 1662 } catch (NullPointerException expected) { 1663 } 1664 } 1665 1666 private static void assertLookup(String expectedTag, List<LanguageRange> languageRanges, 1667 List<String> tags) { 1668 assertEquals(expectedTag.toLowerCase(), Locale.lookupTag(languageRanges, tags)); 1669 1670 assertEquals(Locale.forLanguageTag(expectedTag), 1671 Locale.lookup(languageRanges, localesOf(tags))); 1672 } 1673 1674 } 1675