Ver Fonte

Fix hourCycle, ResolveLocale, platform.isLocaleAvailable

Jack Horton há 8 anos atrás
pai
commit
59adcf1527

+ 41 - 9
lib/Runtime/Library/InJavascript/Intl.js

@@ -528,6 +528,37 @@
         LANG_TAG_RE         = new RegExp(LANG_TAG,      'i'); // [1] language; [2] script; [3] region; [4] extensions
     })();
 
+    /**
+     * Returns an object representing the language, script, region, extension, and base of a language tag,
+     * or null if the language tag isn't valid.
+     * 
+     * @param {String} langtag a candidate BCP47 langtag
+     */
+    const parseLangtag = function (langtag) {
+        const parts = _.match(langtag, LANG_TAG_RE);
+        if (!parts || !parts[1]) {
+            return null;
+        }
+
+        const ret = { language: parts[1], base: parts[1] };
+        if (parts[2]) {
+            ret.script = parts[2];
+            ret.base += "-" + parts[2];
+        }
+
+        if (parts[3]) {
+            ret.region = parts[3];
+            ret.base += "-" + parts[3];
+        }
+
+        if (parts[4]) {
+            // strip the first character to turn -u-... into u-...
+            ret.extension = _.substring(parts[4], 1);
+        }
+
+        return ret;
+    }
+
     function IsWellFormedCurrencyCode(code) {
         code = Internal.ToString(code);
 
@@ -1459,21 +1490,22 @@
              */
             const LookupMatcher = function (requestedLocales) {
                 const result = bare();
-                _.forEach(requestedLocales, function (locale) {
-                    const parts = _.match(locale, LANG_TAG_RE);
-                    const noExtensionsLocale = parts ? (_.join(_.slice(parts, 1, 4), "-")) : undefined;
-                    // turn -u-... to u-... for the UnicodeExtensionValue algorithm
-                    const extension = parts ? _.substring(parts[4], 1) : undefined;
-                    const availableLocale = BestAvailableLocale(noExtensionsLocale);
+                for (let i = 0; i < requestedLocales.length; ++i) {
+                    const parsedLangtag = parseLangtag(requestedLocales[i]);
+                    if (parsedLangtag === null) {
+                        continue;
+                    }
+
+                    const availableLocale = BestAvailableLocale(parsedLangtag.base);
                     if (availableLocale !== undefined) {
                         result.locale = availableLocale;
-                        if (locale !== noExtensionsLocale) {
-                            result.extension = extension;
+                        if (requestedLocales[i] !== parsedLangtag.base) {
+                            result.extension = parsedLangtag.extension;
                         }
 
                         return result;
                     }
-                });
+                }
 
                 result.locale = GetDefaultLocale();
                 return result;

+ 1 - 1
lib/Runtime/PlatformAgnostic/Intl.h

@@ -118,7 +118,7 @@ namespace Intl
     bool IsWellFormedLanguageTag(_In_z_ const char16 *languageTag, _In_ const charcount_t cch);
     HRESULT NormalizeLanguageTag(_In_z_ const char16 *languageTag, _In_ const charcount_t cch,
         _Out_ char16 *normalized, _Out_ size_t *normalizedLength);
-    bool IsLocaleAvailable(_In_z_ const char *locale);
+    bool IsLocaleAvailable(_In_z_ const char *langtag);
     bool ResolveLocaleBestFit(_In_z_ const char16 *locale, _Out_ char16 *resolved);
     size_t GetUserDefaultLocaleName(_Out_ char16* langtag, _In_ size_t cchLangtag);
 

+ 8 - 2
lib/Runtime/PlatformAgnostic/Platform/Common/Intl.cpp

@@ -491,14 +491,20 @@ namespace Intl
         return ret;
     }
 
-    bool IsLocaleAvailable(_In_z_ const char *locale)
+    bool IsLocaleAvailable(_In_z_ const char *langtag)
     {
         int32_t countAvailable = uloc_countAvailable();
         Assert(countAvailable > 0);
+
+        UErrorCode status = U_ZERO_ERROR;
+        char localeID[ULOC_FULLNAME_CAPACITY] = { 0 };
+        int32_t length = 0;
+        uloc_forLanguageTag(langtag, localeID, ULOC_FULLNAME_CAPACITY, &length, &status);
+        ICU_ASSERT(status, length > 0);
         for (int i = 0; i < countAvailable; i++)
         {
             const char *candidate = uloc_getAvailable(i);
-            if (strcmp(locale, candidate) == 0)
+            if (strcmp(localeID, candidate) == 0)
             {
                 return true;
             }

+ 2 - 10
test/Intl/DateTimeFormat.js

@@ -25,7 +25,8 @@ const tests = [
         body: function () {
             const date = new Date(2000, 1, 1, 1, 1, 1);
             function test(options, expected, message) {
-                check(expected, ascii(new Intl.DateTimeFormat("en-US", options).format(date)), message);
+                check(expected, ascii(new Intl.DateTimeFormat("en-US", options).format(date)), `${message} - new Intl.DateTimeFormat().format(date)`);
+                check(expected, ascii(date.toLocaleString("en-US", options)), `${message} - date.toLocaleString()`);
             }
 
             test({ year: "numeric" }, "2000", "Formatting year as numeric");
@@ -79,15 +80,6 @@ const tests = [
             test("en-US", { hour12: false },   { hour12: null }, "Requesting hour12 without hour shouldn't do anything")
         }
     },
-    {
-        name: "Compatability with Date.prototype.toLocale*String",
-        body: function () {
-            const 
-            function test() {
-
-            }
-        }
-    }
 ];
 
 testRunner.runTests(tests, { verbose: WScript.Arguments[0] != "summary" });

+ 58 - 9
test/Intl/intl2018.js

@@ -48,13 +48,25 @@ const tests = [
         body: function () {
             const midnight = new Date(2000, 0, 1, 0, 0, 0);
             const noon = new Date(2000, 0, 1, 12, 0, 0);
-            function test(locale, useHourField, options, expectedHC, expectedHour12, expectedMidnight, expectedNoon) {
+            function test(locale, useHourField, options, expectedHC, expectedHour12) {
                 options = useHourField === false ? options : Object.assign({}, { hour: "2-digit" }, options);
                 const fmt = new Intl.DateTimeFormat(locale, options);
                 const message = `locale: "${locale}", options: ${JSON.stringify(options)}`;
                 assert.areEqual(expectedHC, fmt.resolvedOptions().hourCycle, `${message} - hourCycle is not correct`);
                 assert.areEqual(expectedHour12, fmt.resolvedOptions().hour12, `${message} - hour12 is not correct`);
                 if (useHourField === true) {
+                    const expectedNoon = {
+                        h11: "00",
+                        h12: "12",
+                        h23: "12",
+                        h24: "12",
+                    }[expectedHC];
+                    const expectedMidnight = {
+                        h11: "00",
+                        h12: "12",
+                        h23: "00",
+                        h24: "24",
+                    }[expectedHC];
                     assert.areEqual(expectedMidnight, fmt.formatToParts(midnight).find((p) => p.type === "hour").value, `${message} - midnight value was incorrect`);
                     assert.areEqual(expectedNoon, fmt.formatToParts(noon).find((p) => p.type === "hour").value, `${message} - noon value was incorrect`);
                 } else {
@@ -62,14 +74,51 @@ const tests = [
                 }
             }
 
-            test("en-US", false, undefined, undefined, undefined, undefined);
-            test("en-US", true, undefined, "h12", true, "12", "12");
-            test("en-US", true, { hour12: false }, "h23", false, "00", "12");
-            test("en-US", true, { hourCycle: "h24" }, "h24", false, "24", "12");
-            test("en-US", true, { hourCycle: "h24", hour12: true }, "h12", true, "12", "12");
-            test("en-US", true, { hourCycle: "h11", hour12: false }, "h23", false, "00", "12");
-            test("en-US-u-hc-h24", false, undefined, undefined, undefined, undefined);
-            test("en-US-u-hc-h24", true, undefined, "h24", false, "24", "12");
+            function withoutHour(locale, options) {
+                test(locale, false, options, undefined, undefined);
+            }
+
+            function withHour(locale, options, expectedHC, expectedHour12) {
+                test(locale, true, options, expectedHC, expectedHour12);
+            }
+
+            // ensure hourCycle and hour properties are not there when we don't ask for them
+            withoutHour("en-US", undefined);
+            withoutHour("en-US", { hourCycle: "h11" });
+            withoutHour("en-US", { hour12: true });
+            withoutHour("en-US", { hourCycle: "h11", hour12: false });
+            withoutHour("en-US-u-hc-h24", undefined);
+            withoutHour("en-US-u-hc-h24", { hourCycle: "h11" });
+            withoutHour("en-US-u-hc-h24", { hour12: true });
+            withoutHour("en-US-u-hc-h24", { hourCycle: "h11", hour12: false });
+
+            // ensure hourCycle and hour12 properties along with hour values are correct when we do ask for hour
+            withHour("en-US", undefined, "h12", true);
+            withHour("en-US", { hour12: false }, "h23", false);
+            withHour("en-US", { hourCycle: "h24" }, "h24", false);
+            withHour("en-US", { hourCycle: "h24", hour12: true }, "h12", true);
+            withHour("en-US", { hourCycle: "h11", hour12: false }, "h23", false);
+            withHour("en-US-u-hc-h24", undefined, "h24", false);
+            withHour("en-US-u-hc-h24", { hourCycle: "h23" }, "h23", false);
+            withHour("en-US-u-hc-h24", { hourCycle: "h11" }, "h11", true);
+            withHour("en-US-u-hc-h24", { hour12: false }, "h23", false);
+            withHour("en-US-u-hc-h24", { hour12: true }, "h12", true);
+
+            if (Intl.DateTimeFormat.supportedLocalesOf("en-GB").includes("en-GB")) {
+                withoutHour("en-GB", undefined);
+
+                withHour("en-GB", undefined, "h23", false);
+                withHour("en-GB", { hour12: true }, "h12", true);
+                withHour("en-GB-u-hc-h24", undefined, "h24", false);
+            }
+
+            if (Intl.DateTimeFormat.supportedLocalesOf("ja-JP").includes("ja-JP")) {
+                withoutHour("ja-JP", undefined);
+
+                withHour("ja-JP", undefined, "h23", false);
+                withHour("ja-JP", { hour12: true }, "h11", true);
+                withHour("ja-JP-u-hc-h12", undefined, "h12", true);
+            }
         }
     }
 ];

+ 7 - 0
test/Intl/rlexe.xml

@@ -64,6 +64,13 @@
       <tags>Intl</tags>
     </default>
   </test>
+  <test>
+    <default>
+      <files>intl2018.js</files>
+      <compile-flags>-args summary -endargs</compile-flags>
+      <tags>Intl,require_icu</tags>
+    </default>
+  </test>
   <test>
     <default>
       <files>IntlHiddenInternals.js</files>

+ 6 - 0
test/runtests.cmd

@@ -329,6 +329,12 @@ goto :main
     set _TAGS=%_TAGS% -tags:Slow
   )
 
+  if "%IntlICU%" == "true" (
+    set _NOTTAGS=%_NOTTAGS% -nottags:require_winglob
+  ) else (
+    set _NOTTAGS=%_NOTTAGS% -nottags:require_icu
+  )
+
   if not "%NUM_RL_THREADS%" == "" (
     set _RL_THREAD_FLAGS=-threads:%NUM_RL_THREADS%
   )

+ 6 - 0
test/runtests.py

@@ -156,6 +156,12 @@ if sys.platform != 'win32':
     not_tags.add('require_winglob')
     not_tags.add('require_simd')
 
+if sys.platform == 'win32':
+    if os.environ.get('IntlICU') == 'true':
+        not_tags.add('require_winglob')
+    else
+        not_tags.add('require_icu')
+
 if args.sanitize != None:
     not_tags.add('exclude_sanitize_'+args.sanitize)