Map C Locale to Invariant (#25373)
authorTarek Mahmoud Sayed <tarekms@microsoft.com>
Tue, 25 Jun 2019 20:23:56 +0000 (13:23 -0700)
committerJan Kotas <jkotas@microsoft.com>
Tue, 25 Jun 2019 20:23:56 +0000 (13:23 -0700)
ICU map the C locale to en-US_POSIX which has a non desired collation behavior as it doesn't support case insensitive comparison.
This change is to force mapping C locale to Invariant instead.

src/System.Private.CoreLib/shared/System/Globalization/CultureData.cs
src/corefx/System.Globalization.Native/pal_locale.c

index 03875c3..464019a 100644 (file)
@@ -697,6 +697,14 @@ namespace System.Globalization
                 return cd;
             }
 
+            if (cultureName.Length == 1 && (cultureName[0] == 'C' || cultureName[0] == 'c'))
+            {
+                // Always map the "C" locale to Invariant to avoid mapping it to en_US_POSIX on Linux because POSIX
+                // locale collation doesn't support case insensitive comparisons.
+                // We do the same mapping on Windows for the sake of consistency. 
+                return CultureData.Invariant;
+            }
+
             CultureData culture = new CultureData();
             culture._bUseOverrides = useUserOverride;
             culture._sRealName = cultureName;
index b7465df..99eea37 100644 (file)
@@ -124,39 +124,18 @@ int32_t FixupLocaleName(UChar* value, int32_t valueLength)
     return i;
 }
 
-static int IsEnvVarSet(const char* name)
-{
-    const char* value = getenv(name);
-
-    return (value != NULL) && (strcmp("", value) != 0);
-}
-
-// The behavior of uloc_getDefault() on POSIX systems is to query
-// setlocale(LC_MESSAGES) and use that value, unless it is C or
-// POSIX. In that case it tries to read LC_ALL, LC_MESSAGES and LANG
-// and then falls back to en_US_POSIX if none of them are set.
-//
-// en_US_POSIX is a weird locale since the collation rules treat 'a'
-// and 'A' as different letters even when ignoring case. Furthermore
-// it's common for LC_ALL, LC_MESSAGES and LANG to be unset when
-// running under Docker.
-//
-// We'd rather default to invariant in this case. If any of these
-// are set, we'll just call into ICU and let it do whatever
-// normalization it would do.
+// We use whatever ICU give us as the default locale except if it is en_US_POSIX. We'll map
+// this POSIX locale to Invariant instead. The reason is POSIX locale collation behavior 
+// is not desirable at all because it doesn't support case insensitive string comparisons.
 const char* DetectDefaultLocaleName()
 {
-    char* loc = setlocale(LC_MESSAGES, NULL);
-
-    if (loc != NULL && (strcmp("C", loc) == 0 || strcmp("POSIX", loc) == 0))
+    const char* icuLocale = uloc_getDefault();
+    if (strcmp(icuLocale, "en_US_POSIX") == 0)
     {
-        if (!IsEnvVarSet("LC_ALL") && !IsEnvVarSet("LC_MESSAGES") && !IsEnvVarSet("LANG"))
-        {
-            return "";
-        }
+        return "";
     }
 
-    return uloc_getDefault();
+    return icuLocale;
 }
 
 // GlobalizationNative_GetLocales gets all locale names and store it in the value buffer