best-available-locale-from-default-locale.js (5023B)
1 // |reftest| skip-if(!this.hasOwnProperty('Intl')) 2 3 if (typeof getDefaultLocale === "undefined") { 4 var getDefaultLocale = SpecialPowers.Cu.getJSTestingFunctions().getDefaultLocale; 5 } 6 if (typeof setDefaultLocale === "undefined") { 7 var setDefaultLocale = SpecialPowers.Cu.getJSTestingFunctions().setDefaultLocale; 8 } 9 10 let defaultLocale = null; 11 12 function withLocale(locale, fn) { 13 if (defaultLocale === null) 14 defaultLocale = getDefaultLocale(); 15 16 setDefaultLocale(locale); 17 try { 18 fn(); 19 } finally { 20 setDefaultLocale(defaultLocale); 21 } 22 } 23 24 // This test assumes Azerbaijani ("az") is a supported locale. 25 const supported = Intl.Collator.supportedLocalesOf("az"); 26 assertEq(supported.length, 1); 27 assertEq(supported[0], "az"); 28 29 withLocale("az", () => { 30 // Ensure the new default locale is now active. 31 assertEq(new Intl.Collator().resolvedOptions().locale, "az"); 32 33 // "az" is the active default locale, so explicitly requesting "az" should succeed. 34 assertEq(new Intl.Collator("az").resolvedOptions().locale, "az"); 35 36 // ICU doesn't provide a specialised "az-Cyrl" locale, so we fallback to "az". 37 assertEq(new Intl.Collator("az-Cyrl").resolvedOptions().locale, "az"); 38 39 // ICU doesn't provide a specialised "az-Cyrl-AZ" locale, so we fallback to "az". 40 assertEq(new Intl.Collator("az-Cyrl-AZ").resolvedOptions().locale, "az"); 41 }); 42 43 // As demonstrated above, "az-Cyrl-AZ" normally isn't a supported Intl.Collator locale. But when 44 // used as the default locale, it gets promoted to being supported, because its parent locale "az" 45 // is supported and can act as a fallback. 46 // 47 // This works as follows: 48 // We accept any default locale as long as it can be supported either explicitly or implicitly 49 // through a fallback. But when we claim a default locale is supported, we also need to make sure 50 // we report any parent locale as being supported. So when "az-Cyrl-AZ" is accepted as the 51 // default locale, we also need to report its parent locale "az-Cyrl" as a supported locale. 52 // 53 // The reason we're doing this, is to make sure we aren't limiting the supported default locale to 54 // the intersection of the sets of supported locales for each Intl service constructor. Also see 55 // the requirements in <https://tc39.es/ecma402/#sec-internal-slots>, which state that the default 56 // locale must be a member of [[AvailableLocales]] for every Intl service constructor. 57 // 58 // So the following statement must hold: 59 // 60 // ∀ Constructor ∈ IntlConstructors: DefaultLocale ∈ Constructor.[[AvailableLocales]] 61 // 62 // This can trivially be achieved when we restrict the default locale to: 63 // 64 // { RequestedLocale if RequestedLocale ∈ (∩ C.[[AvailableLocales]]) 65 // { C ∈ IntlConstructors 66 // { 67 // DefaultLocale = { Fallback(RequestedLocale) if Fallback(RequestedLocale) ∈ (∩ C.[[AvailableLocales]]) 68 // { C ∈ IntlConstructors 69 // { 70 // { LastDitchLocale otherwise 71 // 72 // But that severely restricts the possible default locales. For example, "az-Cyrl-AZ" is supported 73 // by all Intl constructors except Intl.Collator. Intl.Collator itself only provides explicit 74 // support for the parent locale "az". So with the trivial solution we'd need to mark "az-Cyrl-AZ" 75 // as an invalid default locale and instead use its fallback locale "az". 76 // 77 // So instead of that we're using the following approach: 78 // 79 // { RequestedLocale if RequestedLocale ∈ (∩ C.[[AvailableLocales]]) 80 // { C ∈ IntlConstructors 81 // { 82 // DefaultLocale = { RequestedLocale if Fallback(RequestedLocale) ∈ (∩ C.[[AvailableLocales]]) 83 // { C ∈ IntlConstructors 84 // { 85 // { LastDitchLocale otherwise 86 // 87 // So even when the requested default locale is only implicitly supported through a fallback, we 88 // still accept it as a valid default locale. 89 withLocale("az-Cyrl-AZ", () => { 90 // Ensure the new default locale is now active. 91 assertEq(new Intl.Collator().resolvedOptions().locale, "az-Cyrl-AZ"); 92 93 // "az-Cyrl-AZ" is the active default locale, so explicitly requesting the parent locale 94 // "az" should succeed. 95 assertEq(new Intl.Collator("az").resolvedOptions().locale, "az"); 96 97 // "az-Cyrl-AZ" is the active default locale, so explicitly requesting the parent locale 98 // "az-Cyrl" should succeed. 99 assertEq(new Intl.Collator("az-Cyrl").resolvedOptions().locale, "az-Cyrl"); 100 101 // "az-Cyrl-AZ" is the active default locale, so explicitly requesting "az-Cyrl-AZ" 102 // should succeed. 103 assertEq(new Intl.Collator("az-Cyrl-AZ").resolvedOptions().locale, "az-Cyrl-AZ"); 104 }); 105 106 if (typeof reportCompare === "function") 107 reportCompare(0, 0);