Use ICU on Windows when available (#34645)
authorSantiago Fernandez Madero <safern@microsoft.com>
Tue, 14 Apr 2020 20:45:15 +0000 (13:45 -0700)
committerGitHub <noreply@github.com>
Tue, 14 Apr 2020 20:45:15 +0000 (13:45 -0700)
* Use ICU when available on Windows

* Fix System.Globalization tests

* Fix System.Globalization.Extensions tests

* Fix System.Runtime tests

* Add System.Globalization.Nls.Tests to test UseNls runtime switch

* Add System.Globalization.Extensions.Nls.Tests to force them to run on NLS

* Add System.Runtime.Nls.Tests.csproj to test Nls behavior

* Fix left over from GlobalizationTestMode

* PR Feedback

* PR Feedback 2

* Fix mono build

* Fix failing tests

* Fix typo

* Fix bad merge in projitems for tvOS

* PR Feedback 3

* PR Feedback 4

95 files changed:
src/coreclr/src/System.Private.CoreLib/PinvokeAnalyzerExceptionList.analyzerdata
src/coreclr/src/System.Private.CoreLib/System.Private.CoreLib.csproj
src/coreclr/src/System.Private.CoreLib/src/System/Globalization/GlobalizationMode.Unix.cs
src/coreclr/src/System.Private.CoreLib/src/System/Globalization/GlobalizationMode.Windows.cs
src/coreclr/src/System.Private.CoreLib/src/System/Globalization/GlobalizationMode.cs
src/libraries/Common/src/Interop/Interop.Calendar.cs [moved from src/libraries/Common/src/Interop/Unix/System.Globalization.Native/Interop.Calendar.cs with 100% similarity]
src/libraries/Common/src/Interop/Interop.Casing.cs [moved from src/libraries/Common/src/Interop/Unix/System.Globalization.Native/Interop.Casing.cs with 100% similarity]
src/libraries/Common/src/Interop/Interop.Collation.cs [moved from src/libraries/Common/src/Interop/Unix/System.Globalization.Native/Interop.Collation.cs with 100% similarity]
src/libraries/Common/src/Interop/Interop.ICU.cs [moved from src/libraries/Common/src/Interop/Unix/System.Globalization.Native/Interop.ICU.cs with 100% similarity]
src/libraries/Common/src/Interop/Interop.Idna.cs [moved from src/libraries/Common/src/Interop/Unix/System.Globalization.Native/Interop.Idna.cs with 100% similarity]
src/libraries/Common/src/Interop/Interop.Libraries.cs [new file with mode: 0644]
src/libraries/Common/src/Interop/Interop.Locale.cs [moved from src/libraries/Common/src/Interop/Unix/System.Globalization.Native/Interop.Locale.cs with 100% similarity]
src/libraries/Common/src/Interop/Interop.Normalization.cs [moved from src/libraries/Common/src/Interop/Unix/System.Globalization.Native/Interop.Normalization.cs with 100% similarity]
src/libraries/Common/src/Interop/Interop.ResultCode.cs [moved from src/libraries/Common/src/Interop/Unix/System.Globalization.Native/Interop.ResultCode.cs with 100% similarity]
src/libraries/Common/src/Interop/Interop.TimeZoneInfo.cs [moved from src/libraries/Common/src/Interop/Unix/System.Globalization.Native/Interop.TimeZoneInfo.cs with 100% similarity]
src/libraries/Common/src/Interop/Interop.Utils.cs [moved from src/libraries/Common/src/Interop/Unix/System.Globalization.Native/Interop.Utils.cs with 100% similarity]
src/libraries/Common/src/Interop/Unix/Interop.Libraries.cs
src/libraries/Common/tests/TestUtilities/System/PlatformDetection.Unix.cs
src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs
src/libraries/Common/tests/Tests/System/StringTests.cs
src/libraries/Native/Unix/Common/pal_atomic.h
src/libraries/Native/Unix/System.Globalization.Native/pal_collation.c
src/libraries/Native/Unix/System.Globalization.Native/pal_icushim.c
src/libraries/System.Data.Common/tests/System/Data/SqlTypes/SqlStringSortingTest.cs
src/libraries/System.Globalization.Calendars/tests/System/Globalization/CalendarTestBase.cs
src/libraries/System.Globalization.Calendars/tests/TaiwanCalendar/TaiwanCalendarDaysAndMonths.cs
src/libraries/System.Globalization.Extensions/System.Globalization.Extensions.sln
src/libraries/System.Globalization.Extensions/tests/GetStringComparerTests.cs
src/libraries/System.Globalization.Extensions/tests/IdnMapping/IdnMappingGetAsciiTests.cs
src/libraries/System.Globalization.Extensions/tests/IdnMapping/IdnMappingGetUnicodeTests.cs
src/libraries/System.Globalization.Extensions/tests/IdnMapping/IdnMappingUseStd3AsciiRulesTests.cs
src/libraries/System.Globalization.Extensions/tests/NlsTests/System.Globalization.Extensions.Nls.Tests.csproj [new file with mode: 0644]
src/libraries/System.Globalization.Extensions/tests/NlsTests/runtimeconfig.template.json [new file with mode: 0644]
src/libraries/System.Globalization/System.Globalization.sln
src/libraries/System.Globalization/tests/CompareInfo/CompareInfoTests.Compare.cs
src/libraries/System.Globalization/tests/CompareInfo/CompareInfoTests.IndexOf.cs
src/libraries/System.Globalization/tests/CompareInfo/CompareInfoTests.IsPrefix.cs
src/libraries/System.Globalization/tests/CompareInfo/CompareInfoTests.IsSuffix.cs
src/libraries/System.Globalization/tests/CompareInfo/CompareInfoTests.LastIndexOf.cs
src/libraries/System.Globalization/tests/CompareInfo/CompareInfoTests.cs
src/libraries/System.Globalization/tests/CultureInfo/CultureInfoAll.cs
src/libraries/System.Globalization/tests/DateTimeFormatInfo/DateTimeFormatInfoData.cs
src/libraries/System.Globalization/tests/DateTimeFormatInfo/DateTimeFormatInfoLongTimePattern.cs
src/libraries/System.Globalization/tests/DateTimeFormatInfo/DateTimeFormatInfoTests.cs
src/libraries/System.Globalization/tests/IcuTests.cs [new file with mode: 0644]
src/libraries/System.Globalization/tests/Invariant/InvariantMode.cs
src/libraries/System.Globalization/tests/NlsTests/NlsSwitchTests.cs [new file with mode: 0644]
src/libraries/System.Globalization/tests/NlsTests/System.Globalization.Nls.Tests.csproj [new file with mode: 0644]
src/libraries/System.Globalization/tests/NlsTests/runtimeconfig.template.json [new file with mode: 0644]
src/libraries/System.Globalization/tests/NumberFormatInfo/NumberFormatInfoCurrencyDecimalDigits.cs
src/libraries/System.Globalization/tests/NumberFormatInfo/NumberFormatInfoData.cs
src/libraries/System.Globalization/tests/NumberFormatInfo/NumberFormatInfoNumberDecimalDigits.cs
src/libraries/System.Globalization/tests/NumberFormatInfo/NumberFormatInfoPercentNegativePattern.cs
src/libraries/System.Globalization/tests/NumberFormatInfo/NumberFormatInfoPercentPositivePattern.cs
src/libraries/System.Globalization/tests/System.Globalization.Tests.csproj
src/libraries/System.Globalization/tests/System/Globalization/RegionInfoTests.cs
src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems
src/libraries/System.Private.CoreLib/src/System/Globalization/Calendar.cs
src/libraries/System.Private.CoreLib/src/System/Globalization/CalendarData.Icu.cs [moved from src/libraries/System.Private.CoreLib/src/System/Globalization/CalendarData.Unix.cs with 94% similarity]
src/libraries/System.Private.CoreLib/src/System/Globalization/CalendarData.Nls.cs [moved from src/libraries/System.Private.CoreLib/src/System/Globalization/CalendarData.Windows.cs with 94% similarity]
src/libraries/System.Private.CoreLib/src/System/Globalization/CalendarData.cs
src/libraries/System.Private.CoreLib/src/System/Globalization/CompareInfo.Icu.cs [moved from src/libraries/System.Private.CoreLib/src/System/Globalization/CompareInfo.Unix.cs with 94% similarity]
src/libraries/System.Private.CoreLib/src/System/Globalization/CompareInfo.Nls.cs [moved from src/libraries/System.Private.CoreLib/src/System/Globalization/CompareInfo.Windows.cs with 90% similarity]
src/libraries/System.Private.CoreLib/src/System/Globalization/CompareInfo.cs
src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.Icu.cs [moved from src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.Unix.cs with 70% similarity]
src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.Nls.cs [moved from src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.Windows.cs with 88% similarity]
src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.cs
src/libraries/System.Private.CoreLib/src/System/Globalization/CultureInfo.Icu.cs [moved from src/libraries/System.Private.CoreLib/src/System/Globalization/CultureInfo.Unix.cs with 76% similarity]
src/libraries/System.Private.CoreLib/src/System/Globalization/CultureInfo.Nls.cs [moved from src/libraries/System.Private.CoreLib/src/System/Globalization/CultureInfo.Windows.cs with 88% similarity]
src/libraries/System.Private.CoreLib/src/System/Globalization/CultureInfo.cs
src/libraries/System.Private.CoreLib/src/System/Globalization/IcuLocaleData.cs [moved from src/libraries/System.Private.CoreLib/src/System/Globalization/LocaleData.Unix.cs with 99% similarity]
src/libraries/System.Private.CoreLib/src/System/Globalization/IdnMapping.Icu.cs [moved from src/libraries/System.Private.CoreLib/src/System/Globalization/IdnMapping.Unix.cs with 84% similarity]
src/libraries/System.Private.CoreLib/src/System/Globalization/IdnMapping.Nls.cs [moved from src/libraries/System.Private.CoreLib/src/System/Globalization/IdnMapping.Windows.cs with 77% similarity]
src/libraries/System.Private.CoreLib/src/System/Globalization/IdnMapping.cs
src/libraries/System.Private.CoreLib/src/System/Globalization/JapaneseCalendar.Icu.cs [moved from src/libraries/System.Private.CoreLib/src/System/Globalization/JapaneseCalendar.Unix.cs with 96% similarity]
src/libraries/System.Private.CoreLib/src/System/Globalization/JapaneseCalendar.Nls.cs [moved from src/libraries/System.Private.CoreLib/src/System/Globalization/JapaneseCalendar.Win32.cs with 92% similarity]
src/libraries/System.Private.CoreLib/src/System/Globalization/JapaneseCalendar.cs
src/libraries/System.Private.CoreLib/src/System/Globalization/Normalization.Icu.cs [moved from src/libraries/System.Private.CoreLib/src/System/Globalization/Normalization.Unix.cs with 88% similarity]
src/libraries/System.Private.CoreLib/src/System/Globalization/Normalization.Nls.cs [moved from src/libraries/System.Private.CoreLib/src/System/Globalization/Normalization.Windows.cs with 88% similarity]
src/libraries/System.Private.CoreLib/src/System/Globalization/Normalization.cs [new file with mode: 0644]
src/libraries/System.Private.CoreLib/src/System/Globalization/TextInfo.Icu.cs [moved from src/libraries/System.Private.CoreLib/src/System/Globalization/TextInfo.Unix.cs with 90% similarity]
src/libraries/System.Private.CoreLib/src/System/Globalization/TextInfo.Nls.cs [moved from src/libraries/System.Private.CoreLib/src/System/Globalization/TextInfo.Windows.cs with 88% similarity]
src/libraries/System.Private.CoreLib/src/System/Globalization/TextInfo.cs
src/libraries/System.Runtime/tests/NlsTests/System.Runtime.Nls.Tests.csproj [new file with mode: 0644]
src/libraries/System.Runtime/tests/NlsTests/runtimeconfig.template.json [new file with mode: 0644]
src/libraries/System.Runtime/tests/System.Runtime.Tests.csproj
src/libraries/System.Runtime/tests/System/Text/RuneTests.cs
src/libraries/System.Runtime/tests/System/Uri.MethodsTests.cs
src/libraries/System.Utf8String.Experimental/tests/System/Utf8SpanTests.Comparison.cs
src/libraries/System.Utf8String.Experimental/tests/System/Utf8SpanTests.Searching.cs
src/libraries/System.Utf8String.Experimental/tests/System/Utf8SpanTests.cs
src/libraries/System.Utf8String.Experimental/tests/System/Utf8StringTests.Searching.cs
src/libraries/System.Utf8String.Experimental/tests/System/Utf8StringTests.cs
src/mono/netcore/System.Private.CoreLib/src/System/Globalization/GlobalizationMode.Unix.Mono.cs
src/mono/netcore/System.Private.CoreLib/src/System/Globalization/GlobalizationMode.Windows.Mono.cs

index 141a5c2..c53e2b7 100644 (file)
@@ -6,5 +6,41 @@ normaliz.dll!NormalizeString
 user32.dll!GetProcessWindowStation
 user32.dll!GetUserObjectInformationW
 
-<!-- GetGeoInfo is supported by the analyzer complain for nit using GetGeoInfoW instead and we need to keep the style of not using 'W' in the names -->
-kernel32.dll!GetGeoInfo
\ No newline at end of file
+<!-- GetGeoInfo is supported by the analyzer complain for not using GetGeoInfoW instead and we need to keep the style of not using 'W' in the names -->
+kernel32.dll!GetGeoInfo
+
+<!-- PInvokes to System.Globalization.Native shim -->
+libSystem.Globalization.Native!GlobalizationNative_ChangeCase
+libSystem.Globalization.Native!GlobalizationNative_ChangeCaseInvariant
+libSystem.Globalization.Native!GlobalizationNative_ChangeCaseTurkish
+libSystem.Globalization.Native!GlobalizationNative_CloseSortHandle
+libSystem.Globalization.Native!GlobalizationNative_CompareString
+libSystem.Globalization.Native!GlobalizationNative_CompareStringOrdinalIgnoreCase
+libSystem.Globalization.Native!GlobalizationNative_EndsWith
+libSystem.Globalization.Native!GlobalizationNative_EnumCalendarInfo
+libSystem.Globalization.Native!GlobalizationNative_GetCalendarInfo
+libSystem.Globalization.Native!GlobalizationNative_GetCalendars
+libSystem.Globalization.Native!GlobalizationNative_GetDefaultLocaleName
+libSystem.Globalization.Native!GlobalizationNative_GetICUVersion
+libSystem.Globalization.Native!GlobalizationNative_GetJapaneseEraStartDate
+libSystem.Globalization.Native!GlobalizationNative_GetLatestJapaneseEra
+libSystem.Globalization.Native!GlobalizationNative_GetLocaleInfoGroupingSizes
+libSystem.Globalization.Native!GlobalizationNative_GetLocaleInfoInt
+libSystem.Globalization.Native!GlobalizationNative_GetLocaleInfoString
+libSystem.Globalization.Native!GlobalizationNative_GetLocaleName
+libSystem.Globalization.Native!GlobalizationNative_GetLocales
+libSystem.Globalization.Native!GlobalizationNative_GetLocaleTimeFormat
+libSystem.Globalization.Native!GlobalizationNative_GetSortHandle
+libSystem.Globalization.Native!GlobalizationNative_GetSortKey
+libSystem.Globalization.Native!GlobalizationNative_GetSortVersion
+libSystem.Globalization.Native!GlobalizationNative_GetTimeZoneDisplayName
+libSystem.Globalization.Native!GlobalizationNative_IndexOf
+libSystem.Globalization.Native!GlobalizationNative_IndexOfOrdinalIgnoreCase
+libSystem.Globalization.Native!GlobalizationNative_IsNormalized
+libSystem.Globalization.Native!GlobalizationNative_IsPredefinedLocale
+libSystem.Globalization.Native!GlobalizationNative_LastIndexOf
+libSystem.Globalization.Native!GlobalizationNative_LoadICU
+libSystem.Globalization.Native!GlobalizationNative_NormalizeString
+libSystem.Globalization.Native!GlobalizationNative_StartsWith
+libSystem.Globalization.Native!GlobalizationNative_ToAscii
+libSystem.Globalization.Native!GlobalizationNative_ToUnicode
index e1d9a09..b0456f3 100644 (file)
     <Compile Include="$(BclSourcesRoot)\System\Variant.cs" />
   </ItemGroup>
   <ItemGroup Condition="'$(TargetsUnix)' == 'true'">
-    <Compile Include="$(CommonPath)Interop\Windows\Interop.BOOL.cs"> <!-- The CLR internally uses a BOOL type analogous to the Windows BOOL type on Unix -->
-      <Link>Common\Interop\Windows\Interop.BOOL.cs</Link>
-    </Compile>
     <Compile Include="$(BclSourcesRoot)\Interop\Unix\Interop.Libraries.cs" />
     <Compile Include="$(BclSourcesRoot)\System\DateTime.Unix.CoreCLR.cs" />
     <Compile Include="$(BclSourcesRoot)\System\Globalization\GlobalizationMode.Unix.cs" />
index dccd60c..6cb513a 100644 (file)
@@ -6,6 +6,12 @@ namespace System.Globalization
 {
     internal static partial class GlobalizationMode
     {
+        // Order of these properties in Windows matter because GetUseIcuMode is dependent on Invariant.
+        // So we need Invariant to be initialized first.
+        internal static bool Invariant { get; } = GetGlobalizationInvariantMode();
+
+        internal static bool UseNls => false;
+
         private static bool GetGlobalizationInvariantMode()
         {
             bool invariantEnabled = GetInvariantSwitchValue();
index f5def09..50285f0 100644 (file)
@@ -6,9 +6,12 @@ namespace System.Globalization
 {
     internal static partial class GlobalizationMode
     {
-        private static bool GetGlobalizationInvariantMode()
-        {
-            return GetInvariantSwitchValue();
-        }
+        // Order of these properties in Windows matter because GetUseIcuMode is dependent on Invariant.
+        // So we need Invariant to be initialized first.
+        internal static bool Invariant { get; } = GetInvariantSwitchValue();
+
+        internal static bool UseNls { get; } = !Invariant &&
+            (GetSwitchValue("System.Globalization.UseNls", "DOTNET_SYSTEM_GLOBALIZATION_USENLS") ||
+                Interop.Globalization.LoadICU() == 0);
     }
 }
index b216677..1433e52 100644 (file)
@@ -6,18 +6,18 @@ namespace System.Globalization
 {
     internal static partial class GlobalizationMode
     {
-        internal static bool Invariant { get; } = GetGlobalizationInvariantMode();
+        private static bool GetInvariantSwitchValue() =>
+            GetSwitchValue("System.Globalization.Invariant", "DOTNET_SYSTEM_GLOBALIZATION_INVARIANT");
 
-        // GetInvariantSwitchValue calls CLRConfig first to detect if the switch is defined in the config file.
+        // GetSwitchValue calls CLRConfig first to detect if the switch is defined in the config file.
         // if the switch is defined we just use the value of this switch. otherwise, we'll try to get the switch
         // value from the environment variable if it is defined.
-        internal static bool GetInvariantSwitchValue()
+        private static bool GetSwitchValue(string switchName, string envVariable)
         {
-            bool ret = CLRConfig.GetBoolValue("System.Globalization.Invariant", out bool exist);
+            bool ret = CLRConfig.GetBoolValue(switchName, out bool exist);
             if (!exist)
             {
-                // Linux doesn't support environment variable names include dots
-                string? switchValue = Environment.GetEnvironmentVariable("DOTNET_SYSTEM_GLOBALIZATION_INVARIANT");
+                string? switchValue = Environment.GetEnvironmentVariable(envVariable);
                 if (switchValue != null)
                 {
                     ret = bool.IsTrueStringIgnoreCase(switchValue) || switchValue.Equals("1");
diff --git a/src/libraries/Common/src/Interop/Interop.Libraries.cs b/src/libraries/Common/src/Interop/Interop.Libraries.cs
new file mode 100644 (file)
index 0000000..4fee38a
--- /dev/null
@@ -0,0 +1,11 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+internal static partial class Interop
+{
+    internal static partial class Libraries
+    {
+        internal const string GlobalizationNative = "libSystem.Globalization.Native";
+    }
+}
index 2ee0c3e..f511948 100644 (file)
@@ -8,7 +8,6 @@ internal static partial class Interop
     {
         // Shims
         internal const string SystemNative = "libSystem.Native";
-        internal const string GlobalizationNative = "libSystem.Globalization.Native";
         internal const string NetSecurityNative = "libSystem.Net.Security.Native";
         internal const string CryptoNative = "libSystem.Security.Cryptography.Native.OpenSsl";
         internal const string CompressionNative = "libSystem.IO.Compression.Native";
index 58aa950..4ecee83 100644 (file)
@@ -2,7 +2,6 @@
 // The .NET Foundation licenses this file to you under the MIT license.
 // See the LICENSE file in the project root for more information.
 
-using System.Reflection;
 using System.Runtime.InteropServices;
 using System.Xml.Linq;
 
@@ -51,9 +50,6 @@ namespace System
         public static bool IsNotFedoraOrRedHatFamily => !IsFedora && !IsRedHatFamily;
         public static bool IsNotDebian10 => !IsDebian10;
 
-        private static Lazy<Version> m_icuVersion = new Lazy<Version>(GetICUVersion);
-        public static Version ICUVersion => m_icuVersion.Value;
-
         public static bool IsSuperUser => !IsWindows ?
             libc.geteuid() == 0 :
             throw new PlatformNotSupportedException();
@@ -110,25 +106,6 @@ namespace System
             }
         }
 
-        private static Version GetICUVersion()
-        {
-            int version = 0;
-            Type interopGlobalization = Type.GetType("Interop+Globalization");
-            if (interopGlobalization != null)
-            {
-                MethodInfo methodInfo = interopGlobalization.GetMethod("GetICUVersion", BindingFlags.NonPublic | BindingFlags.Static);
-                if (methodInfo != null)
-                {
-                    version = (int)methodInfo.Invoke(null, null);
-                }
-            }
-
-            return new Version( version & 0xFF,
-                            (version >> 8)  & 0xFF,
-                            (version >> 16) & 0xFF,
-                                version >> 24);
-        }
-
         private static Version GetOSXProductVersion()
         {
             if (IsOSX)
index a04e3b1..e6b1845 100644 (file)
@@ -4,6 +4,7 @@
 
 using System.IO;
 using System.Security;
+using System.Reflection;
 using System.Runtime.InteropServices;
 using System.Runtime.CompilerServices;
 using Microsoft.Win32;
@@ -150,6 +151,35 @@ namespace System
             }
         }
 
+        private static Lazy<Version> m_icuVersion = new Lazy<Version>(GetICUVersion);
+        public static Version ICUVersion => m_icuVersion.Value;
+
+        public static bool IsIcuGlobalization => ICUVersion > new Version(0,0,0,0);
+        public static bool IsNlsGlobalization => !IsIcuGlobalization;
+
+        private static Version GetICUVersion()
+        {
+            int version = 0;
+            try
+            {
+                Type interopGlobalization = Type.GetType("Interop+Globalization");
+                if (interopGlobalization != null)
+                {
+                    MethodInfo methodInfo = interopGlobalization.GetMethod("GetICUVersion", BindingFlags.NonPublic | BindingFlags.Static);
+                    if (methodInfo != null)
+                    {
+                        version = (int)methodInfo.Invoke(null, null);
+                    }
+                }
+            }
+            catch { }
+
+            return new Version(version >> 24,
+                              (version >> 16) & 0xFF,
+                              (version >> 8) & 0xFF,
+                              version & 0xFF);
+        }
+
         private static bool GetIsInContainer()
         {
             if (IsWindows)
index 18f494d..55ff465 100644 (file)
@@ -12,6 +12,7 @@ using System.Text;
 using System.Threading;
 using System.Threading.Tasks;
 using Microsoft.DotNet.RemoteExecutor;
+using Microsoft.DotNet.XUnitExtensions;
 using Xunit;
 
 #pragma warning disable xUnit2009 // these are the tests for String and so should be using the explicit methods on String
@@ -1678,8 +1679,10 @@ namespace System.Tests
             Assert.Equal(expected, s.AsSpan().EndsWith(value.AsSpan(), comparisonType));
         }
 
-        [Theory]
-        [ActiveIssue("https://github.com/dotnet/runtime/issues/4673", TestPlatforms.AnyUnix)]
+        // NOTE: This is by design. ICU ignores the null characters (i.e. null characters have no weights for the string comparison).
+        // For desired behavior, use ordinal comparison instead of linguistic comparison.
+        // This is a known difference between NLS and ICU (https://github.com/dotnet/runtime/issues/4673).
+        [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNlsGlobalization))]
         [InlineData(StringComparison.CurrentCulture)]
         [InlineData(StringComparison.CurrentCultureIgnoreCase)]
         [InlineData(StringComparison.Ordinal)]
@@ -1699,11 +1702,10 @@ namespace System.Tests
             Assert.False("test".AsSpan().EndsWith("\0st".AsSpan(), comparison));
         }
 
-        // NOTE: This is by design. Unix ignores the null characters (i.e. null characters have no weights for the string comparison).
+        // NOTE: This is by design. ICU ignores the null characters (i.e. null characters have no weights for the string comparison).
         // For desired behavior, use ordinal comparison instead of linguistic comparison.
-        // This is a known difference between Windows and Unix (https://github.com/dotnet/runtime/issues/4673).
-        [Theory]
-        [PlatformSpecific(TestPlatforms.Windows)]
+        // This is a known difference between NLS and ICU (https://github.com/dotnet/runtime/issues/4673).
+        [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNlsGlobalization))]
         [InlineData(StringComparison.InvariantCulture)]
         [InlineData(StringComparison.InvariantCultureIgnoreCase)]
         public static void EndsWith_NullInStrings_NonOrdinal(StringComparison comparison)
@@ -1862,11 +1864,11 @@ namespace System.Tests
                     string s1 = new string(first);
                     string s2 = new string(second);
 
-                    //On Linux there are some characters in the range of 0~32 which has a sort weight.
-                    //For example null character on Linux will be ignored if it is compared to anything
-                    //while on Windows null will be always compared as ordinal.
-                    //For desired behavior, use ordinal comparison instead of linguistic comparison.
-                    //This is a known difference between Windows and Unix (https://github.com/dotnet/runtime/issues/4673).
+                    // On ICU there are some characters in the range of 0~32 which have a sort weight.
+                    // For example null character on ICU will be ignored if it is compared to anything
+                    // while on NLS null will be always compared as ordinal.
+                    // For desired behavior, use ordinal comparison instead of linguistic comparison.
+                    // This is a known difference between NLS and ICU (https://github.com/dotnet/runtime/issues/4673).
                     bool b = s1.EndsWith(s2, StringComparison.Ordinal);
                     Assert.False(b);
 
@@ -2524,9 +2526,9 @@ namespace System.Tests
             yield return new object[] { StringComparison.Ordinal, false };
             yield return new object[] { StringComparison.OrdinalIgnoreCase, false };
 
-            // Windows and ICU disagree about how these strings compare in the default locale.
-            yield return new object[] { StringComparison.InvariantCulture, PlatformDetection.IsWindows };
-            yield return new object[] { StringComparison.InvariantCultureIgnoreCase, PlatformDetection.IsWindows };
+            // NLS and ICU disagree about how these strings compare in the default locale.
+            yield return new object[] { StringComparison.InvariantCulture, PlatformDetection.IsNlsGlobalization };
+            yield return new object[] { StringComparison.InvariantCultureIgnoreCase, PlatformDetection.IsNlsGlobalization };
         }
 
         [Theory]
@@ -2616,7 +2618,7 @@ namespace System.Tests
 #pragma warning restore IDE0043 // Format string contains invalid placeholder
         }
 
-        [Theory]
+        [ConditionalTheory]
         [InlineData("Hello", 'l', 0, 5, 2)]
         [InlineData("Hello", 'x', 0, 5, -1)]
         [InlineData("Hello", 'l', 1, 4, 2)]
@@ -2628,9 +2630,7 @@ namespace System.Tests
         [InlineData("Hello", 'l', 0, 3, 2)]
         [InlineData("Hello", 'o', 5, 0, -1)]
         [InlineData("H" + SoftHyphen + "ello", 'e', 0, 3, 2)]
-        // For some reason, this is failing on *nix with ordinal comparisons.
-        // Possibly related issue: https://github.com/dotnet/runtime/issues/4673
-        // [InlineData("Hello", '\0', 0, 5, -1)] // .NET strings are terminated with a null character, but they should not be included as part of the string
+        [InlineData("Hello", '\0', 0, 5, -1)] // .NET strings are terminated with a null character, but they should not be included as part of the string
         [InlineData("\ud800\udfff", '\ud800', 0, 1, 0)] // Surrogate characters
         [InlineData("ABCDEFGHIJKLMNOPQRSTUVWXYZ", 'A', 0, 26, 0)]
         [InlineData("ABCDEFGHIJKLMNOPQRSTUVWXYZ", 'B', 1, 25, 1)]
@@ -2668,6 +2668,14 @@ namespace System.Tests
         [InlineData("", 'H', 0, 0, -1)]
         public static void IndexOf_SingleLetter(string s, char target, int startIndex, int count, int expected)
         {
+            // This is by design. ICU ignores the null characters (i.e. null characters have no weights for the string comparison).
+            // For desired behavior, use ordinal comparison instead of linguistic comparison.
+            // This is a known difference between NLS and ICU (https://github.com/dotnet/runtime/issues/4673).
+            if (target == '\0' && PlatformDetection.IsIcuGlobalization)
+            {
+                throw new SkipTestException("Target \\0 is not supported in ICU");
+            }
+
             bool safeForCurrentCulture =
                 IsSafeForCurrentCultureComparisons(s)
                 && IsSafeForCurrentCultureComparisons(target.ToString());
@@ -2774,8 +2782,10 @@ namespace System.Tests
             return true;
         }
 
-        [Theory]
-        [ActiveIssue("https://github.com/dotnet/runtime/issues/4673", TestPlatforms.AnyUnix)]
+        // NOTE: This is by design. ICU ignores the null characters (i.e. null characters have no weights for the string comparison).
+        // For desired behavior, use ordinal comparison instead of linguistic comparison.
+        // This is a known difference between NLS and ICU (https://github.com/dotnet/runtime/issues/4673).
+        [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNlsGlobalization))]
         [InlineData("He\0lo", "He\0lo", 0)]
         [InlineData("He\0lo", "He\0", 0)]
         [InlineData("He\0lo", "\0", 2)]
@@ -2934,14 +2944,14 @@ namespace System.Tests
                 string target = "ddzs";
 
                 /*
-                    There are differences between Windows and ICU regarding contractions.
-                    Windows has equal contraction collation weights, including case (target="Ddzs" same behavior as "ddzs").
+                    There are differences between NLS and ICU regarding contractions.
+                    NLS has equal contraction collation weights, including case (target="Ddzs" same behavior as "ddzs").
                     ICU has different contraction collation weights, depending on locale collation rules.
                     If CurrentCultureIgnoreCase is specified, ICU will use 'secondary' collation rules
                     which ignore the contraction collation weights (defined as 'tertiary' rules)
                 */
-                Assert.Equal(PlatformDetection.IsWindows ? 0 : -1, source.IndexOf(target));
-                Assert.Equal(PlatformDetection.IsWindows ? 0 : -1, source.IndexOf(target, StringComparison.CurrentCulture));
+                Assert.Equal(PlatformDetection.IsNlsGlobalization ? 0 : -1, source.IndexOf(target));
+                Assert.Equal(PlatformDetection.IsNlsGlobalization ? 0 : -1, source.IndexOf(target, StringComparison.CurrentCulture));
 
                 Assert.Equal(0, source.IndexOf(target, StringComparison.CurrentCultureIgnoreCase));
                 Assert.Equal(-1, source.IndexOf(target, StringComparison.Ordinal));
@@ -2949,7 +2959,7 @@ namespace System.Tests
 
                 ReadOnlySpan<char> span = source.AsSpan();
 
-                Assert.Equal(PlatformDetection.IsWindows ? 0 : -1, span.IndexOf(target.AsSpan(), StringComparison.CurrentCulture));
+                Assert.Equal(PlatformDetection.IsNlsGlobalization ? 0 : -1, span.IndexOf(target.AsSpan(), StringComparison.CurrentCulture));
 
                 Assert.Equal(0, span.IndexOf(target.AsSpan(), StringComparison.CurrentCultureIgnoreCase));
                 Assert.Equal(-1, span.IndexOf(target.AsSpan(), StringComparison.Ordinal));
@@ -3861,8 +3871,10 @@ namespace System.Tests
             }
         }
 
-        [Theory]
-        [ActiveIssue("https://github.com/dotnet/runtime/issues/4673", TestPlatforms.AnyUnix)]
+        // NOTE: This is by design. ICU ignores the null characters (i.e. null characters have no weights for the string comparison).
+        // For desired behavior, use ordinal comparison instead of linguistic comparison.
+        // This is a known difference between NLS and ICU (https://github.com/dotnet/runtime/issues/4673).
+        [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNlsGlobalization))]
         [InlineData("He\0lo", "He\0lo", 0)]
         [InlineData("He\0lo", "He\0", 0)]
         [InlineData("He\0lo", "\0", 2)]
@@ -4648,8 +4660,10 @@ namespace System.Tests
             Assert.Equal(expected, s.AsSpan().StartsWith(value.AsSpan(), comparisonType));
         }
 
-        [Theory]
-        [ActiveIssue("https://github.com/dotnet/runtime/issues/4673", TestPlatforms.AnyUnix)]
+        // NOTE: This is by design. ICU ignores the null characters (i.e. null characters have no weights for the string comparison).
+        // For desired behavior, use ordinal comparison instead of linguistic comparison.
+        // This is a known difference between NLS and ICU (https://github.com/dotnet/runtime/issues/4673).
+        [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNlsGlobalization))]
         [InlineData(StringComparison.CurrentCulture)]
         [InlineData(StringComparison.CurrentCultureIgnoreCase)]
         [InlineData(StringComparison.Ordinal)]
@@ -7074,11 +7088,10 @@ namespace System.Tests
             Assert.False(span.StartsWith(value, StringComparison.InvariantCultureIgnoreCase));
         }
 
-        // NOTE: This is by design. Unix ignores the null characters (i.e. null characters have no weights for the string comparison).
+        // NOTE: This is by design. ICU ignores the null characters (i.e. null characters have no weights for the string comparison).
         // For desired behavior, use ordinal comparison instead of linguistic comparison.
-        // This is a known difference between Windows and Unix (https://github.com/dotnet/runtime/issues/4673).
-        [Theory]
-        [PlatformSpecific(TestPlatforms.Windows)]
+        // This is a known difference between NLS and ICU (https://github.com/dotnet/runtime/issues/4673).
+        [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNlsGlobalization))]
         [InlineData(StringComparison.CurrentCulture)]
         [InlineData(StringComparison.CurrentCultureIgnoreCase)]
         [InlineData(StringComparison.InvariantCulture)]
index a2ebcf6..908f330 100644 (file)
@@ -9,10 +9,11 @@
 #include "windows.h"
 #endif
 
+// The args passed in should match InterlockedCompareExchangePointer Windows API
 static int pal_atomic_cas_ptr(void* volatile* dest, void* exchange, void* comparand)
 {
 #if defined(TARGET_UNIX)
-    return __atomic_compare_exchange_n(dest, exchange, comparand, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST);
+    return __atomic_compare_exchange_n(dest, &comparand, exchange, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST);
 #elif defined(TARGET_WINDOWS)
     return InterlockedCompareExchangePointer(dest, exchange, comparand) == comparand;
 #endif
index 081a016..4410c3e 100644 (file)
@@ -404,7 +404,7 @@ static const UCollator* GetCollatorFromSortHandle(SortHandle* pSortHandle, int32
         pCollator = CloneCollatorWithOptions(pSortHandle->collatorsPerOption[0], options, pErr);
         UCollator* pNull = NULL;
 
-        if (!pal_atomic_cas_ptr((void* volatile*)&pSortHandle->collatorsPerOption[options], &pNull, pCollator))
+        if (!pal_atomic_cas_ptr((void* volatile*)&pSortHandle->collatorsPerOption[options], pCollator, pNull))
         {
             ucol_close(pCollator);
             pCollator = pSortHandle->collatorsPerOption[options];
index 5438b44..fbea9cf 100644 (file)
@@ -374,7 +374,11 @@ int32_t GlobalizationNative_LoadICU()
 // return the current loaded ICU version
 int32_t GlobalizationNative_GetICUVersion()
 {
-    int32_t version;
-    u_getVersion((uint8_t *) &version);
-    return version;
+    if (u_getVersion_ptr == NULL)
+        return 0;
+    
+    UVersionInfo versionInfo;
+    u_getVersion(versionInfo);
+
+    return (versionInfo[0] << 24) + (versionInfo[1] << 16) + (versionInfo[2] << 8) + versionInfo[3];
 }
index a4403b4..5291163 100644 (file)
@@ -5,6 +5,7 @@
 using System.Collections.Generic;
 using System.Globalization;
 using System.Text;
+using Microsoft.DotNet.XUnitExtensions;
 using Xunit;
 
 namespace System.Data.SqlTypes.Tests
@@ -37,12 +38,8 @@ namespace System.Data.SqlTypes.Tests
 
         private static readonly UnicodeEncoding s_unicodeEncoding = new UnicodeEncoding(bigEndian: false, byteOrderMark: false, throwOnInvalidBytes: true);
 
-        [ActiveIssue("https://github.com/dotnet/runtime/issues/18912", TestPlatforms.AnyUnix)] // TODO: Add this to the theory below when the issue is addressed on Unix
-        [Theory]
+        [ConditionalTheory]
         [InlineData("ja-JP", 0x0411)] // Japanese - Japan
-        public static void SqlStringValidComparisonTest_Windows(string cultureName, int localeId) => SqlStringValidComparisonTest(cultureName, localeId);
-
-        [Theory]
         [InlineData("ar-SA", 0x0401)] // Arabic - Saudi Arabia
         [InlineData("de-DE", 0x0407)] // German - Germany
         [InlineData("hi-IN", 0x0439)] // Hindi - India
@@ -56,6 +53,12 @@ namespace System.Data.SqlTypes.Tests
         [InlineData("en-US", 0x0409)] // English - United States
         public static void SqlStringValidComparisonTest(string cultureName, int localeId)
         {
+            if (PlatformDetection.IsIcuGlobalization && cultureName == "ja-JP" && localeId == 0x0411)
+            {
+                // TODO: Remove this once: https://github.com/dotnet/runtime/issues/18912 is fixed on ICU.
+                throw new SkipTestException($"PlatformDetection.IsIcuGlobalization and cultureName == ja-JP");
+            }
+
             var culture = new CultureInfo(cultureName);
 
             const SqlCompareOptions DefaultCompareOption = SqlCompareOptions.IgnoreCase | SqlCompareOptions.IgnoreKanaType | SqlCompareOptions.IgnoreWidth;
index 62c6166..6e9b839 100644 (file)
@@ -430,8 +430,8 @@ namespace System.Globalization.Tests
             Calendar calendar = Calendar;
             Assert.All(DateTime_TestData(calendar), dt =>
             {
-                // JapaneseCalendar throws on Unix (ICU), but not on Windows
-                if ((calendar is JapaneseCalendar && PlatformDetection.IsWindows) || calendar is HebrewCalendar || calendar is TaiwanLunisolarCalendar || calendar is JapaneseLunisolarCalendar)
+                // JapaneseCalendar throws on ICU, but not on NLS
+                if ((calendar is JapaneseCalendar && PlatformDetection.IsNlsGlobalization) || calendar is HebrewCalendar || calendar is TaiwanLunisolarCalendar || calendar is JapaneseLunisolarCalendar)
                 {
                     calendar.GetEra(dt);
                 }
index a159467..6da5b82 100644 (file)
@@ -32,7 +32,7 @@ namespace System.Globalization.Tests
 
         private static string[] GetExpectedMonthNames()
         {
-            if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+            if (PlatformDetection.IsNlsGlobalization)
             {
                 return new string[]
                 {
index 5e66148..1290450 100644 (file)
@@ -1,6 +1,6 @@
 Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio 15
-VisualStudioVersion = 15.0.27213.1
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.29923.206
 MinimumVisualStudioVersion = 10.0.40219.1
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Globalization.Extensions.Tests", "tests\System.Globalization.Extensions.Tests.csproj", "{BC439554-4AB4-4C94-8E28-C00EDE4FD1C7}"
        ProjectSection(ProjectDependencies) = postProject
@@ -20,7 +20,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{E107E9C1-E89
 EndProject
 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ref", "ref", "{2E666815-2EDB-464B-9DF6-380BF4789AD4}"
 EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestUtilities", "..\Common\tests\TestUtilities\TestUtilities.csproj", "{A36A6300-3BE9-42CA-B7BC-62E926E07CC5}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Globalization.Extensions.Nls.Tests", "tests\NlsTests\System.Globalization.Extensions.Nls.Tests.csproj", "{CB60359A-D0ED-474E-B395-9589F1A5656A}"
 EndProject
 Global
        GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -40,10 +40,10 @@ Global
                {3634FAA3-A33E-406A-94EE-5611C6CC2810}.Debug|Any CPU.Build.0 = Debug|Any CPU
                {3634FAA3-A33E-406A-94EE-5611C6CC2810}.Release|Any CPU.ActiveCfg = Release|Any CPU
                {3634FAA3-A33E-406A-94EE-5611C6CC2810}.Release|Any CPU.Build.0 = Release|Any CPU
-               {A36A6300-3BE9-42CA-B7BC-62E926E07CC5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-               {A36A6300-3BE9-42CA-B7BC-62E926E07CC5}.Debug|Any CPU.Build.0 = Debug|Any CPU
-               {A36A6300-3BE9-42CA-B7BC-62E926E07CC5}.Release|Any CPU.ActiveCfg = Release|Any CPU
-               {A36A6300-3BE9-42CA-B7BC-62E926E07CC5}.Release|Any CPU.Build.0 = Release|Any CPU
+               {CB60359A-D0ED-474E-B395-9589F1A5656A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+               {CB60359A-D0ED-474E-B395-9589F1A5656A}.Debug|Any CPU.Build.0 = Debug|Any CPU
+               {CB60359A-D0ED-474E-B395-9589F1A5656A}.Release|Any CPU.ActiveCfg = Release|Any CPU
+               {CB60359A-D0ED-474E-B395-9589F1A5656A}.Release|Any CPU.Build.0 = Release|Any CPU
        EndGlobalSection
        GlobalSection(SolutionProperties) = preSolution
                HideSolutionNode = FALSE
@@ -52,7 +52,7 @@ Global
                {BC439554-4AB4-4C94-8E28-C00EDE4FD1C7} = {1A2F9F4A-A032-433E-B914-ADD5992BB178}
                {2B96AA10-84C0-4927-8611-8D2474B990E8} = {E107E9C1-E893-4E87-987E-04EF0DCEAEFD}
                {3634FAA3-A33E-406A-94EE-5611C6CC2810} = {2E666815-2EDB-464B-9DF6-380BF4789AD4}
-               {A36A6300-3BE9-42CA-B7BC-62E926E07CC5} = {1A2F9F4A-A032-433E-B914-ADD5992BB178}
+               {CB60359A-D0ED-474E-B395-9589F1A5656A} = {1A2F9F4A-A032-433E-B914-ADD5992BB178}
        EndGlobalSection
        GlobalSection(ExtensibilityGlobals) = postSolution
                SolutionGuid = {3E550ED4-6053-4B16-97D1-BAADB02924FE}
index da82bbb..ff53746 100644 (file)
@@ -10,8 +10,6 @@ namespace System.Globalization.Tests
 {
     public class GetStringComparerTests
     {
-        private static bool s_isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
-
         [Fact]
         public void GetStringComparer_Invalid()
         {
@@ -35,9 +33,9 @@ namespace System.Globalization.Tests
         [InlineData("abc", "ABC", "en-US", CompareOptions.OrdinalIgnoreCase, 0, 0)]
         [InlineData("Cot\u00E9", "cot\u00E9", "fr-FR", CompareOptions.IgnoreCase, 0, 0)]
         [InlineData("cot\u00E9", "c\u00F4te", "fr-FR", CompareOptions.None, 1, -1)]
-        public static void Compare(string x, string y, string cultureName, CompareOptions options, int expectedWindows, int expectedICU)
+        public static void Compare(string x, string y, string cultureName, CompareOptions options, int expectedNls, int expectedICU)
         {
-            int expected = s_isWindows ? expectedWindows : expectedICU;
+            int expected = PlatformDetection.IsNlsGlobalization ? expectedNls : expectedICU;
             StringComparer comparer = new CultureInfo(cultureName).CompareInfo.GetStringComparer(options);
 
             Assert.Equal(expected, Math.Sign(comparer.Compare(x, y)));
index f1a65e0..93f093d 100644 (file)
@@ -22,9 +22,9 @@ namespace System.Globalization.Tests
                     continue;
                 }
                 string ascii = c.ToString();
-                if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) // expected platform differences, see https://github.com/dotnet/runtime/issues/17190
+                if (PlatformDetection.IsIcuGlobalization) // expected platform differences, see https://github.com/dotnet/runtime/issues/17190
                 {
-                    if ((c >= 'A' && c <= 'Z'))
+                    if (c >= 'A' && c <= 'Z')
                     {
                         yield return new object[] { ascii, 0, 1, ascii.ToLower() };
                     }
index 6cafb29..6c58383 100644 (file)
@@ -96,7 +96,7 @@ namespace System.Globalization.Tests
 
             yield return new object[] { "abc" + (char)0x7F + "def", 0, 7, typeof(ArgumentException) };
 
-            if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) // expected platform differences, see https://github.com/dotnet/runtime/issues/17190
+            if (PlatformDetection.IsNlsGlobalization) // expected platform differences, see https://github.com/dotnet/runtime/issues/17190
             {
                 yield return new object[] { "xn--\u1234", 0, 5, typeof(ArgumentException) };
                 yield return new object[] { "xn--\u1234pck", 0, 8, typeof(ArgumentException) };
index 98949ea..10d468a 100644 (file)
@@ -22,8 +22,6 @@ namespace System.Globalization.Tests
     /// </summary>
     public class UseStd3AsciiRules
     {
-        private static bool s_isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
-
         [Fact]
         public void UseStd3AsciiRules_IsFalseByDefault()
         {
@@ -52,7 +50,7 @@ namespace System.Globalization.Tests
             var idnStd3False = new IdnMapping { UseStd3AsciiRules = false };
             var idnStd3True = new IdnMapping { UseStd3AsciiRules = true };
 
-            if (containsInvalidHyphen && !s_isWindows)
+            if (containsInvalidHyphen && PlatformDetection.IsIcuGlobalization)
             {
                 // ICU always fails on leading/trailing hyphens regardless of the Std3 rules option.
                 AssertExtensions.Throws<ArgumentException>("unicode", () => idnStd3False.GetAscii(unicode));
diff --git a/src/libraries/System.Globalization.Extensions/tests/NlsTests/System.Globalization.Extensions.Nls.Tests.csproj b/src/libraries/System.Globalization.Extensions/tests/NlsTests/System.Globalization.Extensions.Nls.Tests.csproj
new file mode 100644 (file)
index 0000000..87bbcd4
--- /dev/null
@@ -0,0 +1,66 @@
+<Project Sdk="Microsoft.NET.Sdk">
+  <PropertyGroup>
+    <TargetFrameworks>$(NetCoreAppCurrent)-Windows_NT</TargetFrameworks>
+    <TestRuntime>true</TestRuntime>
+  </PropertyGroup>
+  <ItemGroup>
+    <Compile Include="..\GetStringComparerTests.cs">
+      <Link>GetStringComparerTests.cs</Link>
+    </Compile>
+    <Compile Include="..\IdnMapping\Data\ConformanceIdnaUnicodeTestResult.cs">
+      <Link>IdnMapping\Data\ConformanceIdnaUnicodeTestResult.cs</Link>
+    </Compile>
+    <Compile Include="..\IdnMapping\Data\Unicode_9_0\Unicode_9_0_IdnaTest.cs">
+      <Link>IdnMapping\Data\Unicode_9_0\Unicode_9_0_IdnaTest.cs</Link>
+    </Compile>
+    <Compile Include="..\IdnMapping\Data\Unicode_11_0\Unicode_11_0_IdnaTest.cs">
+      <Link>IdnMapping\Data\Unicode_11_0\Unicode_11_0_IdnaTest.cs</Link>
+    </Compile>
+    <Compile Include="..\IdnMapping\IdnMappingIdnaConformanceTests.cs">
+      <Link>IdnMapping\IdnMappingIdnaConformanceTests.cs</Link>
+    </Compile>
+    <Compile Include="..\IdnMapping\Data\Factory.cs">
+      <Link>IdnMapping\Data\Factory.cs</Link>
+    </Compile>
+    <Compile Include="..\IdnMapping\Data\ConformanceIdnaTestResult.cs">
+      <Link>IdnMapping\Data\ConformanceIdnaTestResult.cs</Link>
+    </Compile>
+    <Compile Include="..\IdnMapping\Data\Unicode_6_0\Unicode_6_0_IdnaTest.cs">
+      <Link>IdnMapping\Data\Unicode_6_0\Unicode_6_0_IdnaTest.cs</Link>
+    </Compile>
+    <Compile Include="..\IdnMapping\Data\Unicode_Win7\Unicode_Win7_IdnaTest.cs">
+      <Link>IdnMapping\Data\Unicode_Win7\Unicode_Win7_IdnaTest.cs</Link>
+    </Compile>
+    <Compile Include="..\IdnMapping\Data\IConformanceIdnaTest.cs">
+      <Link>IdnMapping\Data\IConformanceIdnaTest.cs</Link>
+    </Compile>
+    <Compile Include="..\IdnMapping\IdnMappingGetAsciiTests.cs">
+      <Link>IdnMapping\IdnMappingGetAsciiTests.cs</Link>
+    </Compile>
+    <Compile Include="..\IdnMapping\IdnMappingGetUnicodeTests.cs">
+      <Link>IdnMapping\IdnMappingGetUnicodeTests.cs</Link>
+    </Compile>
+    <Compile Include="..\IdnMapping\IdnMappingUseStd3AsciiRulesTests.cs">
+      <Link>IdnMapping\IdnMappingUseStd3AsciiRulesTests.cs</Link>
+    </Compile>
+    <Compile Include="..\Normalization\StringNormalizationTests.cs">
+      <Link>Normalization\StringNormalizationTests.cs</Link>
+    </Compile>
+    <Compile Include="..\Normalization\NormalizationAll.cs">
+      <Link>Normalization\NormalizationAll.cs</Link>
+    </Compile>
+  </ItemGroup>
+  <ItemGroup>
+    <EmbeddedResource Include="..\IdnMapping\Data\Unicode_6_0\IdnaTest_6.txt" />
+    <EmbeddedResource Include="..\IdnMapping\Data\Unicode_Win7\IdnaTest_Win7.txt" />
+    <EmbeddedResource Include="..\IdnMapping\Data\Unicode_9_0\IdnaTest_9.txt" />
+    <EmbeddedResource Include="..\IdnMapping\Data\Unicode_11_0\IdnaTest_11.txt" />
+    <EmbeddedResource Include="..\Normalization\Data\win8.txt">
+      <LogicalName>NormalizationDataWin8</LogicalName>
+    </EmbeddedResource>
+    <EmbeddedResource Include="..\Normalization\Data\win7.txt">
+      <LogicalName>NormalizationDataWin7</LogicalName>
+    </EmbeddedResource>
+    <None Include="runtimeconfig.template.json" />
+  </ItemGroup>
+</Project>
\ No newline at end of file
diff --git a/src/libraries/System.Globalization.Extensions/tests/NlsTests/runtimeconfig.template.json b/src/libraries/System.Globalization.Extensions/tests/NlsTests/runtimeconfig.template.json
new file mode 100644 (file)
index 0000000..ec1e961
--- /dev/null
@@ -0,0 +1,5 @@
+{
+  "configProperties": {
+    "System.Globalization.UseNls": true
+  }
+}
index 4607fdf..36e2149 100644 (file)
@@ -1,6 +1,6 @@
 Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio 15
-VisualStudioVersion = 15.0.27213.1
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.29923.206
 MinimumVisualStudioVersion = 10.0.40219.1
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Globalization.Tests", "tests\System.Globalization.Tests.csproj", "{484C92C6-6D2C-45BC-A5E2-4A12BA228E1E}"
        ProjectSection(ProjectDependencies) = postProject
@@ -25,6 +25,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{E107E9C1-E89
 EndProject
 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ref", "ref", "{2E666815-2EDB-464B-9DF6-380BF4789AD4}"
 EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Globalization.Nls.Tests", "tests\NlsTests\System.Globalization.Nls.Tests.csproj", "{6B71E284-DA57-4657-9E08-A02BAFB877CC}"
+EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestUtilities", "..\Common\tests\TestUtilities\TestUtilities.csproj", "{443745F1-A200-432B-A666-3D0E9F938DED}"
 EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestUtilities.Unicode", "..\Common\tests\TestUtilities.Unicode\TestUtilities.Unicode.csproj", "{E5ECA266-C7E9-4597-8FFA-095BD6890407}"
@@ -51,6 +53,10 @@ Global
                {E1E58C98-808F-4065-9C1D-E6411166AF6F}.Debug|Any CPU.Build.0 = Debug|Any CPU
                {E1E58C98-808F-4065-9C1D-E6411166AF6F}.Release|Any CPU.ActiveCfg = Release|Any CPU
                {E1E58C98-808F-4065-9C1D-E6411166AF6F}.Release|Any CPU.Build.0 = Release|Any CPU
+               {6B71E284-DA57-4657-9E08-A02BAFB877CC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+               {6B71E284-DA57-4657-9E08-A02BAFB877CC}.Debug|Any CPU.Build.0 = Debug|Any CPU
+               {6B71E284-DA57-4657-9E08-A02BAFB877CC}.Release|Any CPU.ActiveCfg = Release|Any CPU
+               {6B71E284-DA57-4657-9E08-A02BAFB877CC}.Release|Any CPU.Build.0 = Release|Any CPU
                {443745F1-A200-432B-A666-3D0E9F938DED}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
                {443745F1-A200-432B-A666-3D0E9F938DED}.Debug|Any CPU.Build.0 = Debug|Any CPU
                {443745F1-A200-432B-A666-3D0E9F938DED}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -68,6 +74,7 @@ Global
                {9A8926D9-1D4C-4069-8965-A626F6CA8C29} = {1A2F9F4A-A032-433E-B914-ADD5992BB178}
                {2395E8CA-73CB-40DF-BE40-A60BC189B737} = {E107E9C1-E893-4E87-987E-04EF0DCEAEFD}
                {E1E58C98-808F-4065-9C1D-E6411166AF6F} = {2E666815-2EDB-464B-9DF6-380BF4789AD4}
+               {6B71E284-DA57-4657-9E08-A02BAFB877CC} = {1A2F9F4A-A032-433E-B914-ADD5992BB178}
                {443745F1-A200-432B-A666-3D0E9F938DED} = {1A2F9F4A-A032-433E-B914-ADD5992BB178}
                {E5ECA266-C7E9-4597-8FFA-095BD6890407} = {1A2F9F4A-A032-433E-B914-ADD5992BB178}
        EndGlobalSection
index 3b2f121..963d7ad 100644 (file)
@@ -16,12 +16,12 @@ namespace System.Globalization.Tests
 
         // On Windows, hiragana characters sort after katakana.
         // On ICU, it is the opposite
-        private static int s_expectedHiraganaToKatakanaCompare = PlatformDetection.IsWindows ? 1 : -1;
+        private static int s_expectedHiraganaToKatakanaCompare = PlatformDetection.IsNlsGlobalization ? 1 : -1;
 
         // On Windows, all halfwidth characters sort before fullwidth characters.
         // On ICU, half and fullwidth characters that aren't in the "Halfwidth and fullwidth forms" block U+FF00-U+FFEF
         // sort before the corresponding characters that are in the block U+FF00-U+FFEF
-        private static int s_expectedHalfToFullFormsComparison = PlatformDetection.IsWindows ? -1 : 1;
+        private static int s_expectedHalfToFullFormsComparison = PlatformDetection.IsNlsGlobalization ? -1 : 1;
 
         private const string SoftHyphen = "\u00AD";
 
@@ -39,13 +39,13 @@ namespace System.Globalization.Tests
             yield return new object[] { s_invariantCompare, "\u3070\u3073\u3076\u3079\u307C", "\u30D0\u30D3\u3076\u30D9\uFF8E\uFF9E", CompareOptions.None, s_expectedHiraganaToKatakanaCompare };
             yield return new object[] { s_invariantCompare, "\u3060", "\uFF80\uFF9E", CompareOptions.None, s_expectedHiraganaToKatakanaCompare };
 
-            bool isWindows = PlatformDetection.IsWindows;
+            bool useNls = PlatformDetection.IsNlsGlobalization;
 
-            yield return new object[] { s_invariantCompare, "\u30C7\u30BF\u30D9\u30B9", "\uFF83\uFF9E\uFF80\uFF8D\uFF9E\uFF7D", CompareOptions.None, isWindows ? 1 : -1 };
-            yield return new object[] { s_invariantCompare, "\u30C7", "\uFF83\uFF9E", CompareOptions.None, isWindows ? 1 : -1 };
-            yield return new object[] { s_invariantCompare, "\u30C7\u30BF", "\uFF83\uFF9E\uFF80", CompareOptions.None, isWindows ? 1 : -1 };
-            yield return new object[] { s_invariantCompare, "\u30C7\u30BF\u30D9", "\uFF83\uFF9E\uFF80\uFF8D\uFF9E", CompareOptions.None, isWindows ? 1 : -1 };
-            yield return new object[] { s_invariantCompare, "\uFF83\uFF9E\uFF70\uFF80\uFF8D\uFF9E\uFF70\uFF7D", "\u3067\u30FC\u305F\u3079\u30FC\u3059", CompareOptions.None, isWindows ? -1 : 1 };
+            yield return new object[] { s_invariantCompare, "\u30C7\u30BF\u30D9\u30B9", "\uFF83\uFF9E\uFF80\uFF8D\uFF9E\uFF7D", CompareOptions.None, useNls ? 1 : -1 };
+            yield return new object[] { s_invariantCompare, "\u30C7", "\uFF83\uFF9E", CompareOptions.None, useNls ? 1 : -1 };
+            yield return new object[] { s_invariantCompare, "\u30C7\u30BF", "\uFF83\uFF9E\uFF80", CompareOptions.None, useNls ? 1 : -1 };
+            yield return new object[] { s_invariantCompare, "\u30C7\u30BF\u30D9", "\uFF83\uFF9E\uFF80\uFF8D\uFF9E", CompareOptions.None, useNls ? 1 : -1 };
+            yield return new object[] { s_invariantCompare, "\uFF83\uFF9E\uFF70\uFF80\uFF8D\uFF9E\uFF70\uFF7D", "\u3067\u30FC\u305F\u3079\u30FC\u3059", CompareOptions.None, useNls ? -1 : 1 };
         }
 
         public static IEnumerable<object[]> Compare_TestData()
@@ -237,28 +237,29 @@ namespace System.Globalization.Tests
             yield return new object[] { new CultureInfo("es-ES").CompareInfo, "llegar", "lugar", CompareOptions.None, -1 };
 
             // Misc differences between platforms
-            bool isWindows = PlatformDetection.IsWindows;
-
-            yield return new object[] { s_invariantCompare, "\u3042", "\u30A1", CompareOptions.IgnoreKanaType | CompareOptions.IgnoreWidth | CompareOptions.IgnoreCase, isWindows ? 1: 0 };
-            yield return new object[] { s_invariantCompare, "'\u3000'", "''", CompareOptions.IgnoreKanaType | CompareOptions.IgnoreWidth | CompareOptions.IgnoreCase, isWindows ? 1 : -1 };
-            yield return new object[] { s_invariantCompare, "\u30BF", "\uFF80", CompareOptions.None, isWindows ? 1 : -1 };
-            yield return new object[] { s_invariantCompare, "'\u3000'", "''", CompareOptions.None, isWindows ? 1 : -1 };
-            yield return new object[] { s_invariantCompare, "\u30FC", "\uFF70", CompareOptions.None, isWindows ? 0 : -1 };
-            yield return new object[] { s_hungarianCompare, "dzsdzs", "ddzs", CompareOptions.None, isWindows ? 0 : -1 };
-            yield return new object[] { s_invariantCompare, "Test's", "Tests", CompareOptions.None, isWindows ? 1 : -1 };
+            bool useNls = PlatformDetection.IsNlsGlobalization;
+
+            yield return new object[] { s_invariantCompare, "\u3042", "\u30A1", CompareOptions.IgnoreKanaType | CompareOptions.IgnoreWidth | CompareOptions.IgnoreCase, useNls ? 1: 0 };
+            yield return new object[] { s_invariantCompare, "'\u3000'", "''", CompareOptions.IgnoreKanaType | CompareOptions.IgnoreWidth | CompareOptions.IgnoreCase, useNls ? 1 : -1 };
+            yield return new object[] { s_invariantCompare, "\u30BF", "\uFF80", CompareOptions.None, useNls ? 1 : -1 };
+            yield return new object[] { s_invariantCompare, "'\u3000'", "''", CompareOptions.None, useNls ? 1 : -1 };
+            yield return new object[] { s_invariantCompare, "\u30FC", "\uFF70", CompareOptions.None, useNls ? 0 : -1 };
+            yield return new object[] { s_hungarianCompare, "dzsdzs", "ddzs", CompareOptions.None, useNls ? 0 : -1 };
+            yield return new object[] { s_invariantCompare, "Test's", "Tests", CompareOptions.None, useNls ? 1 : -1 };
             yield return new object[] { new CultureInfo("de-DE").CompareInfo, "\u00DC", "UE", CompareOptions.None, -1 };
-            yield return new object[] { new CultureInfo("de-DE_phoneb").CompareInfo, "\u00DC", "UE", CompareOptions.None, isWindows ? 0 : -1 };
-            yield return new object[] { new CultureInfo("es-ES_tradnl").CompareInfo, "llegar", "lugar", CompareOptions.None, isWindows ? 1 : -1 };
+            yield return new object[] { new CultureInfo("de-DE_phoneb").CompareInfo, "\u00DC", "UE", CompareOptions.None, useNls ? 0 : -1 };
+            yield return new object[] { new CultureInfo("es-ES_tradnl").CompareInfo, "llegar", "lugar", CompareOptions.None, useNls ? 1 : -1 };
         }
 
         // There is a regression in Windows 190xx version with the Kana comparison. Avoid running this test there.
         public static bool IsNotWindowsKanaRegressedVersion() => !PlatformDetection.IsWindows10Version1903OrGreater ||
+                                                              PlatformDetection.IsIcuGlobalization ||
                                                               s_invariantCompare.Compare("\u3060", "\uFF80\uFF9E", CompareOptions.IgnoreKanaType | CompareOptions.IgnoreWidth | CompareOptions.IgnoreCase) == 0;
 
         [Fact]
         public void CompareWithUnassignedChars()
         {
-            int result = PlatformDetection.IsWindows ? 0 : -1;
+            int result = PlatformDetection.IsNlsGlobalization ? 0 : -1;
             Compare(s_invariantCompare, "FooBar", "Foo\uFFFFBar", CompareOptions.None, result);
             Compare(s_invariantCompare, "FooBar", "Foo\uFFFFBar", CompareOptions.IgnoreNonSpace, result);
         }
index 2036dc4..6c41d6c 100644 (file)
@@ -109,26 +109,26 @@ namespace System.Globalization.Tests
             yield return new object[] { s_currentCompare, "\u0131", "\u0130", 0, 1, CompareOptions.Ordinal, -1 };
 
             // Platform differences
-            yield return new object[] { s_hungarianCompare, "foobardzsdzs", "rddzs", 0, 12, CompareOptions.None, PlatformDetection.IsWindows ? 5 : -1};
+            yield return new object[] { s_hungarianCompare, "foobardzsdzs", "rddzs", 0, 12, CompareOptions.None, PlatformDetection.IsNlsGlobalization ? 5 : -1};
         }
 
         public static IEnumerable<object[]> IndexOf_Aesc_Ligature_TestData()
         {
-            bool isWindows = PlatformDetection.IsWindows;
+            bool useNls = PlatformDetection.IsNlsGlobalization;
             // Searches for the ligature \u00C6
             string source1 = "Is AE or ae the same as \u00C6 or \u00E6?";
-            yield return new object[] { s_invariantCompare, source1, "AE", 8, 18, CompareOptions.None, isWindows ? 24 : -1};
+            yield return new object[] { s_invariantCompare, source1, "AE", 8, 18, CompareOptions.None, useNls ? 24 : -1};
             yield return new object[] { s_invariantCompare, source1, "ae", 8, 18, CompareOptions.None, 9 };
             yield return new object[] { s_invariantCompare, source1, "\u00C6", 8, 18, CompareOptions.None, 24 };
-            yield return new object[] { s_invariantCompare, source1, "\u00E6", 8, 18, CompareOptions.None, isWindows ? 9 : -1};
+            yield return new object[] { s_invariantCompare, source1, "\u00E6", 8, 18, CompareOptions.None, useNls ? 9 : -1};
             yield return new object[] { s_invariantCompare, source1, "AE", 8, 18, CompareOptions.Ordinal, -1 };
             yield return new object[] { s_invariantCompare, source1, "ae", 8, 18, CompareOptions.Ordinal, 9 };
             yield return new object[] { s_invariantCompare, source1, "\u00C6", 8, 18, CompareOptions.Ordinal, 24 };
             yield return new object[] { s_invariantCompare, source1, "\u00E6", 8, 18, CompareOptions.Ordinal, -1 };
             yield return new object[] { s_invariantCompare, source1, "AE", 8, 18, CompareOptions.IgnoreCase, 9 };
             yield return new object[] { s_invariantCompare, source1, "ae", 8, 18, CompareOptions.IgnoreCase, 9 };
-            yield return new object[] { s_invariantCompare, source1, "\u00C6", 8, 18, CompareOptions.IgnoreCase, isWindows? 9 : 24 };
-            yield return new object[] { s_invariantCompare, source1, "\u00E6", 8, 18, CompareOptions.IgnoreCase, isWindows? 9 : 24 };
+            yield return new object[] { s_invariantCompare, source1, "\u00C6", 8, 18, CompareOptions.IgnoreCase, useNls ? 9 : 24 };
+            yield return new object[] { s_invariantCompare, source1, "\u00E6", 8, 18, CompareOptions.IgnoreCase, useNls ? 9 : 24 };
         }
 
         public static IEnumerable<object[]> IndexOf_U_WithDiaeresis_TestData()
@@ -225,9 +225,9 @@ namespace System.Globalization.Tests
         [Fact]
         public void IndexOf_UnassignedUnicode()
         {
-            bool isWindows = PlatformDetection.IsWindows;
-            IndexOf_String(s_invariantCompare, "FooBar", "Foo\uFFFFBar", 0, 6, CompareOptions.None, isWindows ? 0 : -1);
-            IndexOf_String(s_invariantCompare, "~FooBar", "Foo\uFFFFBar", 0, 7, CompareOptions.IgnoreNonSpace, isWindows ? 1 : -1);
+            bool useNls = PlatformDetection.IsNlsGlobalization;
+            IndexOf_String(s_invariantCompare, "FooBar", "Foo\uFFFFBar", 0, 6, CompareOptions.None, useNls ? 0 : -1);
+            IndexOf_String(s_invariantCompare, "~FooBar", "Foo\uFFFFBar", 0, 7, CompareOptions.IgnoreNonSpace, useNls ? 1 : -1);
         }
 
         [Fact]
index a9a93c5..32f45d4 100644 (file)
@@ -71,15 +71,16 @@ namespace System.Globalization.Tests
             yield return new object[] { s_invariantCompare, "Test's can be interesting", "Tests", CompareOptions.None, false };
 
             // Platform differences
-            yield return new object[] { s_hungarianCompare, "dzsdzsfoobar", "ddzsf", CompareOptions.None, PlatformDetection.IsWindows ? true : false };
-            yield return new object[] { s_invariantCompare, "''Tests", "Tests", CompareOptions.IgnoreSymbols, PlatformDetection.IsWindows ? true : false };
-            yield return new object[] { s_frenchCompare, "\u0153", "oe", CompareOptions.None, PlatformDetection.IsWindows ? true : false };
-            yield return new object[] { s_invariantCompare, "\uD800\uDC00", "\uD800", CompareOptions.None, PlatformDetection.IsWindows ? true : false };
-            yield return new object[] { s_invariantCompare, "\uD800\uDC00", "\uD800", CompareOptions.IgnoreCase, PlatformDetection.IsWindows ? true : false };
+            bool useNls = PlatformDetection.IsNlsGlobalization;
+            yield return new object[] { s_hungarianCompare, "dzsdzsfoobar", "ddzsf", CompareOptions.None, useNls ? true : false };
+            yield return new object[] { s_invariantCompare, "''Tests", "Tests", CompareOptions.IgnoreSymbols, useNls ? true : false };
+            yield return new object[] { s_frenchCompare, "\u0153", "oe", CompareOptions.None, useNls ? true : false };
+            yield return new object[] { s_invariantCompare, "\uD800\uDC00", "\uD800", CompareOptions.None, useNls ? true : false };
+            yield return new object[] { s_invariantCompare, "\uD800\uDC00", "\uD800", CompareOptions.IgnoreCase, useNls ? true : false };
 
             // ICU bugs
             // UInt16 overflow: https://unicode-org.atlassian.net/browse/ICU-20832 fixed in https://github.com/unicode-org/icu/pull/840 (ICU 65)
-            if (PlatformDetection.IsWindows || PlatformDetection.ICUVersion.Major >= 65)
+            if (useNls || PlatformDetection.ICUVersion.Major >= 65)
             {
                 yield return new object[] { s_frenchCompare, "b", new string('a', UInt16.MaxValue + 1), CompareOptions.None, false };
             }
@@ -106,7 +107,7 @@ namespace System.Globalization.Tests
         [Fact]
         public void IsPrefix_UnassignedUnicode()
         {
-            bool result = PlatformDetection.IsWindows ? true : false;
+            bool result = PlatformDetection.IsNlsGlobalization ? true : false;
             IsPrefix(s_invariantCompare, "FooBar", "Foo\uFFFFBar", CompareOptions.None, result);
             IsPrefix(s_invariantCompare, "FooBar", "Foo\uFFFFBar", CompareOptions.IgnoreNonSpace, result);
         }
index 3d920dd..909ceb9 100644 (file)
@@ -82,10 +82,10 @@ namespace System.Globalization.Tests
             yield return new object[] { s_invariantCompare, "a\u0000b", "b\u0000b", CompareOptions.None, false };
 
             // Platform differences
-            yield return new object[] { s_hungarianCompare, "foobardzsdzs", "rddzs", CompareOptions.None, PlatformDetection.IsWindows ? true : false };
-            yield return new object[] { s_frenchCompare, "\u0153", "oe", CompareOptions.None, PlatformDetection.IsWindows ? true : false };
-            yield return new object[] { s_invariantCompare, "\uD800\uDC00", "\uDC00", CompareOptions.None, PlatformDetection.IsWindows ? true : false };
-            yield return new object[] { s_invariantCompare, "\uD800\uDC00", "\uDC00", CompareOptions.IgnoreCase, PlatformDetection.IsWindows ? true : false };
+            yield return new object[] { s_hungarianCompare, "foobardzsdzs", "rddzs", CompareOptions.None, PlatformDetection.IsIcuGlobalization ? false : true };
+            yield return new object[] { s_frenchCompare, "\u0153", "oe", CompareOptions.None, PlatformDetection.IsIcuGlobalization ? false : true };
+            yield return new object[] { s_invariantCompare, "\uD800\uDC00", "\uDC00", CompareOptions.None, PlatformDetection.IsIcuGlobalization ? false : true };
+            yield return new object[] { s_invariantCompare, "\uD800\uDC00", "\uDC00", CompareOptions.IgnoreCase, PlatformDetection.IsIcuGlobalization ? false : true };
         }
 
         [Theory]
@@ -109,7 +109,7 @@ namespace System.Globalization.Tests
         [Fact]
         public void IsSuffix_UnassignedUnicode()
         {
-            bool result = PlatformDetection.IsWindows ? true : false;
+            bool result = PlatformDetection.IsIcuGlobalization ? false : true;
 
             IsSuffix(s_invariantCompare, "FooBar", "Foo\uFFFFBar", CompareOptions.None, result);
             IsSuffix(s_invariantCompare, "FooBar", "Foo\uFFFFBar", CompareOptions.IgnoreNonSpace, result);
index 4d0474e..2702d72 100644 (file)
@@ -81,26 +81,26 @@ namespace System.Globalization.Tests
             yield return new object[] { s_invariantCompare, "cbabababdbaba", "ab", 12, 13, CompareOptions.None, 10 };
 
             // Platform differences
-            yield return new object[] { s_hungarianCompare, "foobardzsdzs", "rddzs", 11, 12, CompareOptions.None, PlatformDetection.IsWindows ? 5 : -1 };
+            yield return new object[] { s_hungarianCompare, "foobardzsdzs", "rddzs", 11, 12, CompareOptions.None, PlatformDetection.IsNlsGlobalization ? 5 : -1 };
 
         }
 
         public static IEnumerable<object[]> LastIndexOf_Aesc_Ligature_TestData()
         {
-            bool isWindows = PlatformDetection.IsWindows;
+            bool useNls = PlatformDetection.IsNlsGlobalization;
 
             // Searches for the ligature \u00C6
             string source = "Is AE or ae the same as \u00C6 or \u00E6?";
-            yield return new object[] { s_invariantCompare, source, "AE", 25, 18, CompareOptions.None, isWindows ? 24 : -1 };
+            yield return new object[] { s_invariantCompare, source, "AE", 25, 18, CompareOptions.None, useNls ? 24 : -1 };
             yield return new object[] { s_invariantCompare, source, "ae", 25, 18, CompareOptions.None, 9 };
             yield return new object[] { s_invariantCompare, source, '\u00C6', 25, 18, CompareOptions.None, 24 };
-            yield return new object[] { s_invariantCompare, source, '\u00E6', 25, 18, CompareOptions.None, isWindows ? 9 : -1 };
+            yield return new object[] { s_invariantCompare, source, '\u00E6', 25, 18, CompareOptions.None, useNls ? 9 : -1 };
             yield return new object[] { s_invariantCompare, source, "AE", 25, 18, CompareOptions.Ordinal, -1 };
             yield return new object[] { s_invariantCompare, source, "ae", 25, 18, CompareOptions.Ordinal, 9 };
             yield return new object[] { s_invariantCompare, source, '\u00C6', 25, 18, CompareOptions.Ordinal, 24 };
             yield return new object[] { s_invariantCompare, source, '\u00E6', 25, 18, CompareOptions.Ordinal, -1 };
-            yield return new object[] { s_invariantCompare, source, "AE", 25, 18, CompareOptions.IgnoreCase, isWindows ? 24 : 9 };
-            yield return new object[] { s_invariantCompare, source, "ae", 25, 18, CompareOptions.IgnoreCase, isWindows ? 24 : 9 };
+            yield return new object[] { s_invariantCompare, source, "AE", 25, 18, CompareOptions.IgnoreCase, useNls ? 24 : 9 };
+            yield return new object[] { s_invariantCompare, source, "ae", 25, 18, CompareOptions.IgnoreCase, useNls ? 24 : 9 };
             yield return new object[] { s_invariantCompare, source, '\u00C6', 25, 18, CompareOptions.IgnoreCase, 24 };
             yield return new object[] { s_invariantCompare, source, '\u00E6', 25, 18, CompareOptions.IgnoreCase, 24 };
         }
@@ -233,9 +233,9 @@ namespace System.Globalization.Tests
         [Fact]
         public void LastIndexOf_UnassignedUnicode()
         {
-            bool isWindows = PlatformDetection.IsWindows;
-            LastIndexOf_String(s_invariantCompare, "FooBar", "Foo\uFFFFBar", 5, 6, CompareOptions.None, isWindows ? 0 : -1);
-            LastIndexOf_String(s_invariantCompare, "~FooBar", "Foo\uFFFFBar", 6, 7, CompareOptions.IgnoreNonSpace, isWindows ? 1 : -1);
+            bool useNls = PlatformDetection.IsNlsGlobalization;
+            LastIndexOf_String(s_invariantCompare, "FooBar", "Foo\uFFFFBar", 5, 6, CompareOptions.None, useNls ? 0 : -1);
+            LastIndexOf_String(s_invariantCompare, "~FooBar", "Foo\uFFFFBar", 6, 7, CompareOptions.IgnoreNonSpace, useNls ? 1 : -1);
         }
 
         [Fact]
index b83073a..d4a7620 100644 (file)
@@ -95,14 +95,14 @@ namespace System.Globalization.Tests
             yield return new object[] { "tr-TR"  , 0x041f };
         }
 
-        // On Windows, hiragana characters sort after katakana.
+        // On NLS, hiragana characters sort after katakana.
         // On ICU, it is the opposite
-        private static int s_expectedHiraganaToKatakanaCompare = PlatformDetection.IsWindows ? 1 : -1;
+        private static int s_expectedHiraganaToKatakanaCompare = PlatformDetection.IsNlsGlobalization ? 1 : -1;
 
-        // On Windows, all halfwidth characters sort before fullwidth characters.
+        // On NLS, all halfwidth characters sort before fullwidth characters.
         // On ICU, half and fullwidth characters that aren't in the "Halfwidth and fullwidth forms" block U+FF00-U+FFEF
         // sort before the corresponding characters that are in the block U+FF00-U+FFEF
-        private static int s_expectedHalfToFullFormsComparison = PlatformDetection.IsWindows ? -1 : 1;
+        private static int s_expectedHalfToFullFormsComparison = PlatformDetection.IsNlsGlobalization ? -1 : 1;
 
         private static CompareInfo s_invariantCompare = CultureInfo.InvariantCulture.CompareInfo;
         private static CompareInfo s_turkishCompare = new CultureInfo("tr-TR").CompareInfo;
index 028cf71..cf40173 100644 (file)
@@ -14,9 +14,9 @@ namespace System.Globalization.Tests
 {
     public class CultureInfoAll
     {
-        [Fact]
         [PlatformSpecific(TestPlatforms.Windows)] // P/Invoke to Win32 function
-        public void TestAllCultures()
+        [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNlsGlobalization))]
+        public void TestAllCultures_Nls()
         {
             Assert.True(EnumSystemLocalesEx(EnumLocales, LOCALE_WINDOWS, IntPtr.Zero, IntPtr.Zero), "EnumSystemLocalesEx has failed");
 
index c8fcdb9..ed232bb 100644 (file)
@@ -11,12 +11,12 @@ namespace System.Globalization.Tests
     {
         public static string EnUSEraName()
         {
-            return PlatformDetection.IsWindows ? "A.D." : "AD";
+            return PlatformDetection.IsNlsGlobalization ? "A.D." : "AD";
         }
 
         public static string EnUSAbbreviatedEraName()
         {
-            return PlatformDetection.IsWindows ? "AD" : "A";
+            return PlatformDetection.IsNlsGlobalization ? "AD" : "A";
         }
 
         public static string JaJPAbbreviatedEraName()
@@ -24,7 +24,7 @@ namespace System.Globalization.Tests
             // For Windows<Win7 and others, the default calendar is Gregorian Calendar, AD is expected to be the Era Name
             // CLDR has the Japanese abbreviated era name for the Gregorian Calendar in English - "AD",
             // so for non-Windows machines it will be "AD".
-            return PlatformDetection.IsWindows ? "\u897F\u66A6" : "AD";
+            return PlatformDetection.IsNlsGlobalization ? "\u897F\u66A6" : "AD";
         }
 
         public static string[] FrFRDayNames()
index e98d438..8e78315 100644 (file)
@@ -48,9 +48,8 @@ namespace System.Globalization.Tests
             Assert.Throws<InvalidOperationException>(() => DateTimeFormatInfo.InvariantInfo.LongTimePattern = "HH:mm:ss");
         }
 
-        [Fact]
-        [PlatformSpecific(TestPlatforms.AnyUnix)]
-        public void LongTimePattern_CheckReadingTimeFormatWithSingleQuotes()
+        [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsIcuGlobalization))]
+        public void LongTimePattern_CheckReadingTimeFormatWithSingleQuotes_ICU()
         {
             // Usually fr-CA long time format has a single quotes e.g. "HH 'h' mm 'min' ss 's'".
             // Ensuring when reading such formats from ICU we'll not eat the spaces after the single quotes.
index e8ccf90..14c5afa 100644 (file)
@@ -71,12 +71,12 @@ namespace System.Globalization.Tests
             }
             catch
             {
-                if (PlatformDetection.IsWindows)
+                if (PlatformDetection.IsNlsGlobalization)
                 {
                     // Persian calendar is recently supported as one of the optional calendars for fa-IR
                     Assert.True(calendar is PersianCalendar, "Exception can occur only with PersianCalendar");
                 }
-                else // !PlatformDetection.IsWindows
+                else // !PlatformDetection.IsNlsGlobalization
                 {
                     Assert.True(calendar is HijriCalendar || calendar is UmAlQuraCalendar || calendar is ThaiBuddhistCalendar ||
                                 calendar is HebrewCalendar || calendar is KoreanCalendar, "failed to set the calendar on DTFI");
diff --git a/src/libraries/System.Globalization/tests/IcuTests.cs b/src/libraries/System.Globalization/tests/IcuTests.cs
new file mode 100644 (file)
index 0000000..8e862c9
--- /dev/null
@@ -0,0 +1,40 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Reflection;
+using Xunit;
+using Xunit.Sdk;
+
+namespace System.Globalization.Tests
+{
+    public class IcuTests
+    {
+        private static bool IsIcuCompatiblePlatform => PlatformDetection.IsNotWindows ||
+                                                       (!PlatformDetection.IsMonoRuntime &&
+                                                       PlatformDetection.IsWindows10Version1903OrGreater);
+
+        [ConditionalFact(nameof(IsIcuCompatiblePlatform))]
+        public static void IcuShouldBeUsedByDefault()
+        {
+            Type globalizationMode = Type.GetType("System.Globalization.GlobalizationMode");
+            if (globalizationMode != null)
+            {
+                MethodInfo methodInfo = globalizationMode.GetProperty("UseNls", BindingFlags.NonPublic | BindingFlags.Static)?.GetMethod;
+                if (methodInfo != null)
+                {
+                    Assert.False((bool)methodInfo.Invoke(null, null));
+                    return;
+                }
+            }
+
+            throw new XunitException("Couldn't get System.Globalization.GlobalizationMode.UseIcu property.");
+        }
+
+        [ConditionalFact(nameof(IsIcuCompatiblePlatform))]
+        public static void IcuShouldBeLoaded()
+        {
+            Assert.True(PlatformDetection.IsIcuGlobalization);
+        }
+    }
+}
index d6c7676..67c0b09 100644 (file)
@@ -476,6 +476,12 @@ namespace System.Globalization.Tests
             yield return new object[] { "xn--de-jg4avhby1noc0d", 0, 21, "\u30D1\u30D5\u30A3\u30FC\u0064\u0065\u30EB\u30F3\u30D0" };
         }
 
+        [Fact]
+        public static void IcuShouldNotBeLoaded()
+        {
+            Assert.False(PlatformDetection.IsIcuGlobalization);
+        }
+
         [Theory]
         [MemberData(nameof(Cultures_TestData))]
         public void TestCultureData(string cultureName)
diff --git a/src/libraries/System.Globalization/tests/NlsTests/NlsSwitchTests.cs b/src/libraries/System.Globalization/tests/NlsTests/NlsSwitchTests.cs
new file mode 100644 (file)
index 0000000..c7885ac
--- /dev/null
@@ -0,0 +1,36 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Reflection;
+using Xunit;
+using Xunit.Sdk;
+
+namespace System.Globalization.Tests
+{
+    public class NlsSwitchTests
+    {
+        [Fact]
+        public static void NlsRuntimeSwitchIsHonored()
+        {
+            Type globalizationMode = Type.GetType("System.Globalization.GlobalizationMode");
+            if (globalizationMode != null)
+            {
+                MethodInfo methodInfo = globalizationMode.GetProperty("UseNls", BindingFlags.NonPublic | BindingFlags.Static)?.GetMethod;
+                if (methodInfo != null)
+                {
+                    Assert.True((bool)methodInfo.Invoke(null, null));
+                    return;
+                }
+            }
+
+            throw new XunitException("Couldn't get System.Globalization.GlobalizationMode.UseIcu property.");
+        }
+
+        [Fact]
+        public static void IcuShouldNotBeLoaded()
+        {
+            Assert.False(PlatformDetection.IsIcuGlobalization);
+        }
+    }
+}
diff --git a/src/libraries/System.Globalization/tests/NlsTests/System.Globalization.Nls.Tests.csproj b/src/libraries/System.Globalization/tests/NlsTests/System.Globalization.Nls.Tests.csproj
new file mode 100644 (file)
index 0000000..7ab8074
--- /dev/null
@@ -0,0 +1,345 @@
+<Project Sdk="Microsoft.NET.Sdk">
+  <PropertyGroup>
+    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+    <TestRuntime>true</TestRuntime>
+    <IncludeRemoteExecutor>true</IncludeRemoteExecutor>
+    <!-- This test project is Windows only as it forces the use of NLS as the Globlaization platform -->
+    <TargetFrameworks>$(NetCoreAppCurrent)-Windows_NT</TargetFrameworks>
+    <UnicodeUcdVersion>13.0</UnicodeUcdVersion>
+  </PropertyGroup>
+  <ItemGroup>
+    <!-- Include tests from System.Globalization.Tests -->
+    <Compile Include="NlsSwitchTests.cs" />
+    <Compile Include="..\CompareInfo\CompareInfoTests.cs">
+      <Link>CompareInfo\CompareInfoTests.cs</Link>
+    </Compile>
+    <Compile Include="..\CompareInfo\CompareInfoTests.IndexOf.cs">
+      <Link>CompareInfo\CompareInfoTests.IndexOf.cs</Link>
+    </Compile>
+    <Compile Include="..\CompareInfo\CompareInfoTests.IsPrefix.cs">
+      <Link>CompareInfo\CompareInfoTests.IsPrefix.cs</Link>
+    </Compile>
+    <Compile Include="..\CompareInfo\CompareInfoTests.Compare.cs">
+      <Link>CompareInfo\CompareInfoTests.Compare.cs</Link>
+    </Compile>
+    <Compile Include="..\CompareInfo\CompareInfoTests.IsSuffix.cs">
+      <Link>CompareInfo\CompareInfoTests.IsSuffix.cs</Link>
+    </Compile>
+    <Compile Include="..\CompareInfo\CompareInfoTests.LastIndexOf.cs">
+      <Link>CompareInfo\CompareInfoTests.LastIndexOf.cs</Link>
+    </Compile>
+    <Compile Include="..\NumberFormatInfo\NumberFormatInfoCurrentInfo.cs">
+      <Link>NumberFormatInfo\NumberFormatInfoCurrentInfo.cs</Link>
+    </Compile>
+    <Compile Include="..\NumberFormatInfo\NumberFormatInfoValidateParseStyle.cs">
+      <Link>NumberFormatInfo\NumberFormatInfoValidateParseStyle.cs</Link>
+    </Compile>
+    <Compile Include="..\NumberFormatInfo\NumberFormatInfoData.cs">
+      <Link>NumberFormatInfo\NumberFormatInfoData.cs</Link>
+    </Compile>
+    <Compile Include="..\NumberFormatInfo\NumberFormatInfoCurrencySymbol.cs">
+      <Link>NumberFormatInfo\NumberFormatInfoCurrencySymbol.cs</Link>
+    </Compile>
+    <Compile Include="..\NumberFormatInfo\NumberFormatInfoNaNSymbol.cs">
+      <Link>NumberFormatInfo\NumberFormatInfoNaNSymbol.cs</Link>
+    </Compile>
+    <Compile Include="..\NumberFormatInfo\NumberFormatInfoPercentGroupSeparator.cs">
+      <Link>NumberFormatInfo\NumberFormatInfoPercentGroupSeparator.cs</Link>
+    </Compile>
+    <Compile Include="..\NumberFormatInfo\NumberFormatInfoPercentDecimalSeparator.cs">
+      <Link>NumberFormatInfo\NumberFormatInfoPercentDecimalSeparator.cs</Link>
+    </Compile>
+    <Compile Include="..\NumberFormatInfo\NumberFormatInfoPercentDecimalDigits.cs">
+      <Link>NumberFormatInfo\NumberFormatInfoPercentDecimalDigits.cs</Link>
+    </Compile>
+    <Compile Include="..\NumberFormatInfo\NumberFormatInfoPerMilleSymbol.cs">
+      <Link>NumberFormatInfo\NumberFormatInfoPerMilleSymbol.cs</Link>
+    </Compile>
+    <Compile Include="..\NumberFormatInfo\NumberFormatInfoPercentSymbol.cs">
+      <Link>NumberFormatInfo\NumberFormatInfoPercentSymbol.cs</Link>
+    </Compile>
+    <Compile Include="..\NumberFormatInfo\NumberFormatInfoPositiveSign.cs">
+      <Link>NumberFormatInfo\NumberFormatInfoPositiveSign.cs</Link>
+    </Compile>
+    <Compile Include="..\NumberFormatInfo\NumberFormatInfoPositiveInfinitySymbol.cs">
+      <Link>NumberFormatInfo\NumberFormatInfoPositiveInfinitySymbol.cs</Link>
+    </Compile>
+    <Compile Include="..\NumberFormatInfo\NumberFormatInfoNegativeSign.cs">
+      <Link>NumberFormatInfo\NumberFormatInfoNegativeSign.cs</Link>
+    </Compile>
+    <Compile Include="..\NumberFormatInfo\NumberFormatInfoNegativeInfinitySymbol.cs">
+      <Link>NumberFormatInfo\NumberFormatInfoNegativeInfinitySymbol.cs</Link>
+    </Compile>
+    <Compile Include="..\NumberFormatInfo\NumberFormatInfoNumberGroupSeparator.cs">
+      <Link>NumberFormatInfo\NumberFormatInfoNumberGroupSeparator.cs</Link>
+    </Compile>
+    <Compile Include="..\NumberFormatInfo\NumberFormatInfoNumberDecimalSeparator.cs">
+      <Link>NumberFormatInfo\NumberFormatInfoNumberDecimalSeparator.cs</Link>
+    </Compile>
+    <Compile Include="..\NumberFormatInfo\NumberFormatInfoNumberDecimalDigits.cs">
+      <Link>NumberFormatInfo\NumberFormatInfoNumberDecimalDigits.cs</Link>
+    </Compile>
+    <Compile Include="..\NumberFormatInfo\NumberFormatInfoNumberGroupSizes.cs">
+      <Link>NumberFormatInfo\NumberFormatInfoNumberGroupSizes.cs</Link>
+    </Compile>
+    <Compile Include="..\NumberFormatInfo\NumberFormatInfoNumberNegativePattern.cs">
+      <Link>NumberFormatInfo\NumberFormatInfoNumberNegativePattern.cs</Link>
+    </Compile>
+    <Compile Include="..\NumberFormatInfo\NumberFormatInfoPercentNegativePattern.cs">
+      <Link>NumberFormatInfo\NumberFormatInfoPercentNegativePattern.cs</Link>
+    </Compile>
+    <Compile Include="..\NumberFormatInfo\NumberFormatInfoPercentGroupSizes.cs">
+      <Link>NumberFormatInfo\NumberFormatInfoPercentGroupSizes.cs</Link>
+    </Compile>
+    <Compile Include="..\NumberFormatInfo\NumberFormatInfoPercentPositivePattern.cs">
+      <Link>NumberFormatInfo\NumberFormatInfoPercentPositivePattern.cs</Link>
+    </Compile>
+    <Compile Include="..\CultureInfo\CultureInfoAll.cs">
+      <Link>CultureInfo\CultureInfoAll.cs</Link>
+    </Compile>
+    <Compile Include="..\CultureInfo\CultureInfoAsync.cs">
+      <Link>CultureInfo\CultureInfoAsync.cs</Link>
+    </Compile>
+    <Compile Include="..\CultureInfo\CultureInfoCalendar.cs">
+      <Link>CultureInfo\CultureInfoCalendar.cs</Link>
+    </Compile>
+    <Compile Include="..\CultureInfo\CultureInfoClone.cs">
+      <Link>CultureInfo\CultureInfoClone.cs</Link>
+    </Compile>
+    <Compile Include="..\CultureInfo\CultureInfoCompareInfo.cs">
+      <Link>CultureInfo\CultureInfoCompareInfo.cs</Link>
+    </Compile>
+    <Compile Include="..\CultureInfo\CultureInfoCtor.cs">
+      <Link>CultureInfo\CultureInfoCtor.cs</Link>
+    </Compile>
+    <Compile Include="..\CultureInfo\CultureInfoDateTimeFormat.cs">
+      <Link>CultureInfo\CultureInfoDateTimeFormat.cs</Link>
+    </Compile>
+    <Compile Include="..\CultureInfo\CultureInfoEnglishName.cs">
+      <Link>CultureInfo\CultureInfoEnglishName.cs</Link>
+    </Compile>
+    <Compile Include="..\CultureInfo\CultureInfoEquals.cs">
+      <Link>CultureInfo\CultureInfoEquals.cs</Link>
+    </Compile>
+    <Compile Include="..\CultureInfo\CultureInfoGetFormat.cs">
+      <Link>CultureInfo\CultureInfoGetFormat.cs</Link>
+    </Compile>
+    <Compile Include="..\CultureInfo\CultureInfoGetHashCode.cs">
+      <Link>CultureInfo\CultureInfoGetHashCode.cs</Link>
+    </Compile>
+    <Compile Include="..\CultureInfo\CultureInfoIsNeutralCulture.cs">
+      <Link>CultureInfo\CultureInfoIsNeutralCulture.cs</Link>
+    </Compile>
+    <Compile Include="..\CultureInfo\CultureInfoNativeName.cs">
+      <Link>CultureInfo\CultureInfoNativeName.cs</Link>
+    </Compile>
+    <Compile Include="..\CultureInfo\CultureInfoNumberFormat.cs">
+      <Link>CultureInfo\CultureInfoNumberFormat.cs</Link>
+    </Compile>
+    <Compile Include="..\CultureInfo\CultureInfoParent.cs">
+      <Link>CultureInfo\CultureInfoParent.cs</Link>
+    </Compile>
+    <Compile Include="..\CultureInfo\CultureInfoReadOnly.cs">
+      <Link>CultureInfo\CultureInfoReadOnly.cs</Link>
+    </Compile>
+    <Compile Include="..\CultureInfo\CultureInfoTwoLetterISOLanguageName.cs">
+      <Link>CultureInfo\CultureInfoTwoLetterISOLanguageName.cs</Link>
+    </Compile>
+    <Compile Include="..\CultureInfo\CultureInfoCurrentCulture.cs">
+      <Link>CultureInfo\CultureInfoCurrentCulture.cs</Link>
+    </Compile>
+    <Compile Include="..\CultureInfo\GetCultureInfo.cs">
+      <Link>CultureInfo\GetCultureInfo.cs</Link>
+    </Compile>
+    <Compile Include="..\DateTimeFormatInfo\DateTimeFormatInfoAbbreviatedDayNames.cs">
+      <Link>DateTimeFormatInfo\DateTimeFormatInfoAbbreviatedDayNames.cs</Link>
+    </Compile>
+    <Compile Include="..\DateTimeFormatInfo\DateTimeFormatInfoAbbreviatedMonthGenitiveNames.cs">
+      <Link>DateTimeFormatInfo\DateTimeFormatInfoAbbreviatedMonthGenitiveNames.cs</Link>
+    </Compile>
+    <Compile Include="..\DateTimeFormatInfo\DateTimeFormatInfoAbbreviatedMonthNames.cs">
+      <Link>DateTimeFormatInfo\DateTimeFormatInfoAbbreviatedMonthNames.cs</Link>
+    </Compile>
+    <Compile Include="..\DateTimeFormatInfo\DateTimeFormatInfoAMDesignator.cs">
+      <Link>DateTimeFormatInfo\DateTimeFormatInfoAMDesignator.cs</Link>
+    </Compile>
+    <Compile Include="..\DateTimeFormatInfo\DateTimeFormatInfoCalendar.cs">
+      <Link>DateTimeFormatInfo\DateTimeFormatInfoCalendar.cs</Link>
+    </Compile>
+    <Compile Include="..\DateTimeFormatInfo\DateTimeFormatInfoCalendarWeekRule.cs">
+      <Link>DateTimeFormatInfo\DateTimeFormatInfoCalendarWeekRule.cs</Link>
+    </Compile>
+    <Compile Include="..\DateTimeFormatInfo\DateTimeFormatInfoClone.cs">
+      <Link>DateTimeFormatInfo\DateTimeFormatInfoClone.cs</Link>
+    </Compile>
+    <Compile Include="..\DateTimeFormatInfo\DateTimeFormatInfoData.cs">
+      <Link>DateTimeFormatInfo\DateTimeFormatInfoData.cs</Link>
+    </Compile>
+    <Compile Include="..\DateTimeFormatInfo\DateTimeFormatInfoDayNames.cs">
+      <Link>DateTimeFormatInfo\DateTimeFormatInfoDayNames.cs</Link>
+    </Compile>
+    <Compile Include="..\DateTimeFormatInfo\DateTimeFormatInfoFirstDayOfWeek.cs">
+      <Link>DateTimeFormatInfo\DateTimeFormatInfoFirstDayOfWeek.cs</Link>
+    </Compile>
+    <Compile Include="..\DateTimeFormatInfo\DateTimeFormatInfoFullDateTimePattern.cs">
+      <Link>DateTimeFormatInfo\DateTimeFormatInfoFullDateTimePattern.cs</Link>
+    </Compile>
+    <Compile Include="..\DateTimeFormatInfo\DateTimeFormatInfoGetAbbreviatedDayName.cs">
+      <Link>DateTimeFormatInfo\DateTimeFormatInfoGetAbbreviatedDayName.cs</Link>
+    </Compile>
+    <Compile Include="..\DateTimeFormatInfo\DateTimeFormatInfoGetAbbreviatedEraName.cs">
+      <Link>DateTimeFormatInfo\DateTimeFormatInfoGetAbbreviatedEraName.cs</Link>
+    </Compile>
+    <Compile Include="..\DateTimeFormatInfo\DateTimeFormatInfoGetAbbreviatedMonthName.cs">
+      <Link>DateTimeFormatInfo\DateTimeFormatInfoGetAbbreviatedMonthName.cs</Link>
+    </Compile>
+    <Compile Include="..\DateTimeFormatInfo\DateTimeFormatInfoGetDayName.cs">
+      <Link>DateTimeFormatInfo\DateTimeFormatInfoGetDayName.cs</Link>
+    </Compile>
+    <Compile Include="..\DateTimeFormatInfo\DateTimeFormatInfoGetEra.cs">
+      <Link>DateTimeFormatInfo\DateTimeFormatInfoGetEra.cs</Link>
+    </Compile>
+    <Compile Include="..\DateTimeFormatInfo\DateTimeFormatInfoGetEraName.cs">
+      <Link>DateTimeFormatInfo\DateTimeFormatInfoGetEraName.cs</Link>
+    </Compile>
+    <Compile Include="..\DateTimeFormatInfo\DateTimeFormatInfoGetFormat.cs">
+      <Link>DateTimeFormatInfo\DateTimeFormatInfoGetFormat.cs</Link>
+    </Compile>
+    <Compile Include="..\DateTimeFormatInfo\DateTimeFormatInfoGetInstance.cs">
+      <Link>DateTimeFormatInfo\DateTimeFormatInfoGetInstance.cs</Link>
+    </Compile>
+    <Compile Include="..\DateTimeFormatInfo\DateTimeFormatInfoGetMonthName.cs">
+      <Link>DateTimeFormatInfo\DateTimeFormatInfoGetMonthName.cs</Link>
+    </Compile>
+    <Compile Include="..\DateTimeFormatInfo\DateTimeFormatInfoLongDatePattern.cs">
+      <Link>DateTimeFormatInfo\DateTimeFormatInfoLongDatePattern.cs</Link>
+    </Compile>
+    <Compile Include="..\DateTimeFormatInfo\DateTimeFormatInfoLongTimePattern.cs">
+      <Link>DateTimeFormatInfo\DateTimeFormatInfoLongTimePattern.cs</Link>
+    </Compile>
+    <Compile Include="..\DateTimeFormatInfo\DateTimeFormatInfoMonthDayPattern.cs">
+      <Link>DateTimeFormatInfo\DateTimeFormatInfoMonthDayPattern.cs</Link>
+    </Compile>
+    <Compile Include="..\DateTimeFormatInfo\DateTimeFormatInfoMonthGenitiveNames.cs">
+      <Link>DateTimeFormatInfo\DateTimeFormatInfoMonthGenitiveNames.cs</Link>
+    </Compile>
+    <Compile Include="..\DateTimeFormatInfo\DateTimeFormatInfoMonthNames.cs">
+      <Link>DateTimeFormatInfo\DateTimeFormatInfoMonthNames.cs</Link>
+    </Compile>
+    <Compile Include="..\DateTimeFormatInfo\DateTimeFormatInfoPMDesignator.cs">
+      <Link>DateTimeFormatInfo\DateTimeFormatInfoPMDesignator.cs</Link>
+    </Compile>
+    <Compile Include="..\DateTimeFormatInfo\DateTimeFormatInfoReadOnly.cs">
+      <Link>DateTimeFormatInfo\DateTimeFormatInfoReadOnly.cs</Link>
+    </Compile>
+    <Compile Include="..\DateTimeFormatInfo\DateTimeFormatInfoRFC1123Pattern.cs">
+      <Link>DateTimeFormatInfo\DateTimeFormatInfoRFC1123Pattern.cs</Link>
+    </Compile>
+    <Compile Include="..\DateTimeFormatInfo\DateTimeFormatInfoShortDatePattern.cs">
+      <Link>DateTimeFormatInfo\DateTimeFormatInfoShortDatePattern.cs</Link>
+    </Compile>
+    <Compile Include="..\DateTimeFormatInfo\DateTimeFormatInfoShortestDayNames.cs">
+      <Link>DateTimeFormatInfo\DateTimeFormatInfoShortestDayNames.cs</Link>
+    </Compile>
+    <Compile Include="..\DateTimeFormatInfo\DateTimeFormatInfoShortTimePattern.cs">
+      <Link>DateTimeFormatInfo\DateTimeFormatInfoShortTimePattern.cs</Link>
+    </Compile>
+    <Compile Include="..\DateTimeFormatInfo\DateTimeFormatInfoSortableDateTimePattern.cs">
+      <Link>DateTimeFormatInfo\DateTimeFormatInfoSortableDateTimePattern.cs</Link>
+    </Compile>
+    <Compile Include="..\DateTimeFormatInfo\DateTimeFormatInfoTests.cs">
+      <Link>DateTimeFormatInfo\DateTimeFormatInfoTests.cs</Link>
+    </Compile>
+    <Compile Include="..\DateTimeFormatInfo\DateTimeFormatInfoUniversalSortableDateTimePattern.cs">
+      <Link>DateTimeFormatInfo\DateTimeFormatInfoUniversalSortableDateTimePattern.cs</Link>
+    </Compile>
+    <Compile Include="..\DateTimeFormatInfo\DateTimeFormatInfoYearMonthPattern.cs">
+      <Link>DateTimeFormatInfo\DateTimeFormatInfoYearMonthPattern.cs</Link>
+    </Compile>
+    <Compile Include="..\NumberFormatInfo\NumberFormatInfoClone.cs">
+      <Link>NumberFormatInfo\NumberFormatInfoClone.cs</Link>
+    </Compile>
+    <Compile Include="..\NumberFormatInfo\NumberFormatInfoCurrencyDecimalDigits.cs">
+      <Link>NumberFormatInfo\NumberFormatInfoCurrencyDecimalDigits.cs</Link>
+    </Compile>
+    <Compile Include="..\NumberFormatInfo\NumberFormatInfoCurrencyDecimalSeparator.cs">
+      <Link>NumberFormatInfo\NumberFormatInfoCurrencyDecimalSeparator.cs</Link>
+    </Compile>
+    <Compile Include="..\NumberFormatInfo\NumberFormatInfoCurrencyGroupSeparator.cs">
+      <Link>NumberFormatInfo\NumberFormatInfoCurrencyGroupSeparator.cs</Link>
+    </Compile>
+    <Compile Include="..\NumberFormatInfo\NumberFormatInfoCurrencyGroupSizes.cs">
+      <Link>NumberFormatInfo\NumberFormatInfoCurrencyGroupSizes.cs</Link>
+    </Compile>
+    <Compile Include="..\NumberFormatInfo\NumberFormatInfoCurrencyNegativePattern.cs">
+      <Link>NumberFormatInfo\NumberFormatInfoCurrencyNegativePattern.cs</Link>
+    </Compile>
+    <Compile Include="..\NumberFormatInfo\NumberFormatInfoCurrencyPositivePattern.cs">
+      <Link>NumberFormatInfo\NumberFormatInfoCurrencyPositivePattern.cs</Link>
+    </Compile>
+    <Compile Include="..\NumberFormatInfo\NumberFormatInfoGetFormat.cs">
+      <Link>NumberFormatInfo\NumberFormatInfoGetFormat.cs</Link>
+    </Compile>
+    <Compile Include="..\NumberFormatInfo\NumberFormatInfoGetInstance.cs">
+      <Link>NumberFormatInfo\NumberFormatInfoGetInstance.cs</Link>
+    </Compile>
+    <Compile Include="..\NumberFormatInfo\NumberFormatInfoReadOnly.cs">
+      <Link>NumberFormatInfo\NumberFormatInfoReadOnly.cs</Link>
+    </Compile>
+    <Compile Include="..\NumberFormatInfo\NumberFormatInfoTests.cs">
+      <Link>NumberFormatInfo\NumberFormatInfoTests.cs</Link>
+    </Compile>
+    <Compile Include="..\System\Globalization\CharUnicodeInfoTestData.cs">
+      <Link>System\Globalization\CharUnicodeInfoTestData.cs</Link>
+    </Compile>
+    <Compile Include="..\System\Globalization\CharUnicodeInfoTests.cs">
+      <Link>System\Globalization\CharUnicodeInfoTests.cs</Link>
+    </Compile>
+    <Compile Include="..\System\Globalization\CharUnicodeInfoTests.Generated.cs">
+      <Link>System\Globalization\CharUnicodeInfoTests.Generated.cs</Link>
+    </Compile>
+    <Compile Include="..\System\Globalization\CultureNotFoundExceptionTests.cs">
+      <Link>System\Globalization\CultureNotFoundExceptionTests.cs</Link>
+    </Compile>
+    <Compile Include="..\System\Globalization\GraphemeBreakTest.cs">
+      <Link>System\Globalization\GraphemeBreakTest.cs</Link>
+    </Compile>
+    <Compile Include="..\System\Globalization\RegionInfoTests.cs">
+      <Link>System\Globalization\RegionInfoTests.cs</Link>
+    </Compile>
+    <Compile Include="..\System\Globalization\StringInfoTests.cs">
+      <Link>System\Globalization\StringInfoTests.cs</Link>
+    </Compile>
+    <Compile Include="..\System\Globalization\SortVersionTests.cs">
+      <Link>System\Globalization\SortVersionTests.cs</Link>
+    </Compile>
+    <Compile Include="..\System\Globalization\TextInfoTests.cs">
+      <Link>System\Globalization\TextInfoTests.cs</Link>
+    </Compile>
+    <Compile Include="..\System\Globalization\TextElementEnumeratorTests.cs">
+      <Link>System\Globalization\TextElementEnumeratorTests.cs</Link>
+    </Compile>
+    <Compile Include="..\System\Globalization\UnicodeCategoryTests.cs">
+      <Link>System\Globalization\UnicodeCategoryTests.cs</Link>
+    </Compile>
+    <!-- Helpers -->
+    <Compile Include="$(CommonTestPath)System\RandomDataGenerator.cs">
+      <Link>Common\System\RandomDataGenerator.cs</Link>
+    </Compile>
+  </ItemGroup>
+  <ItemGroup>
+    <PackageReference Include="System.Private.Runtime.UnicodeData" Version="$(SystemPrivateRuntimeUnicodeDataVersion)" ExcludeAssets="contentFiles" GeneratePathProperty="true" />
+    <EmbeddedResource Include="$(PkgSystem_Private_Runtime_UnicodeData)\contentFiles\any\any\$(UnicodeUcdVersion).0\ucd\UnicodeData.txt">
+      <Link>CharUnicodeInfo\UnicodeData.$(UnicodeUcdVersion).txt</Link>
+      <LogicalName>UnicodeData.txt</LogicalName>
+    </EmbeddedResource>
+    <EmbeddedResource Include="$(PkgSystem_Private_Runtime_UnicodeData)\contentFiles\any\any\$(UnicodeUcdVersion).0\ucd\auxiliary\GraphemeBreakTest.txt">
+      <Link>CharUnicodeInfo\GraphemeBreakTest-$(UnicodeUcdVersion).0.txt</Link>
+      <LogicalName>GraphemeBreakTest.txt</LogicalName>
+    </EmbeddedResource>
+    <None Include="runtimeconfig.template.json" />
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="$(CommonTestPath)CoreFx.Private.TestUtilities.Unicode\CoreFx.Private.TestUtilities.Unicode.csproj" />
+  </ItemGroup>
+</Project>
diff --git a/src/libraries/System.Globalization/tests/NlsTests/runtimeconfig.template.json b/src/libraries/System.Globalization/tests/NlsTests/runtimeconfig.template.json
new file mode 100644 (file)
index 0000000..f93c603
--- /dev/null
@@ -0,0 +1,5 @@
+{
+    "configProperties": {
+        "System.Globalization.UseNls": true
+    }
+}
index 475c74f..e67bd53 100644 (file)
@@ -18,9 +18,9 @@ namespace System.Globalization.Tests
 
         [Theory]
         [MemberData(nameof(CurrencyDecimalDigits_TestData))]
-        public void CurrencyDecimalDigits_Get_ReturnsExpected(NumberFormatInfo format, int expectedWindows, int expectedIcu)
+        public void CurrencyDecimalDigits_Get_ReturnsExpected(NumberFormatInfo format, int expectedNls, int expectedIcu)
         {
-            int expected = PlatformDetection.IsWindows ? expectedWindows : expectedIcu;
+            int expected = PlatformDetection.IsNlsGlobalization ? expectedNls : expectedIcu;
             Assert.Equal(expected, format.CurrencyDecimalDigits);
         }
 
index e6500c9..dfffa50 100644 (file)
@@ -29,13 +29,13 @@ namespace System.Globalization.Tests
             switch (localeName)
             {
                 case "en-US":
-                    return PlatformDetection.IsWindows ? new int[] { 0 } : new int[] { 1, 0 };
+                    return PlatformDetection.IsNlsGlobalization ? new int[] { 0 } : new int[] { 1, 0 };
 
                 case "en-CA":
-                    return PlatformDetection.IsWindows ? new int[] { 1 } : new int[] { 1, 0 };
+                    return PlatformDetection.IsNlsGlobalization ? new int[] { 1 } : new int[] { 1, 0 };
 
                 case "fa-IR":
-                    if (PlatformDetection.IsWindows)
+                    if (PlatformDetection.IsNlsGlobalization)
                     {
                         return (PlatformDetection.WindowsVersion < 10) ? new int[] { 3 } : new int[] { 6, 3 };
                     }
@@ -53,7 +53,7 @@ namespace System.Globalization.Tests
                     }
 
                 case "fr-CD":
-                    if (PlatformDetection.IsWindows)
+                    if (PlatformDetection.IsNlsGlobalization)
                     {
                         return (PlatformDetection.WindowsVersion < 10) ? new int[] { 4 } : new int[] { 8 };
                     }
@@ -63,13 +63,13 @@ namespace System.Globalization.Tests
                     }
 
                 case "as":
-                    return PlatformDetection.IsWindows ? new int[] { 12 } : new int[] { 9 };
+                    return PlatformDetection.IsNlsGlobalization ? new int[] { 12 } : new int[] { 9 };
 
                 case "es-BO":
-                    return (PlatformDetection.IsWindows && PlatformDetection.WindowsVersion < 10) ? new int[] { 14 } : new int[] { 1 };
+                    return (PlatformDetection.IsNlsGlobalization && PlatformDetection.WindowsVersion < 10) ? new int[] { 14 } : new int[] { 1 };
 
                 case "fr-CA":
-                    return PlatformDetection.IsWindows ? new int[] { 15 } : new int[] { 8, 15 };
+                    return PlatformDetection.IsNlsGlobalization ? new int[] { 15 } : new int[] { 8, 15 };
             }
 
             throw DateTimeFormatInfoData.GetCultureNotSupportedException(CultureInfo.GetCultureInfo(localeName));
index 581c0e8..9902cc6 100644 (file)
@@ -17,9 +17,9 @@ namespace System.Globalization.Tests
 
         [Theory]
         [MemberData(nameof(NumberDecimalDigits_TestData))]
-        public void NumberDecimalDigits_Get_ReturnsExpected(NumberFormatInfo format, int expectedWindows, int expectedIcu)
+        public void NumberDecimalDigits_Get_ReturnsExpected(NumberFormatInfo format, int expectedNls, int expectedIcu)
         {
-            int expected = PlatformDetection.IsWindows ? expectedWindows : expectedIcu;
+            int expected = PlatformDetection.IsNlsGlobalization ? expectedNls : expectedIcu;
             Assert.Equal(expected, format.NumberDecimalDigits);
         }
 
index 5bf6adc..81fa3d9 100644 (file)
@@ -18,15 +18,14 @@ namespace System.Globalization.Tests
         }
 
         /// <summary>
-        /// Not testing for Windows as the culture data can change
+        /// Not testing for NLS as the culture data can change
         /// https://blogs.msdn.microsoft.com/shawnste/2005/04/05/culture-data-shouldnt-be-considered-stable-except-for-invariant/
         /// In the CultureInfoAll test class we are testing the expected behavior
-        /// for Windows by enumerating all locales on the system and then test them.
+        /// for NLS by enumerating all locales on the system and then test them.
         /// </summary>
-        [Theory]
-        [PlatformSpecific(TestPlatforms.AnyUnix)]
+        [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsIcuGlobalization))]
         [MemberData(nameof(PercentNegativePattern_TestData))]
-        public void PercentNegativePattern_Get_ReturnsExpected(NumberFormatInfo format, int expected)
+        public void PercentNegativePattern_Get_ReturnsExpected_ICU(NumberFormatInfo format, int expected)
         {
             Assert.Equal(expected, format.PercentNegativePattern);
         }
index da028a2..1dd5425 100644 (file)
@@ -18,15 +18,14 @@ namespace System.Globalization.Tests
         }
 
         /// <summary>
-        /// Not testing for Windows as the culture data can change
+        /// Not testing for NLS as the culture data can change
         /// https://blogs.msdn.microsoft.com/shawnste/2005/04/05/culture-data-shouldnt-be-considered-stable-except-for-invariant/
         /// In the CultureInfoAll test class we are testing the expected behavior
-        /// for Windows by enumerating all locales on the system and then test them.
+        /// for NLS by enumerating all locales on the system and then test them.
         /// </summary>
-        [Theory]
-        [PlatformSpecific(TestPlatforms.AnyUnix)]
+        [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsIcuGlobalization))]
         [MemberData(nameof(PercentPositivePattern_TestData))]
-        public void PercentPositivePattern_Get_ReturnsExpected(NumberFormatInfo format, int expected)
+        public void PercentPositivePattern_Get_ReturnsExpected_ICU(NumberFormatInfo format, int expected)
         {
             Assert.Equal(expected, format.PercentPositivePattern);
         }
index be9b725..a0a3271 100644 (file)
@@ -1,4 +1,4 @@
-<Project Sdk="Microsoft.NET.Sdk">
+<Project Sdk="Microsoft.NET.Sdk">
   <PropertyGroup>
     <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
     <TestRuntime>true</TestRuntime>
@@ -7,6 +7,7 @@
     <UnicodeUcdVersion>13.0</UnicodeUcdVersion>
   </PropertyGroup>
   <ItemGroup>
+    <Compile Include="IcuTests.cs" />
     <Compile Include="CompareInfo\CompareInfoTests.cs" />
     <Compile Include="CompareInfo\CompareInfoTests.IndexOf.cs" />
     <Compile Include="CompareInfo\CompareInfoTests.IsPrefix.cs" />
index 6ca5537..68f4324 100644 (file)
@@ -52,8 +52,8 @@ namespace System.Globalization.Tests
             AssertExtensions.Throws<ArgumentException>("name", () => new RegionInfo(name));
         }
 
-        [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWindows))]
-        public void CurrentRegion()
+        [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsIcuGlobalization))]
+        public void CurrentRegion_Icu()
         {
             using (new ThreadCultureChange("en-US"))
             {
@@ -63,8 +63,8 @@ namespace System.Globalization.Tests
             }
         }
 
-        [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsWindows))]
-        public void TestCurrentRegion()
+        [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNlsGlobalization))]
+        public void TestCurrentRegion_Nls()
         {
             RemoteExecutor.Invoke(() =>
             {
@@ -149,15 +149,15 @@ namespace System.Globalization.Tests
         public static IEnumerable<object[]> RegionInfo_TestData()
         {
             yield return new object[] { 0x409, 244, "US Dollar", "US Dollar", "\u0055\u0053\u0020\u0044\u006f\u006c\u006c\u0061\u0072", "USA", "USA" };
-            yield return new object[] { 0x411, 122, "Japanese Yen", "Japanese Yen", PlatformDetection.IsWindows ? "\u5186" : "\u65e5\u672c\u5186", "JPN", "JPN" };
+            yield return new object[] { 0x411, 122, "Japanese Yen", "Japanese Yen", PlatformDetection.IsNlsGlobalization ? "\u5186" : "\u65e5\u672c\u5186", "JPN", "JPN" };
             yield return new object[] { 0x804, 45, "Chinese Yuan", "PRC Yuan Renminbi", "\u4eba\u6c11\u5e01", "CHN", "CHN" };
-            yield return new object[] { 0x401, 205, "Saudi Riyal", "Saudi Riyal", PlatformDetection.IsWindows ?
+            yield return new object[] { 0x401, 205, "Saudi Riyal", "Saudi Riyal", PlatformDetection.IsNlsGlobalization ?
                                                     "\u0631\u064a\u0627\u0644\u00a0\u0633\u0639\u0648\u062f\u064a" :
                                                     "\u0631\u064a\u0627\u0644\u0020\u0633\u0639\u0648\u062f\u064a",
                                                     "SAU", "SAU" };
-            yield return new object[] { 0x412, 134, "South Korean Won", "Korean Won", PlatformDetection.IsWindows ? "\uc6d0" : "\ub300\ud55c\ubbfc\uad6d\u0020\uc6d0", "KOR", "KOR" };
+            yield return new object[] { 0x412, 134, "South Korean Won", "Korean Won", PlatformDetection.IsNlsGlobalization ? "\uc6d0" : "\ub300\ud55c\ubbfc\uad6d\u0020\uc6d0", "KOR", "KOR" };
             yield return new object[] { 0x40d, 117, "Israeli New Shekel", "Israeli New Sheqel",
-                                                    PlatformDetection.IsWindows || PlatformDetection.ICUVersion.Major >= 58 ? "\u05e9\u05e7\u05dc\u0020\u05d7\u05d3\u05e9" : "\u05e9\u05f4\u05d7", "ISR", "ISR" };
+                                                    PlatformDetection.IsNlsGlobalization || PlatformDetection.ICUVersion.Major >= 58 ? "\u05e9\u05e7\u05dc\u0020\u05d7\u05d3\u05e9" : "\u05e9\u05f4\u05d7", "ISR", "ISR" };
         }
 
         [Theory]
index 9d531ff..8fe6a6d 100644 (file)
     <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\Calendar.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\CalendarAlgorithmType.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\CalendarData.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\CalendarData.Icu.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\CalendarData.Nls.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\CalendarWeekRule.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\CalendricalCalculationsHelper.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\CharUnicodeInfo.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\CharUnicodeInfoData.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\ChineseLunisolarCalendar.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\CompareInfo.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\CompareInfo.Icu.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\CompareInfo.Invariant.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\CompareInfo.Nls.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\CompareOptions.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\CultureData.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\CultureData.Icu.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\CultureData.Nls.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\CultureInfo.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\CultureInfo.Icu.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\CultureInfo.Nls.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\CultureNotFoundException.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\CultureTypes.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\DateTimeFormat.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\HebrewCalendar.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\HebrewNumber.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\HijriCalendar.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\IcuLocaleData.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\IdnMapping.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\IdnMapping.Icu.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\IdnMapping.Nls.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\InternalGlobalizationHelper.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\ISOWeek.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\JapaneseCalendar.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\JapaneseCalendar.Icu.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\JapaneseCalendar.Nls.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\JapaneseLunisolarCalendar.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\JulianCalendar.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\KoreanCalendar.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\KoreanLunisolarCalendar.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\Normalization.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\Normalization.Icu.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\Normalization.Nls.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\NumberFormatInfo.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\NumberStyles.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\PersianCalendar.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\TaiwanLunisolarCalendar.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\TextElementEnumerator.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\TextInfo.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\TextInfo.Icu.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\TextInfo.Nls.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\ThaiBuddhistCalendar.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\TimeSpanFormat.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\TimeSpanParse.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Void.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\WeakReference.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\WeakReference.T.cs" />
+    <Compile Include="$(CommonPath)Interop\Interop.Libraries.cs">
+      <Link>Common\Interop\Interop.Libraries.cs</Link>
+    </Compile>
+    <Compile Include="$(CommonPath)Interop\Interop.Calendar.cs">
+      <Link>Common\Interop\Interop.Calendar.cs</Link>
+    </Compile>
+    <Compile Include="$(CommonPath)Interop\Interop.Casing.cs">
+      <Link>Common\Interop\Interop.Casing.cs</Link>
+    </Compile>
+    <Compile Include="$(CommonPath)Interop\Interop.Collation.cs">
+      <Link>Common\Interop\Interop.Collation.cs</Link>
+    </Compile>
+    <Compile Include="$(CommonPath)Interop\Interop.ICU.cs">
+      <Link>Common\Interop\Interop.ICU.cs</Link>
+    </Compile>
+    <Compile Include="$(CommonPath)Interop\Interop.Idna.cs">
+      <Link>Common\Interop\Interop.Idna.cs</Link>
+    </Compile>
+    <Compile Include="$(CommonPath)Interop\Interop.Locale.cs">
+      <Link>Common\Interop\Interop.Locale.cs</Link>
+    </Compile>
+    <Compile Include="$(CommonPath)Interop\Interop.Normalization.cs">
+      <Link>Common\Interop\Interop.Normalization.cs</Link>
+    </Compile>
+    <Compile Include="$(CommonPath)Interop\Interop.ResultCode.cs">
+      <Link>Common\Interop\Interop.ResultCode.cs</Link>
+    </Compile>
+    <Compile Include="$(CommonPath)Interop\Interop.TimeZoneInfo.cs">
+      <Link>Common\Interop\Interop.TimeZoneInfo.cs</Link>
+    </Compile>
+    <Compile Include="$(CommonPath)Interop\Interop.Utils.cs">
+      <Link>Common\Interop\Interop.Utils.cs</Link>
+    </Compile>
+    <Compile Include="$(CommonPath)Interop\Windows\Interop.Errors.cs">
+      <Link>Common\Interop\Interop.Errors.cs</Link>
+    </Compile>
+    <!-- The CLR internally uses a BOOL type analogous to the Windows BOOL type on Unix -->
+    <Compile Include="$(CommonPath)Interop\Windows\Interop.BOOL.cs">
+      <Link>Common\Interop\Windows\Interop.BOOL.cs</Link>
+    </Compile>
+    <Compile Include="$(CommonPath)Interop\Windows\Kernel32\Interop.Globalization.cs">
+      <Link>Common\Interop\Windows\Kernel32\Interop.Globalization.cs</Link>
+    </Compile>
+    <Compile Include="$(CommonPath)Interop\Windows\Kernel32\Interop.ResolveLocaleName.cs">
+      <Link>Common\Interop\Windows\Kernel32\Interop.ResolveLocaleName.cs</Link>
+    </Compile>
+    <Compile Include="$(CommonPath)Interop\Windows\Normaliz\Interop.Idna.cs">
+      <Link>Common\Interop\Windows\Normaliz\Interop.Idna.cs</Link>
+    </Compile>
+    <Compile Include="$(CommonPath)Interop\Windows\Normaliz\Interop.Normalization.cs">
+      <Link>Common\Interop\Windows\Normaliz\Interop.Normalization.cs</Link>
+    </Compile>
     <Compile Include="$(CommonPath)System\HResults.cs">
       <Link>Common\System\HResults.cs</Link>
     </Compile>
     <Compile Include="$(CommonPath)Interop\Windows\Crypt32\Interop.CryptProtectMemory.cs">
       <Link>Common\Interop\Windows\Crypt32\Interop.CryptProtectMemory.cs</Link>
     </Compile>
-    <Compile Include="$(CommonPath)Interop\Windows\Interop.BOOL.cs">
-      <Link>Common\Interop\Windows\Interop.BOOL.cs</Link>
-    </Compile>
     <Compile Include="$(CommonPath)Interop\Windows\Interop.BOOLEAN.cs">
       <Link>Common\Interop\Windows\Interop.BOOLEAN.cs</Link>
     </Compile>
     <Compile Include="$(CommonPath)Interop\Windows\Kernel32\Interop.GetTempPathW.cs">
       <Link>Common\Interop\Windows\Kernel32\Interop.GetTempPathW.cs</Link>
     </Compile>
-    <Compile Include="$(CommonPath)Interop\Windows\Kernel32\Interop.Globalization.cs">
-      <Link>Common\Interop\Windows\Kernel32\Interop.Globalization.cs</Link>
-    </Compile>
     <Compile Include="$(CommonPath)Interop\Windows\Kernel32\Interop.GlobalMemoryStatusEx.cs">
       <Link>Common\Interop\Windows\Kernel32\Interop.GlobalMemoryStatusEx.cs</Link>
     </Compile>
     <Compile Include="$(CommonPath)Interop\Windows\Kernel32\Interop.ReadFile_SafeHandle_NativeOverlapped.cs">
       <Link>Common\Interop\Windows\Kernel32\Interop.ReadFile_SafeHandle_NativeOverlapped.cs</Link>
     </Compile>
-    <Compile Include="$(CommonPath)Interop\Windows\Kernel32\Interop.ResolveLocaleName.cs">
-      <Link>Common\Interop\Windows\Kernel32\Interop.ResolveLocaleName.cs</Link>
-    </Compile>
     <Compile Include="$(CommonPath)Interop\Windows\Kernel32\Interop.SECURITY_ATTRIBUTES.cs">
       <Link>Common\Interop\Windows\Kernel32\Interop.SECURITY_ATTRIBUTES.cs</Link>
     </Compile>
     <Compile Include="$(CommonPath)Interop\Windows\Kernel32\Interop.WriteFile_SafeHandle_NativeOverlapped.cs">
       <Link>Common\Interop\Windows\Kernel32\Interop.WriteFile_SafeHandle_NativeOverlapped.cs</Link>
     </Compile>
-    <Compile Include="$(CommonPath)Interop\Windows\Normaliz\Interop.Idna.cs">
-      <Link>Common\Interop\Windows\Normaliz\Interop.Idna.cs</Link>
-    </Compile>
-    <Compile Include="$(CommonPath)Interop\Windows\Normaliz\Interop.Normalization.cs">
-      <Link>Common\Interop\Windows\Normaliz\Interop.Normalization.cs</Link>
-    </Compile>
     <Compile Include="$(CommonPath)Interop\Windows\NtDll\Interop.NtQueryInformationFile.cs">
       <Link>Common\Interop\Windows\NtDll\Interop.NtQueryInformationFile.cs</Link>
     </Compile>
     <Compile Include="$(MSBuildThisFileDirectory)System\Diagnostics\DebugProvider.Windows.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Diagnostics\Stopwatch.Windows.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Diagnostics\Tracing\RuntimeEventSourceHelper.Windows.cs" Condition="'$(FeaturePerfTracing)' == 'true'" />
-    <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\CalendarData.Windows.cs" />
-    <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\CultureInfo.Windows.cs" />
-    <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\CompareInfo.Windows.cs" />
-    <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\CultureData.Windows.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\HijriCalendar.Win32.cs" />
-    <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\IdnMapping.Windows.cs" />
-    <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\JapaneseCalendar.Win32.cs" />
-    <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\Normalization.Windows.cs" />
-    <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\TextInfo.Windows.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Guid.Windows.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\IO\DriveInfoInternal.Windows.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\IO\FileStream.Windows.cs" />
   <!-- CoreCLR uses PAL layer that emulates Windows API on Unix. This is bridge for that PAL layer. See issue dotnet/runtime/#31721. -->
   <ItemGroup Condition="'$(TargetsWindows)' == 'true' or '$(FeatureCoreCLR)'=='true'">
     <Compile Include="$(MSBuildThisFileDirectory)Microsoft\Win32\SafeHandles\SafeWaitHandle.Windows.cs" />
-    <Compile Include="$(CommonPath)Interop\Windows\Interop.Errors.cs" />
     <Compile Include="$(CommonPath)Interop\Windows\OleAut32\Interop.SysAllocStringLen.cs">
       <Link>Common\Interop\Windows\OleAut32\Interop.SysAllocStringLen.cs</Link>
     </Compile>
     <Compile Include="$(CommonPath)Interop\Unix\Interop.Libraries.cs">
       <Link>Common\Interop\Unix\Interop.Libraries.cs</Link>
     </Compile>
-    <Compile Include="$(CommonPath)Interop\Unix\System.Globalization.Native\Interop.Calendar.cs">
-      <Link>Common\Interop\Unix\System.Globalization.Native\Interop.Calendar.cs</Link>
-    </Compile>
-    <Compile Include="$(CommonPath)Interop\Unix\System.Globalization.Native\Interop.Casing.cs">
-      <Link>Common\Interop\Unix\System.Globalization.Native\Interop.Casing.cs</Link>
-    </Compile>
-    <Compile Include="$(CommonPath)Interop\Unix\System.Globalization.Native\Interop.Collation.cs">
-      <Link>Common\Interop\Unix\System.Globalization.Native\Interop.Collation.cs</Link>
-    </Compile>
-    <Compile Include="$(CommonPath)Interop\Unix\System.Globalization.Native\Interop.ICU.cs">
-      <Link>Common\Interop\Unix\System.Globalization.Native\Interop.ICU.cs</Link>
-    </Compile>
-    <Compile Include="$(CommonPath)Interop\Unix\System.Globalization.Native\Interop.Idna.cs">
-      <Link>Common\Interop\Unix\System.Globalization.Native\Interop.Idna.cs</Link>
-    </Compile>
-    <Compile Include="$(CommonPath)Interop\Unix\System.Globalization.Native\Interop.Locale.cs">
-      <Link>Common\Interop\Unix\System.Globalization.Native\Interop.Locale.cs</Link>
-    </Compile>
-    <Compile Include="$(CommonPath)Interop\Unix\System.Globalization.Native\Interop.Normalization.cs">
-      <Link>Common\Interop\Unix\System.Globalization.Native\Interop.Normalization.cs</Link>
-    </Compile>
-    <Compile Include="$(CommonPath)Interop\Unix\System.Globalization.Native\Interop.ResultCode.cs">
-      <Link>Common\Interop\Unix\System.Globalization.Native\Interop.ResultCode.cs</Link>
-    </Compile>
-    <Compile Include="$(CommonPath)Interop\Unix\System.Globalization.Native\Interop.TimeZoneInfo.cs">
-      <Link>Common\Interop\Unix\System.Globalization.Native\Interop.TimeZoneInfo.cs</Link>
-    </Compile>
-    <Compile Include="$(CommonPath)Interop\Unix\System.Globalization.Native\Interop.Utils.cs">
-      <Link>Common\Interop\Unix\System.Globalization.Native\Interop.Utils.cs</Link>
-    </Compile>
     <Compile Include="$(CommonPath)Interop\Unix\System.Native\Interop.Access.cs">
       <Link>Common\Interop\Unix\System.Native\Interop.Access.cs</Link>
     </Compile>
     <Compile Include="$(MSBuildThisFileDirectory)System\Environment.NoRegistry.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Environment.Unix.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Environment.Unix.GetFolderPathCore.cs" Condition="'$(TargetsiOS)' != 'true' and '$(TargetstvOS)' != 'true'" />
-    <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\CalendarData.Unix.cs" />
-    <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\CompareInfo.Unix.cs" />
-    <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\CultureData.Unix.cs" />
-    <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\CultureInfo.Unix.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\HijriCalendar.Unix.cs" />
-    <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\IdnMapping.Unix.cs" />
-    <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\JapaneseCalendar.Unix.cs" />
-    <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\LocaleData.Unix.cs" />
-    <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\Normalization.Unix.cs" />
-    <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\TextInfo.Unix.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Guid.Unix.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\IO\DriveInfoInternal.Unix.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\IO\FileStream.OSX.cs" Condition="'$(TargetsOSX)' == 'true' or '$(TargetsiOS)' == 'true' or '$(TargetstvOS)' == 'true'" />
index 7d86971..48e34ff 100644 (file)
@@ -725,7 +725,7 @@ namespace System.Globalization
 
         internal static int GetSystemTwoDigitYearSetting(CalendarId CalID, int defaultYearValue)
         {
-            int twoDigitYearMax = CalendarData.GetTwoDigitYearMax(CalID);
+            int twoDigitYearMax = GlobalizationMode.UseNls ? CalendarData.NlsGetTwoDigitYearMax(CalID) : CalendarData.IcuGetTwoDigitYearMax(CalID);
             return twoDigitYearMax >= 0 ? twoDigitYearMax : defaultYearValue;
         }
     }
@@ -31,8 +31,10 @@ namespace System.Globalization
 
     internal partial class CalendarData
     {
-        private bool LoadCalendarDataFromSystem(string localeName, CalendarId calendarId)
+        private bool IcuLoadCalendarDataFromSystem(string localeName, CalendarId calendarId)
         {
+            Debug.Assert(!GlobalizationMode.UseNls);
+
             bool result = true;
 
             // these can return null but are later replaced with String.Empty or other non-nullable value
@@ -79,17 +81,20 @@ namespace System.Globalization
             return result;
         }
 
-        internal static int GetTwoDigitYearMax(CalendarId calendarId)
+        internal static int IcuGetTwoDigitYearMax(CalendarId calendarId)
         {
+            Debug.Assert(!GlobalizationMode.UseNls);
+
             // There is no user override for this value on Linux or in ICU.
             // So just return -1 to use the hard-coded defaults.
             return -1;
         }
 
         // Call native side to figure out which calendars are allowed
-        internal static int GetCalendars(string localeName, bool useUserOverride, CalendarId[] calendars)
+        internal static int IcuGetCalendars(string localeName, bool useUserOverride, CalendarId[] calendars)
         {
             Debug.Assert(!GlobalizationMode.Invariant);
+            Debug.Assert(!GlobalizationMode.UseNls);
 
             // NOTE: there are no 'user overrides' on Linux
             int count = Interop.Globalization.GetCalendars(localeName, calendars, calendars.Length);
@@ -104,8 +109,9 @@ namespace System.Globalization
             return count;
         }
 
-        private static bool SystemSupportsTaiwaneseCalendar()
+        private static bool IcuSystemSupportsTaiwaneseCalendar()
         {
+            Debug.Assert(!GlobalizationMode.UseNls);
             return true;
         }
 
@@ -133,7 +139,7 @@ namespace System.Globalization
         {
             datePatterns = null;
 
-            EnumCalendarsData callbackContext = default;
+            IcuEnumCalendarsData callbackContext = default;
             callbackContext.Results = new List<string>();
             callbackContext.DisallowDuplicates = true;
             bool result = EnumCalendarInfo(localeName, calendarId, dataType, ref callbackContext);
@@ -358,7 +364,7 @@ namespace System.Globalization
         {
             monthNames = null;
 
-            EnumCalendarsData callbackContext = default;
+            IcuEnumCalendarsData callbackContext = default;
             callbackContext.Results = new List<string>();
             bool result = EnumCalendarInfo(localeName, calendarId, dataType, ref callbackContext);
             if (result)
@@ -406,7 +412,7 @@ namespace System.Globalization
         {
             calendarData = null;
 
-            EnumCalendarsData callbackContext = default;
+            IcuEnumCalendarsData callbackContext = default;
             callbackContext.Results = new List<string>();
             bool result = EnumCalendarInfo(localeName, calendarId, dataType, ref callbackContext);
             if (result)
@@ -417,7 +423,7 @@ namespace System.Globalization
             return result;
         }
 
-        private static unsafe bool EnumCalendarInfo(string localeName, CalendarId calendarId, CalendarDataType dataType, ref EnumCalendarsData callbackContext)
+        private static unsafe bool EnumCalendarInfo(string localeName, CalendarId calendarId, CalendarDataType dataType, ref IcuEnumCalendarsData callbackContext)
         {
             return Interop.Globalization.EnumCalendarInfo(EnumCalendarInfoCallback, localeName, calendarId, dataType, (IntPtr)Unsafe.AsPointer(ref callbackContext));
         }
@@ -426,7 +432,7 @@ namespace System.Globalization
         {
             try
             {
-                ref EnumCalendarsData callbackContext = ref Unsafe.As<byte, EnumCalendarsData>(ref *(byte*)context);
+                ref IcuEnumCalendarsData callbackContext = ref Unsafe.As<byte, IcuEnumCalendarsData>(ref *(byte*)context);
 
                 if (callbackContext.DisallowDuplicates)
                 {
@@ -450,7 +456,7 @@ namespace System.Globalization
             }
         }
 
-        private struct EnumCalendarsData
+        private struct IcuEnumCalendarsData
         {
             public List<string> Results;
             public bool DisallowDuplicates;
@@ -10,9 +10,10 @@ namespace System.Globalization
 {
     internal partial class CalendarData
     {
-        private bool LoadCalendarDataFromSystem(string localeName, CalendarId calendarId)
+        private bool NlsLoadCalendarDataFromSystem(string localeName, CalendarId calendarId)
         {
             Debug.Assert(!GlobalizationMode.Invariant);
+            Debug.Assert(GlobalizationMode.UseNls);
 
             bool ret = true;
 
@@ -106,17 +107,23 @@ namespace System.Globalization
         }
 
         // Get native two digit year max
-        internal static int GetTwoDigitYearMax(CalendarId calendarId) =>
-            GlobalizationMode.Invariant ? Invariant.iTwoDigitYearMax :
-            CallGetCalendarInfoEx(null, calendarId, CAL_ITWODIGITYEARMAX, out int twoDigitYearMax) ? twoDigitYearMax :
-            -1;
+        internal static int NlsGetTwoDigitYearMax(CalendarId calendarId)
+        {
+            Debug.Assert(GlobalizationMode.UseNls);
+
+            return GlobalizationMode.Invariant ? Invariant.iTwoDigitYearMax :
+                    CallGetCalendarInfoEx(null, calendarId, CAL_ITWODIGITYEARMAX, out int twoDigitYearMax) ?
+                        twoDigitYearMax :
+                        -1;
+        }
 
         // Call native side to figure out which calendars are allowed
-        internal static int GetCalendars(string localeName, bool useUserOverride, CalendarId[] calendars)
+        internal static int NlsGetCalendars(string localeName, bool useUserOverride, CalendarId[] calendars)
         {
             Debug.Assert(!GlobalizationMode.Invariant);
+            Debug.Assert(GlobalizationMode.UseNls);
 
-            EnumCalendarsData data = default;
+            NlsEnumCalendarsData data = default;
             data.userOverride = 0;
             data.calendars = new List<int>();
 
@@ -147,9 +154,10 @@ namespace System.Globalization
             return data.calendars.Count;
         }
 
-        private static bool SystemSupportsTaiwaneseCalendar()
+        private static bool NlsSystemSupportsTaiwaneseCalendar()
         {
             Debug.Assert(!GlobalizationMode.Invariant);
+            Debug.Assert(GlobalizationMode.UseNls);
 
             // Taiwanese calendar get listed as one of the optional zh-TW calendars only when having zh-TW UI
             return CallGetCalendarInfoEx("zh-TW", CalendarId.TAIWAN, CAL_SCALNAME, out string _);
@@ -221,7 +229,7 @@ namespace System.Globalization
                     // Taiwan calendar data is not always in all language version of OS due to Geopolical reasons.
                     // It is only available in zh-TW localized versions of Windows.
                     // Let's check if OS supports it.  If not, fallback to Greogrian localized for Taiwan calendar.
-                    if (!SystemSupportsTaiwaneseCalendar())
+                    if (!NlsSystemSupportsTaiwaneseCalendar())
                     {
                         calendar = CalendarId.GREGORIAN;
                     }
@@ -410,7 +418,7 @@ namespace System.Globalization
         //
         // struct to help our calendar data enumaration callback
         //
-        private struct EnumCalendarsData
+        public struct NlsEnumCalendarsData
         {
             public int userOverride;   // user override value (if found)
             public List<int> calendars;      // list of calendars found so far
@@ -419,7 +427,7 @@ namespace System.Globalization
         // [NativeCallable(CallingConvention = CallingConvention.StdCall)]
         private static unsafe Interop.BOOL EnumCalendarsCallback(char* lpCalendarInfoString, uint calendar, IntPtr reserved, void* lParam)
         {
-            ref EnumCalendarsData context = ref Unsafe.As<byte, EnumCalendarsData>(ref *(byte*)lParam);
+            ref NlsEnumCalendarsData context = ref Unsafe.As<byte, NlsEnumCalendarsData>(ref *(byte*)lParam);
             try
             {
                 // If we had a user override, check to make sure this differs
index 997cfb4..72d14b3 100644 (file)
@@ -105,7 +105,11 @@ namespace System.Globalization
 
             Debug.Assert(!GlobalizationMode.Invariant);
 
-            if (!LoadCalendarDataFromSystem(localeName, calendarId))
+            bool loadedCalendarData = GlobalizationMode.UseNls ?
+                                        NlsLoadCalendarDataFromSystem(localeName, calendarId) :
+                                        IcuLoadCalendarDataFromSystem(localeName, calendarId);
+
+            if (!loadedCalendarData)
             {
                 // LoadCalendarDataFromSystem sometimes can fail on Linux if the installed ICU package is missing some resources.
                 // The ICU package can miss some resources in some cases like if someone compile and build the ICU package manually or ICU has a regression.
@@ -376,5 +380,9 @@ namespace System.Globalization
 
             return "en-US";
         }
+
+        private bool SystemSupportsTaiwaneseCalendar() => GlobalizationMode.UseNls ?
+                                                            NlsSystemSupportsTaiwaneseCalendar() :
+                                                            IcuSystemSupportsTaiwaneseCalendar();
     }
 }
@@ -12,21 +12,18 @@ namespace System.Globalization
     public partial class CompareInfo
     {
         [NonSerialized]
-        private IntPtr _sortHandle;
-
-        [NonSerialized]
         private bool _isAsciiEqualityOrdinal;
 
-        private void InitSort(CultureInfo culture)
+        private void IcuInitSortHandle()
         {
-            _sortName = culture.SortName;
-
             if (GlobalizationMode.Invariant)
             {
                 _isAsciiEqualityOrdinal = true;
             }
             else
             {
+                Debug.Assert(!GlobalizationMode.UseNls);
+
                 // Inline the following condition to avoid potential implementation cycles within globalization
                 //
                 // _isAsciiEqualityOrdinal = _sortName == "" || _sortName == "en" || _sortName.StartsWith("en-", StringComparison.Ordinal);
@@ -38,9 +35,10 @@ namespace System.Globalization
             }
         }
 
-        internal static unsafe int IndexOfOrdinalCore(ReadOnlySpan<char> source, ReadOnlySpan<char> value, bool ignoreCase, bool fromBeginning)
+        private static unsafe int IcuIndexOfOrdinalCore(ReadOnlySpan<char> source, ReadOnlySpan<char> value, bool ignoreCase, bool fromBeginning)
         {
             Debug.Assert(!GlobalizationMode.Invariant);
+            Debug.Assert(!GlobalizationMode.UseNls);
             Debug.Assert(!value.IsEmpty);
 
             // Ordinal (non-linguistic) comparisons require the length of the target string to be no greater
@@ -99,9 +97,10 @@ namespace System.Globalization
             return -1;
         }
 
-        internal static unsafe int LastIndexOfOrdinalCore(string source, string value, int startIndex, int count, bool ignoreCase)
+        private static unsafe int IcuLastIndexOfOrdinalCore(string source, string value, int startIndex, int count, bool ignoreCase)
         {
             Debug.Assert(!GlobalizationMode.Invariant);
+            Debug.Assert(!GlobalizationMode.UseNls);
 
             Debug.Assert(source != null);
             Debug.Assert(value != null);
@@ -148,9 +147,10 @@ namespace System.Globalization
             return -1;
         }
 
-        private static unsafe int CompareStringOrdinalIgnoreCase(ref char string1, int count1, ref char string2, int count2)
+        private static unsafe int IcuCompareStringOrdinalIgnoreCase(ref char string1, int count1, ref char string2, int count2)
         {
             Debug.Assert(!GlobalizationMode.Invariant);
+            Debug.Assert(!GlobalizationMode.UseNls);
 
             Debug.Assert(count1 > 0);
             Debug.Assert(count2 > 0);
@@ -167,9 +167,10 @@ namespace System.Globalization
         // TODO https://github.com/dotnet/runtime/issues/8890:
         // This method shouldn't be necessary, as we should be able to just use the overload
         // that takes two spans.  But due to this issue, that's adding significant overhead.
-        private unsafe int CompareString(ReadOnlySpan<char> string1, string string2, CompareOptions options)
+        private unsafe int IcuCompareString(ReadOnlySpan<char> string1, string string2, CompareOptions options)
         {
             Debug.Assert(!GlobalizationMode.Invariant);
+            Debug.Assert(!GlobalizationMode.UseNls);
             Debug.Assert(string2 != null);
             Debug.Assert((options & (CompareOptions.Ordinal | CompareOptions.OrdinalIgnoreCase)) == 0);
 
@@ -183,9 +184,10 @@ namespace System.Globalization
             }
         }
 
-        private unsafe int CompareString(ReadOnlySpan<char> string1, ReadOnlySpan<char> string2, CompareOptions options)
+        private unsafe int IcuCompareString(ReadOnlySpan<char> string1, ReadOnlySpan<char> string2, CompareOptions options)
         {
             Debug.Assert(!GlobalizationMode.Invariant);
+            Debug.Assert(!GlobalizationMode.UseNls);
             Debug.Assert((options & (CompareOptions.Ordinal | CompareOptions.OrdinalIgnoreCase)) == 0);
 
             // Unlike NLS, ICU (ucol_getSortKey) allows passing nullptr for either of the source arguments
@@ -198,9 +200,10 @@ namespace System.Globalization
             }
         }
 
-        internal unsafe int IndexOfCore(string source, string target, int startIndex, int count, CompareOptions options, int* matchLengthPtr)
+        private unsafe int IcuIndexOfCore(string source, string target, int startIndex, int count, CompareOptions options, int* matchLengthPtr)
         {
             Debug.Assert(!GlobalizationMode.Invariant);
+            Debug.Assert(!GlobalizationMode.UseNls);
 
             Debug.Assert(!string.IsNullOrEmpty(source));
             Debug.Assert(target != null);
@@ -228,10 +231,10 @@ namespace System.Globalization
             return index != -1 ? index + startIndex : -1;
         }
 
-        // For now, this method is only called from Span APIs with either options == CompareOptions.None or CompareOptions.IgnoreCase
-        internal unsafe int IndexOfCore(ReadOnlySpan<char> source, ReadOnlySpan<char> target, CompareOptions options, int* matchLengthPtr, bool fromBeginning)
+        private unsafe int IcuIndexOfCore(ReadOnlySpan<char> source, ReadOnlySpan<char> target, CompareOptions options, int* matchLengthPtr, bool fromBeginning)
         {
             Debug.Assert(!GlobalizationMode.Invariant);
+            Debug.Assert(!GlobalizationMode.UseNls);
             Debug.Assert(source.Length != 0);
             Debug.Assert(target.Length != 0);
 
@@ -457,9 +460,10 @@ namespace System.Globalization
             }
         }
 
-        private unsafe int LastIndexOfCore(string source, string target, int startIndex, int count, CompareOptions options)
+        private unsafe int IcuLastIndexOfCore(string source, string target, int startIndex, int count, CompareOptions options)
         {
             Debug.Assert(!GlobalizationMode.Invariant);
+            Debug.Assert(!GlobalizationMode.UseNls);
 
             Debug.Assert(!string.IsNullOrEmpty(source));
             Debug.Assert(target != null);
@@ -475,7 +479,7 @@ namespace System.Globalization
 
             if (options == CompareOptions.Ordinal)
             {
-                return LastIndexOfOrdinalCore(source, target, startIndex, count, ignoreCase: false);
+                return IcuLastIndexOfOrdinalCore(source, target, startIndex, count, ignoreCase: false);
             }
 
             // startIndex is the index into source where we start search backwards from. leftStartIndex is the index into source
@@ -503,7 +507,7 @@ namespace System.Globalization
             return lastIndex != -1 ? lastIndex + leftStartIndex : -1;
         }
 
-        private unsafe bool StartsWith(ReadOnlySpan<char> source, ReadOnlySpan<char> prefix, CompareOptions options)
+        private unsafe bool IcuStartsWith(ReadOnlySpan<char> source, ReadOnlySpan<char> prefix, CompareOptions options)
         {
             Debug.Assert(!GlobalizationMode.Invariant);
 
@@ -656,9 +660,10 @@ namespace System.Globalization
             }
         }
 
-        private unsafe bool EndsWith(ReadOnlySpan<char> source, ReadOnlySpan<char> suffix, CompareOptions options)
+        private unsafe bool IcuEndsWith(ReadOnlySpan<char> source, ReadOnlySpan<char> suffix, CompareOptions options)
         {
             Debug.Assert(!GlobalizationMode.Invariant);
+            Debug.Assert(!GlobalizationMode.UseNls);
 
             Debug.Assert(!suffix.IsEmpty);
             Debug.Assert((options & (CompareOptions.Ordinal | CompareOptions.OrdinalIgnoreCase)) == 0);
@@ -771,9 +776,10 @@ namespace System.Globalization
             }
         }
 
-        private unsafe SortKey CreateSortKey(string source, CompareOptions options)
+        private unsafe SortKey IcuCreateSortKey(string source, CompareOptions options)
         {
             Debug.Assert(!GlobalizationMode.Invariant);
+            Debug.Assert(!GlobalizationMode.UseNls);
 
             if (source==null) { throw new ArgumentNullException(nameof(source)); }
 
@@ -800,9 +806,10 @@ namespace System.Globalization
             return new SortKey(this, source, options, keyData);
         }
 
-        private static unsafe bool IsSortable(char *text, int length)
+        private static unsafe bool IcuIsSortable(char *text, int length)
         {
             Debug.Assert(!GlobalizationMode.Invariant);
+            Debug.Assert(!GlobalizationMode.UseNls);
 
             int index = 0;
             UnicodeCategory uc;
@@ -843,9 +850,10 @@ namespace System.Globalization
         // ---- PAL layer ends here ----
         // -----------------------------
 
-        internal unsafe int GetHashCodeOfStringCore(ReadOnlySpan<char> source, CompareOptions options)
+        private unsafe int IcuGetHashCodeOfString(ReadOnlySpan<char> source, CompareOptions options)
         {
             Debug.Assert(!GlobalizationMode.Invariant);
+            Debug.Assert(!GlobalizationMode.UseNls);
             Debug.Assert((options & (CompareOptions.Ordinal | CompareOptions.OrdinalIgnoreCase)) == 0);
 
             // according to ICU User Guide the performance of ucol_getSortKey is worse when it is called with null output buffer
@@ -914,9 +922,10 @@ namespace System.Globalization
             return (options & CompareOptions.IgnoreSymbols) == 0;
         }
 
-        private SortVersion GetSortVersion()
+        private SortVersion IcuGetSortVersion()
         {
             Debug.Assert(!GlobalizationMode.Invariant);
+            Debug.Assert(!GlobalizationMode.UseNls);
 
             int sortVersion = Interop.Globalization.GetSortVersion(_sortHandle);
             return new SortVersion(sortVersion, LCID, new Guid(sortVersion, 0, 0, 0, 0, 0, 0,
@@ -10,7 +10,13 @@ namespace System.Globalization
 {
     public partial class CompareInfo
     {
-        internal static unsafe IntPtr GetSortHandle(string cultureName)
+        private void NlsInitSortHandle()
+        {
+            Debug.Assert(GlobalizationMode.UseNls);
+            _sortHandle = NlsGetSortHandle(_sortName);
+        }
+
+        internal static unsafe IntPtr NlsGetSortHandle(string cultureName)
         {
             if (GlobalizationMode.Invariant)
             {
@@ -36,12 +42,6 @@ namespace System.Globalization
             return IntPtr.Zero;
         }
 
-        private void InitSort(CultureInfo culture)
-        {
-            _sortName = culture.SortName;
-            _sortHandle = GetSortHandle(_sortName);
-        }
-
         private static unsafe int FindStringOrdinal(
             uint dwFindStringOrdinalFlags,
             ReadOnlySpan<char> source,
@@ -75,9 +75,10 @@ namespace System.Globalization
             }
         }
 
-        internal static int IndexOfOrdinalCore(ReadOnlySpan<char> source, ReadOnlySpan<char> value, bool ignoreCase, bool fromBeginning)
+        private static int NlsIndexOfOrdinalCore(ReadOnlySpan<char> source, ReadOnlySpan<char> value, bool ignoreCase, bool fromBeginning)
         {
             Debug.Assert(!GlobalizationMode.Invariant);
+            Debug.Assert(GlobalizationMode.UseNls);
 
             Debug.Assert(source.Length != 0);
             Debug.Assert(value.Length != 0);
@@ -86,9 +87,10 @@ namespace System.Globalization
             return FindStringOrdinal(positionFlag, source, value, ignoreCase);
         }
 
-        internal static int LastIndexOfOrdinalCore(string source, string value, int startIndex, int count, bool ignoreCase)
+        private static int NlsLastIndexOfOrdinalCore(string source, string value, int startIndex, int count, bool ignoreCase)
         {
             Debug.Assert(!GlobalizationMode.Invariant);
+            Debug.Assert(GlobalizationMode.UseNls);
 
             Debug.Assert(source != null);
             Debug.Assert(value != null);
@@ -102,9 +104,10 @@ namespace System.Globalization
             return result;
         }
 
-        private unsafe int GetHashCodeOfStringCore(ReadOnlySpan<char> source, CompareOptions options)
+        private unsafe int NlsGetHashCodeOfString(ReadOnlySpan<char> source, CompareOptions options)
         {
             Debug.Assert(!GlobalizationMode.Invariant);
+            Debug.Assert(GlobalizationMode.UseNls);
             Debug.Assert((options & (CompareOptions.Ordinal | CompareOptions.OrdinalIgnoreCase)) == 0);
 
             // LCMapStringEx doesn't support passing cchSrc = 0, so if given a null or empty input
@@ -166,9 +169,10 @@ namespace System.Globalization
             }
         }
 
-        private static unsafe int CompareStringOrdinalIgnoreCase(ref char string1, int count1, ref char string2, int count2)
+        private static unsafe int NlsCompareStringOrdinalIgnoreCase(ref char string1, int count1, ref char string2, int count2)
         {
             Debug.Assert(!GlobalizationMode.Invariant);
+            Debug.Assert(GlobalizationMode.UseNls);
 
             Debug.Assert(count1 > 0);
             Debug.Assert(count2 > 0);
@@ -192,10 +196,11 @@ namespace System.Globalization
         // TODO https://github.com/dotnet/runtime/issues/8890:
         // This method shouldn't be necessary, as we should be able to just use the overload
         // that takes two spans.  But due to this issue, that's adding significant overhead.
-        private unsafe int CompareString(ReadOnlySpan<char> string1, string string2, CompareOptions options)
+        private unsafe int NlsCompareString(ReadOnlySpan<char> string1, string string2, CompareOptions options)
         {
             Debug.Assert(string2 != null);
             Debug.Assert(!GlobalizationMode.Invariant);
+            Debug.Assert(GlobalizationMode.UseNls);
             Debug.Assert((options & (CompareOptions.Ordinal | CompareOptions.OrdinalIgnoreCase)) == 0);
 
             string? localeName = _sortHandle != IntPtr.Zero ? null : _sortName;
@@ -237,9 +242,10 @@ namespace System.Globalization
             }
         }
 
-        private unsafe int CompareString(ReadOnlySpan<char> string1, ReadOnlySpan<char> string2, CompareOptions options)
+        private unsafe int NlsCompareString(ReadOnlySpan<char> string1, ReadOnlySpan<char> string2, CompareOptions options)
         {
             Debug.Assert(!GlobalizationMode.Invariant);
+            Debug.Assert(GlobalizationMode.UseNls);
             Debug.Assert((options & (CompareOptions.Ordinal | CompareOptions.OrdinalIgnoreCase)) == 0);
 
             string? localeName = _sortHandle != IntPtr.Zero ? null : _sortName;
@@ -337,7 +343,7 @@ namespace System.Globalization
             }
         }
 
-        internal unsafe int IndexOfCore(string source, string target, int startIndex, int count, CompareOptions options, int* matchLengthPtr)
+        private unsafe int NlsIndexOfCore(string source, string target, int startIndex, int count, CompareOptions options, int* matchLengthPtr)
         {
             Debug.Assert(!GlobalizationMode.Invariant);
 
@@ -354,9 +360,10 @@ namespace System.Globalization
             return -1;
         }
 
-        internal unsafe int IndexOfCore(ReadOnlySpan<char> source, ReadOnlySpan<char> target, CompareOptions options, int* matchLengthPtr, bool fromBeginning)
+        private unsafe int NlsIndexOfCore(ReadOnlySpan<char> source, ReadOnlySpan<char> target, CompareOptions options, int* matchLengthPtr, bool fromBeginning)
         {
             Debug.Assert(!GlobalizationMode.Invariant);
+            Debug.Assert(GlobalizationMode.UseNls);
 
             Debug.Assert(target.Length != 0);
             Debug.Assert(options == CompareOptions.None || options == CompareOptions.IgnoreCase);
@@ -365,9 +372,10 @@ namespace System.Globalization
             return FindString(positionFlag | (uint)GetNativeCompareFlags(options), source, target, matchLengthPtr);
         }
 
-        private unsafe int LastIndexOfCore(string source, string target, int startIndex, int count, CompareOptions options)
+        private unsafe int NlsLastIndexOfCore(string source, string target, int startIndex, int count, CompareOptions options)
         {
             Debug.Assert(!GlobalizationMode.Invariant);
+            Debug.Assert(GlobalizationMode.UseNls);
 
             Debug.Assert(!string.IsNullOrEmpty(source));
             Debug.Assert(target != null);
@@ -396,7 +404,7 @@ namespace System.Globalization
             return -1;
         }
 
-        private unsafe bool StartsWith(ReadOnlySpan<char> source, ReadOnlySpan<char> prefix, CompareOptions options)
+        private unsafe bool NlsStartsWith(ReadOnlySpan<char> source, ReadOnlySpan<char> prefix, CompareOptions options)
         {
             Debug.Assert(!GlobalizationMode.Invariant);
 
@@ -407,9 +415,10 @@ namespace System.Globalization
             return FindString(FIND_STARTSWITH | (uint)GetNativeCompareFlags(options), source, prefix, null) >= 0;
         }
 
-        private unsafe bool EndsWith(ReadOnlySpan<char> source, ReadOnlySpan<char> suffix, CompareOptions options)
+        private unsafe bool NlsEndsWith(ReadOnlySpan<char> source, ReadOnlySpan<char> suffix, CompareOptions options)
         {
             Debug.Assert(!GlobalizationMode.Invariant);
+            Debug.Assert(GlobalizationMode.UseNls);
 
             Debug.Assert(!suffix.IsEmpty);
             Debug.Assert((options & (CompareOptions.Ordinal | CompareOptions.OrdinalIgnoreCase)) == 0);
@@ -417,10 +426,6 @@ namespace System.Globalization
             return FindString(FIND_ENDSWITH | (uint)GetNativeCompareFlags(options), source, suffix, null) >= 0;
         }
 
-        // PAL ends here
-        [NonSerialized]
-        private IntPtr _sortHandle;
-
         private const uint LCMAP_SORTKEY = 0x00000400;
 
         private const int FIND_STARTSWITH = 0x00100000;
@@ -472,9 +477,10 @@ namespace System.Globalization
             return retValue;
         }
 
-        private unsafe SortKey CreateSortKey(string source, CompareOptions options)
+        private unsafe SortKey NlsCreateSortKey(string source, CompareOptions options)
         {
             Debug.Assert(!GlobalizationMode.Invariant);
+            Debug.Assert(GlobalizationMode.UseNls);
 
             if (source == null) { throw new ArgumentNullException(nameof(source)); }
 
@@ -525,9 +531,10 @@ namespace System.Globalization
             return new SortKey(this, source, options, keyData);
         }
 
-        private static unsafe bool IsSortable(char* text, int length)
+        private static unsafe bool NlsIsSortable(char* text, int length)
         {
             Debug.Assert(!GlobalizationMode.Invariant);
+            Debug.Assert(GlobalizationMode.UseNls);
             Debug.Assert(text != null);
 
             return Interop.Kernel32.IsNLSDefinedString(Interop.Kernel32.COMPARE_STRING, 0, IntPtr.Zero, text, length);
@@ -571,9 +578,10 @@ namespace System.Globalization
             return nativeCompareFlags;
         }
 
-        private unsafe SortVersion GetSortVersion()
+        private unsafe SortVersion NlsGetSortVersion()
         {
             Debug.Assert(!GlobalizationMode.Invariant);
+            Debug.Assert(GlobalizationMode.UseNls);
 
             Interop.Kernel32.NlsVersionInfoEx nlsVersion = default;
             nlsVersion.dwNLSVersionInfoSize = sizeof(Interop.Kernel32.NlsVersionInfoEx);
index 57c6f81..83b475c 100644 (file)
@@ -40,6 +40,9 @@ namespace System.Globalization
         private string m_name;  // The name used to construct this CompareInfo. Do not rename (binary serialization)
 
         [NonSerialized]
+        private IntPtr _sortHandle;
+
+        [NonSerialized]
         private string _sortName = null!; // The name that defines our behavior
 
         [OptionalField(VersionAdded = 3)]
@@ -131,7 +134,7 @@ namespace System.Globalization
             }
 
             char* pChar = &ch;
-            return IsSortable(pChar, 1);
+            return IsSortableCore(pChar, 1);
         }
 
         public static unsafe bool IsSortable(string text)
@@ -153,7 +156,26 @@ namespace System.Globalization
 
             fixed (char* pChar = text)
             {
-                return IsSortable(pChar, text.Length);
+                return IsSortableCore(pChar, text.Length);
+            }
+        }
+
+        private static unsafe bool IsSortableCore(char* pChar, int length) =>
+            GlobalizationMode.UseNls ?
+                NlsIsSortable(pChar, length) :
+                IcuIsSortable(pChar, length);
+
+        private void InitSort(CultureInfo culture)
+        {
+            _sortName = culture.SortName;
+
+            if (GlobalizationMode.UseNls)
+            {
+                NlsInitSortHandle();
+            }
+            else
+            {
+                IcuInitSortHandle();
             }
         }
 
@@ -281,7 +303,7 @@ namespace System.Globalization
                 return string.CompareOrdinal(string1, string2);
             }
 
-            return CompareString(string1.AsSpan(), string2.AsSpan(), options);
+            return CompareStringCore(string1.AsSpan(), string2.AsSpan(), options);
         }
 
         // TODO https://github.com/dotnet/runtime/issues/8890:
@@ -323,7 +345,7 @@ namespace System.Globalization
                     string.CompareOrdinal(string1, string2.AsSpan());
             }
 
-            return CompareString(string1, string2, options);
+            return CompareStringCore(string1, string2, options);
         }
 
         internal int CompareOptionNone(ReadOnlySpan<char> string1, ReadOnlySpan<char> string2)
@@ -336,7 +358,7 @@ namespace System.Globalization
 
             return GlobalizationMode.Invariant ?
                 string.CompareOrdinal(string1, string2) :
-                CompareString(string1, string2, CompareOptions.None);
+                CompareStringCore(string1, string2, CompareOptions.None);
         }
 
         internal int CompareOptionIgnoreCase(ReadOnlySpan<char> string1, ReadOnlySpan<char> string2)
@@ -349,7 +371,7 @@ namespace System.Globalization
 
             return GlobalizationMode.Invariant ?
                 CompareOrdinalIgnoreCase(string1, string2) :
-                CompareString(string1, string2, CompareOptions.IgnoreCase);
+                CompareStringCore(string1, string2, CompareOptions.IgnoreCase);
         }
 
         /// <summary>
@@ -448,9 +470,22 @@ namespace System.Globalization
                 return string.CompareOrdinal(span1, span2);
             }
 
-            return CompareString(span1, span2, options);
+            return CompareStringCore(span1, span2, options);
         }
 
+        // TODO https://github.com/dotnet/runtime/issues/8890:
+        // This method shouldn't be necessary, as we should be able to just use the overload
+        // that takes two spans.  But due to this issue, that's adding significant overhead.
+        private unsafe int CompareStringCore(ReadOnlySpan<char> string1, string string2, CompareOptions options) =>
+            GlobalizationMode.UseNls ?
+                NlsCompareString(string1, string2, options) :
+                IcuCompareString(string1, string2, options);
+
+        private unsafe int CompareStringCore(ReadOnlySpan<char> string1, ReadOnlySpan<char> string2, CompareOptions options) =>
+            GlobalizationMode.UseNls ?
+                NlsCompareString(string1, string2, options) :
+                IcuCompareString(string1, string2, options);
+
         /// <summary>
         /// CompareOrdinalIgnoreCase compare two string ordinally with ignoring the case.
         /// it assumes the strings are Ascii string till we hit non Ascii character in strA or strB and then we continue the comparison by
@@ -528,7 +563,7 @@ namespace System.Globalization
 
             range -= length;
 
-            return CompareStringOrdinalIgnoreCase(ref charA, lengthA - range, ref charB, lengthB - range);
+            return CompareStringOrdinalIgnoreCaseCore(ref charA, lengthA - range, ref charB, lengthB - range);
         }
 
         internal static bool EqualsOrdinalIgnoreCase(ref char charA, ref char charB, int length)
@@ -638,7 +673,7 @@ namespace System.Globalization
         {
             if (!GlobalizationMode.Invariant)
             {
-                return CompareStringOrdinalIgnoreCase(ref charA, length, ref charB, length) == 0;
+                return CompareStringOrdinalIgnoreCaseCore(ref charA, length, ref charB, length) == 0;
             }
             else
             {
@@ -670,6 +705,11 @@ namespace System.Globalization
             }
         }
 
+        private static unsafe int CompareStringOrdinalIgnoreCaseCore(ref char string1, int count1, ref char string2, int count2) =>
+            GlobalizationMode.UseNls ?
+                NlsCompareStringOrdinalIgnoreCase(ref string1, count1, ref string2, count2) :
+                IcuCompareStringOrdinalIgnoreCase(ref string1, count1, ref string2, count2);
+
         /// <summary>
         /// Determines whether prefix is a prefix of string.  If prefix equals
         /// string.Empty, true is returned.
@@ -714,7 +754,7 @@ namespace System.Globalization
                 return source.StartsWith(prefix, (options & CompareOptions.IgnoreCase) != 0 ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal);
             }
 
-            return StartsWith(source, prefix, options);
+            return StartsWithCore(source, prefix, options);
         }
 
         internal bool IsPrefix(ReadOnlySpan<char> source, ReadOnlySpan<char> prefix, CompareOptions options)
@@ -725,9 +765,14 @@ namespace System.Globalization
             Debug.Assert(!GlobalizationMode.Invariant);
             Debug.Assert((options & (CompareOptions.Ordinal | CompareOptions.OrdinalIgnoreCase)) == 0);
 
-            return StartsWith(source, prefix, options);
+            return StartsWithCore(source, prefix, options);
         }
 
+        private unsafe bool StartsWithCore(ReadOnlySpan<char> source, ReadOnlySpan<char> prefix, CompareOptions options) =>
+            GlobalizationMode.UseNls ?
+                NlsStartsWith(source, prefix, options) :
+                IcuStartsWith(source, prefix, options);
+
         public bool IsPrefix(string source, string prefix)
         {
             return IsPrefix(source, prefix, 0);
@@ -777,7 +822,7 @@ namespace System.Globalization
                 return source.EndsWith(suffix, (options & CompareOptions.IgnoreCase) != 0 ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal);
             }
 
-            return EndsWith(source, suffix, options);
+            return EndsWithCore(source, suffix, options);
         }
 
         internal bool IsSuffix(ReadOnlySpan<char> source, ReadOnlySpan<char> suffix, CompareOptions options)
@@ -788,7 +833,7 @@ namespace System.Globalization
             Debug.Assert(!GlobalizationMode.Invariant);
             Debug.Assert((options & (CompareOptions.Ordinal | CompareOptions.OrdinalIgnoreCase)) == 0);
 
-            return EndsWith(source, suffix, options);
+            return EndsWithCore(source, suffix, options);
         }
 
         public bool IsSuffix(string source, string suffix)
@@ -796,6 +841,11 @@ namespace System.Globalization
             return IsSuffix(source, suffix, 0);
         }
 
+        private unsafe bool EndsWithCore(ReadOnlySpan<char> source, ReadOnlySpan<char> suffix, CompareOptions options) =>
+            GlobalizationMode.UseNls ?
+                NlsEndsWith(source, suffix, options) :
+                IcuEndsWith(source, suffix, options);
+
         /// <summary>
         /// Returns the first index where value is found in string.  The
         /// search starts from startIndex and ends at endIndex.  Returns -1 if
@@ -983,6 +1033,11 @@ namespace System.Globalization
             return IndexOfOrdinalCore(source, value, ignoreCase, fromBeginning: false);
         }
 
+        private static int IndexOfOrdinalCore(ReadOnlySpan<char> source, ReadOnlySpan<char> value, bool ignoreCase, bool fromBeginning) =>
+            GlobalizationMode.UseNls ?
+                NlsIndexOfOrdinalCore(source, value, ignoreCase, fromBeginning) :
+                IcuIndexOfOrdinalCore(source, value, ignoreCase, fromBeginning);
+
         internal unsafe int IndexOf(ReadOnlySpan<char> source, ReadOnlySpan<char> value, CompareOptions options)
         {
             Debug.Assert(!GlobalizationMode.Invariant);
@@ -1110,6 +1165,16 @@ namespace System.Globalization
             }
         }
 
+        private unsafe int IndexOfCore(string source, string target, int startIndex, int count, CompareOptions options, int* matchLengthPtr) =>
+            GlobalizationMode.UseNls ?
+                NlsIndexOfCore(source, target, startIndex, count, options, matchLengthPtr) :
+                IcuIndexOfCore(source, target, startIndex, count, options, matchLengthPtr);
+
+        private unsafe int IndexOfCore(ReadOnlySpan<char> source, ReadOnlySpan<char> target, CompareOptions options, int* matchLengthPtr, bool fromBeginning) =>
+            GlobalizationMode.UseNls ?
+                NlsIndexOfCore(source, target, options, matchLengthPtr, fromBeginning) :
+                IcuIndexOfCore(source, target, options, matchLengthPtr, fromBeginning);
+
         internal static int IndexOfOrdinal(string source, string value, int startIndex, int count, bool ignoreCase)
         {
             Debug.Assert(source != null);
@@ -1360,7 +1425,12 @@ namespace System.Globalization
             return LastIndexOfCore(source, value, startIndex, count, options);
         }
 
-        private static int LastIndexOfOrdinal(string source, string value, int startIndex, int count, bool ignoreCase)
+        private int LastIndexOfCore(string source, string target, int startIndex, int count, CompareOptions options) =>
+            GlobalizationMode.UseNls ?
+                NlsLastIndexOfCore(source, target, startIndex, count, options) :
+                IcuLastIndexOfCore(source, target, startIndex, count, options);
+
+        internal static int LastIndexOfOrdinal(string source, string value, int startIndex, int count, bool ignoreCase)
         {
             Debug.Assert(!string.IsNullOrEmpty(source));
             Debug.Assert(value != null);
@@ -1385,7 +1455,9 @@ namespace System.Globalization
                 return -1;
             }
 
-            return LastIndexOfOrdinalCore(source, value, startIndex, count, ignoreCase);
+            return GlobalizationMode.UseNls ?
+                NlsLastIndexOfOrdinalCore(source, value, startIndex, count, ignoreCase) :
+                IcuLastIndexOfOrdinalCore(source, value, startIndex, count, ignoreCase);
         }
 
         /// <summary>
@@ -1398,7 +1470,7 @@ namespace System.Globalization
                 return InvariantCreateSortKey(source, options);
             }
 
-            return CreateSortKey(source, options);
+            return CreateSortKeyCore(source, options);
         }
 
         public SortKey GetSortKey(string source)
@@ -1408,9 +1480,14 @@ namespace System.Globalization
                 return InvariantCreateSortKey(source, CompareOptions.None);
             }
 
-            return CreateSortKey(source, CompareOptions.None);
+            return CreateSortKeyCore(source, CompareOptions.None);
         }
 
+        private SortKey CreateSortKeyCore(string source, CompareOptions options) =>
+            GlobalizationMode.UseNls ?
+                NlsCreateSortKey(source, options) :
+                IcuCreateSortKey(source, options);
+
         public override bool Equals(object? value)
         {
             return value is CompareInfo otherCompareInfo
@@ -1490,6 +1567,11 @@ namespace System.Globalization
             }
         }
 
+        private unsafe int GetHashCodeOfStringCore(ReadOnlySpan<char> source, CompareOptions options) =>
+            GlobalizationMode.UseNls ?
+                NlsGetHashCodeOfString(source, options) :
+                IcuGetHashCodeOfString(source, options);
+
         public override string ToString() => "CompareInfo - " + Name;
 
         public SortVersion Version
@@ -1508,7 +1590,7 @@ namespace System.Globalization
                     }
                     else
                     {
-                        m_SortVersion = GetSortVersion();
+                        m_SortVersion = GlobalizationMode.UseNls ? NlsGetSortVersion() : IcuGetSortVersion();
                     }
                 }
 
@@ -18,11 +18,12 @@ namespace System.Globalization
         /// This method uses the sRealName field (which is initialized by the constructor before this is called) to
         /// initialize the rest of the state of CultureData based on the underlying OS globalization library.
         /// </summary>
-        private unsafe bool InitCultureData()
+        private unsafe bool IcuInitCultureData()
         {
             Debug.Assert(_sRealName != null);
 
             Debug.Assert(!GlobalizationMode.Invariant);
+            Debug.Assert(!GlobalizationMode.UseNls);
 
             string realNameBuffer = _sRealName;
 
@@ -73,7 +74,7 @@ namespace System.Globalization
 
             _bNeutral = TwoLetterISOCountryName.Length == 0;
 
-            _sSpecificCulture = _bNeutral ? LocaleData.GetSpecificCultureName(_sRealName) : _sRealName;
+            _sSpecificCulture = _bNeutral ? IcuLocaleData.GetSpecificCultureName(_sRealName) : _sRealName;
 
             // Remove the sort from sName unless custom culture
             if (index > 0 && !_bNeutral && !IsCustomCultureId(_iLanguage))
@@ -113,26 +114,28 @@ namespace System.Globalization
             return true;
         }
 
-        private string GetLocaleInfo(LocaleStringData type)
+        private string IcuGetLocaleInfo(LocaleStringData type)
         {
             Debug.Assert(!GlobalizationMode.Invariant);
+            Debug.Assert(!GlobalizationMode.UseNls);
 
-            Debug.Assert(_sWindowsName != null, "[CultureData.GetLocaleInfo] Expected _sWindowsName to be populated already");
-            return GetLocaleInfo(_sWindowsName, type);
+            Debug.Assert(_sWindowsName != null, "[CultureData.IcuGetLocaleInfo] Expected _sWindowsName to be populated already");
+            return IcuGetLocaleInfo(_sWindowsName, type);
         }
 
         // For LOCALE_SPARENT we need the option of using the "real" name (forcing neutral names) instead of the
         // "windows" name, which can be specific for downlevel (< windows 7) os's.
-        private unsafe string GetLocaleInfo(string localeName, LocaleStringData type)
+        private unsafe string IcuGetLocaleInfo(string localeName, LocaleStringData type)
         {
-            Debug.Assert(localeName != null, "[CultureData.GetLocaleInfo] Expected localeName to be not be null");
+            Debug.Assert(!GlobalizationMode.UseNls);
+            Debug.Assert(localeName != null, "[CultureData.IcuGetLocaleInfo] Expected localeName to be not be null");
 
             switch (type)
             {
                 case LocaleStringData.NegativeInfinitySymbol:
                     // not an equivalent in ICU; prefix the PositiveInfinitySymbol with NegativeSign
-                    return GetLocaleInfo(localeName, LocaleStringData.NegativeSign) +
-                        GetLocaleInfo(localeName, LocaleStringData.PositiveInfinitySymbol);
+                    return IcuGetLocaleInfo(localeName, LocaleStringData.NegativeSign) +
+                        IcuGetLocaleInfo(localeName, LocaleStringData.PositiveInfinitySymbol);
             }
 
             char* buffer = stackalloc char[ICU_ULOC_KEYWORD_AND_VALUES_CAPACITY];
@@ -140,18 +143,18 @@ namespace System.Globalization
             if (!result)
             {
                 // Failed, just use empty string
-                Debug.Fail("[CultureData.GetLocaleInfo(LocaleStringData)] Failed");
+                Debug.Fail("[CultureData.IcuGetLocaleInfo(LocaleStringData)] Failed");
                 return string.Empty;
             }
 
             return new string(buffer);
         }
 
-        private int GetLocaleInfo(LocaleNumberData type)
+        private int IcuGetLocaleInfo(LocaleNumberData type)
         {
-            Debug.Assert(!GlobalizationMode.Invariant);
+            Debug.Assert(!GlobalizationMode.UseNls);
 
-            Debug.Assert(_sWindowsName != null, "[CultureData.GetLocaleInfo(LocaleNumberData)] Expected _sWindowsName to be populated already");
+            Debug.Assert(_sWindowsName != null, "[CultureData.IcuGetLocaleInfo(LocaleNumberData)] Expected _sWindowsName to be populated already");
 
             switch (type)
             {
@@ -166,22 +169,23 @@ namespace System.Globalization
             if (!result)
             {
                 // Failed, just use 0
-                Debug.Fail("[CultureData.GetLocaleInfo(LocaleNumberData)] failed");
+                Debug.Fail("[CultureData.IcuGetLocaleInfo(LocaleNumberData)] failed");
             }
 
             return value;
         }
 
-        private int[] GetLocaleInfo(LocaleGroupingData type)
+        private int[] IcuGetLocaleInfo(LocaleGroupingData type)
         {
-            Debug.Assert(_sWindowsName != null, "[CultureData.GetLocaleInfo(LocaleGroupingData)] Expected _sWindowsName to be populated already");
+            Debug.Assert(!GlobalizationMode.UseNls);
+            Debug.Assert(_sWindowsName != null, "[CultureData.IcuGetLocaleInfo(LocaleGroupingData)] Expected _sWindowsName to be populated already");
 
             int primaryGroupingSize = 0;
             int secondaryGroupingSize = 0;
             bool result = Interop.Globalization.GetLocaleInfoGroupingSizes(_sWindowsName, (uint)type, ref primaryGroupingSize, ref secondaryGroupingSize);
             if (!result)
             {
-                Debug.Fail("[CultureData.GetLocaleInfo(LocaleGroupingData type)] failed");
+                Debug.Fail("[CultureData.IcuGetLocaleInfo(LocaleGroupingData type)] failed");
             }
 
             if (secondaryGroupingSize == 0)
@@ -192,10 +196,11 @@ namespace System.Globalization
             return new int[] { primaryGroupingSize, secondaryGroupingSize };
         }
 
-        private string GetTimeFormatString() => GetTimeFormatString(shortFormat: false);
+        private string IcuGetTimeFormatString() => IcuGetTimeFormatString(shortFormat: false);
 
-        private unsafe string GetTimeFormatString(bool shortFormat)
+        private unsafe string IcuGetTimeFormatString(bool shortFormat)
         {
+            Debug.Assert(!GlobalizationMode.UseNls);
             Debug.Assert(_sWindowsName != null, "[CultureData.GetTimeFormatString(bool shortFormat)] Expected _sWindowsName to be populated already");
 
             char* buffer = stackalloc char[ICU_ULOC_KEYWORD_AND_VALUES_CAPACITY];
@@ -212,32 +217,32 @@ namespace System.Globalization
             return ConvertIcuTimeFormatString(span.Slice(0, span.IndexOf('\0')));
         }
 
-        private int GetFirstDayOfWeek() => GetLocaleInfo(LocaleNumberData.FirstDayOfWeek);
+        private int IcuGetFirstDayOfWeek() => IcuGetLocaleInfo(LocaleNumberData.FirstDayOfWeek);
 
-        private string[] GetTimeFormats()
+        private string[] IcuGetTimeFormats()
         {
-            string format = GetTimeFormatString(false);
+            string format = IcuGetTimeFormatString(false);
             return new string[] { format };
         }
 
-        private string[] GetShortTimeFormats()
+        private string[] IcuGetShortTimeFormats()
         {
-            string format = GetTimeFormatString(true);
+            string format = IcuGetTimeFormatString(true);
             return new string[] { format };
         }
 
-        private static CultureData? GetCultureDataFromRegionName(string? regionName)
+        private static CultureData? IcuGetCultureDataFromRegionName(string? regionName)
         {
             // no support to lookup by region name, other than the hard-coded list in CultureData
             return null;
         }
 
-        private static string GetLanguageDisplayName(string cultureName)
+        private static string IcuGetLanguageDisplayName(string cultureName)
         {
-            return new CultureInfo(cultureName)._cultureData.GetLocaleInfo(cultureName, LocaleStringData.LocalizedDisplayName);
+            return new CultureInfo(cultureName)._cultureData.IcuGetLocaleInfo(cultureName, LocaleStringData.LocalizedDisplayName);
         }
 
-        private static string? GetRegionDisplayName()
+        private static string? IcuGetRegionDisplayName()
         {
             // use the fallback which is to return NativeName
             return null;
@@ -299,65 +304,75 @@ namespace System.Globalization
             return result.Slice(0, resultPos).ToString();
         }
 
-        private static string? LCIDToLocaleName(int culture)
+        private static string? IcuLCIDToLocaleName(int culture)
         {
             Debug.Assert(!GlobalizationMode.Invariant);
+            Debug.Assert(!GlobalizationMode.UseNls);
 
-            return LocaleData.LCIDToLocaleName(culture);
+            return IcuLocaleData.LCIDToLocaleName(culture);
         }
 
-        private static int LocaleNameToLCID(string cultureName)
+        private static int IcuLocaleNameToLCID(string cultureName)
         {
             Debug.Assert(!GlobalizationMode.Invariant);
+            Debug.Assert(!GlobalizationMode.UseNls);
 
-            int lcid = LocaleData.GetLocaleDataNumericPart(cultureName, LocaleDataParts.Lcid);
+            int lcid = IcuLocaleData.GetLocaleDataNumericPart(cultureName, IcuLocaleDataParts.Lcid);
             return lcid == -1 ? CultureInfo.LOCALE_CUSTOM_UNSPECIFIED : lcid;
         }
 
-        private static int GetAnsiCodePage(string cultureName)
+        private static int IcuGetAnsiCodePage(string cultureName)
         {
-            int ansiCodePage = LocaleData.GetLocaleDataNumericPart(cultureName, LocaleDataParts.AnsiCodePage);
+            Debug.Assert(!GlobalizationMode.UseNls);
+            int ansiCodePage = IcuLocaleData.GetLocaleDataNumericPart(cultureName, IcuLocaleDataParts.AnsiCodePage);
             return ansiCodePage == -1 ? CultureData.Invariant.ANSICodePage : ansiCodePage;
         }
 
-        private static int GetOemCodePage(string cultureName)
+        private static int IcuGetOemCodePage(string cultureName)
         {
-            int oemCodePage = LocaleData.GetLocaleDataNumericPart(cultureName, LocaleDataParts.OemCodePage);
+            Debug.Assert(!GlobalizationMode.UseNls);
+            int oemCodePage = IcuLocaleData.GetLocaleDataNumericPart(cultureName, IcuLocaleDataParts.OemCodePage);
             return oemCodePage == -1 ? CultureData.Invariant.OEMCodePage : oemCodePage;
         }
 
-        private static int GetMacCodePage(string cultureName)
+        private static int IcuGetMacCodePage(string cultureName)
         {
-            int macCodePage = LocaleData.GetLocaleDataNumericPart(cultureName, LocaleDataParts.MacCodePage);
+            Debug.Assert(!GlobalizationMode.UseNls);
+            int macCodePage = IcuLocaleData.GetLocaleDataNumericPart(cultureName, IcuLocaleDataParts.MacCodePage);
             return macCodePage == -1 ? CultureData.Invariant.MacCodePage : macCodePage;
         }
 
-        private static int GetEbcdicCodePage(string cultureName)
+        private static int IcuGetEbcdicCodePage(string cultureName)
         {
-            int ebcdicCodePage = LocaleData.GetLocaleDataNumericPart(cultureName, LocaleDataParts.EbcdicCodePage);
+            Debug.Assert(!GlobalizationMode.UseNls);
+            int ebcdicCodePage = IcuLocaleData.GetLocaleDataNumericPart(cultureName, IcuLocaleDataParts.EbcdicCodePage);
             return ebcdicCodePage == -1 ? CultureData.Invariant.EBCDICCodePage : ebcdicCodePage;
         }
 
-        private static int GetGeoId(string cultureName)
+        private static int IcuGetGeoId(string cultureName)
         {
-            int geoId = LocaleData.GetLocaleDataNumericPart(cultureName, LocaleDataParts.GeoId);
+            Debug.Assert(!GlobalizationMode.UseNls);
+            int geoId = IcuLocaleData.GetLocaleDataNumericPart(cultureName, IcuLocaleDataParts.GeoId);
             return geoId == -1 ? CultureData.Invariant.GeoId : geoId;
         }
 
-        private static int GetDigitSubstitution(string cultureName)
+        private static int IcuGetDigitSubstitution(string cultureName)
         {
-            int digitSubstitution = LocaleData.GetLocaleDataNumericPart(cultureName, LocaleDataParts.DigitSubstitution);
+            Debug.Assert(!GlobalizationMode.UseNls);
+            int digitSubstitution = IcuLocaleData.GetLocaleDataNumericPart(cultureName, IcuLocaleDataParts.DigitSubstitution);
             return digitSubstitution == -1 ? (int) DigitShapes.None : digitSubstitution;
         }
 
-        private static string GetThreeLetterWindowsLanguageName(string cultureName)
+        private static string IcuGetThreeLetterWindowsLanguageName(string cultureName)
         {
-            return LocaleData.GetThreeLetterWindowsLanguageName(cultureName) ?? "ZZZ" /* default lang name */;
+            Debug.Assert(!GlobalizationMode.UseNls);
+            return IcuLocaleData.GetThreeLetterWindowsLanguageName(cultureName) ?? "ZZZ" /* default lang name */;
         }
 
-        private static CultureInfo[] EnumCultures(CultureTypes types)
+        private static CultureInfo[] IcuEnumCultures(CultureTypes types)
         {
             Debug.Assert(!GlobalizationMode.Invariant);
+            Debug.Assert(!GlobalizationMode.UseNls);
 
             if ((types & (CultureTypes.NeutralCultures | CultureTypes.SpecificCultures)) == 0)
             {
@@ -406,15 +421,10 @@ namespace System.Globalization
             return list.ToArray();
         }
 
-        private static string GetConsoleFallbackName(string cultureName)
+        private static string IcuGetConsoleFallbackName(string cultureName)
         {
-            return LocaleData.GetConsoleUICulture(cultureName);
+            Debug.Assert(!GlobalizationMode.UseNls);
+            return IcuLocaleData.GetConsoleUICulture(cultureName);
         }
-
-        internal bool IsWin32Installed => false;
-
-        internal bool IsReplacementCulture => false;
-
-        internal static CultureData GetCurrentRegionData() => CultureInfo.CurrentCulture._cultureData;
     }
 }
@@ -46,9 +46,10 @@ namespace System.Globalization
         /// For a neutral we just populate the neutral name, but we leave the windows name pointing to the
         /// windows locale that's going to provide data for us.
         /// </summary>
-        private unsafe bool InitCultureData()
+        private unsafe bool NlsInitCultureData()
         {
             Debug.Assert(!GlobalizationMode.Invariant);
+            Debug.Assert(GlobalizationMode.UseNls);
 
             int result;
             string realNameBuffer = _sRealName;
@@ -179,23 +180,26 @@ namespace System.Globalization
             return Interop.Kernel32.GetLocaleInfoEx(lpLocaleName, lcType, lpLCData, cchData);
         }
 
-        private string GetLocaleInfo(LocaleStringData type)
+        private string NlsGetLocaleInfo(LocaleStringData type)
         {
+            Debug.Assert(GlobalizationMode.UseNls);
             Debug.Assert(_sWindowsName != null, "[CultureData.DoGetLocaleInfo] Expected _sWindowsName to be populated by already");
-            return GetLocaleInfo(_sWindowsName, type);
+            return NlsGetLocaleInfo(_sWindowsName, type);
         }
 
         // For LOCALE_SPARENT we need the option of using the "real" name (forcing neutral names) instead of the
         // "windows" name, which can be specific for downlevel (< windows 7) os's.
-        private string GetLocaleInfo(string localeName, LocaleStringData type)
+        private string NlsGetLocaleInfo(string localeName, LocaleStringData type)
         {
+            Debug.Assert(GlobalizationMode.UseNls);
             uint lctype = (uint)type;
 
             return GetLocaleInfoFromLCType(localeName, lctype, UseUserOverride);
         }
 
-        private int GetLocaleInfo(LocaleNumberData type)
+        private int NlsGetLocaleInfo(LocaleNumberData type)
         {
+            Debug.Assert(GlobalizationMode.UseNls);
             uint lctype = (uint)type;
 
             // Fix lctype if we don't want overrides
@@ -210,20 +214,23 @@ namespace System.Globalization
             return GetLocaleInfoExInt(_sWindowsName, lctype);
         }
 
-        private int[] GetLocaleInfo(LocaleGroupingData type)
+        private int[] NlsGetLocaleInfo(LocaleGroupingData type)
         {
+            Debug.Assert(GlobalizationMode.UseNls);
             Debug.Assert(_sWindowsName != null, "[CultureData.DoGetLocaleInfoInt] Expected _sWindowsName to be populated by already");
             return ConvertWin32GroupString(GetLocaleInfoFromLCType(_sWindowsName, (uint)type, UseUserOverride));
         }
 
-        private string? GetTimeFormatString()
+        private string? NlsGetTimeFormatString()
         {
+            Debug.Assert(GlobalizationMode.UseNls);
             Debug.Assert(_sWindowsName != null, "[CultureData.DoGetLocaleInfoInt] Expected _sWindowsName to be populated by already");
             return ReescapeWin32String(GetLocaleInfoFromLCType(_sWindowsName, Interop.Kernel32.LOCALE_STIMEFORMAT, UseUserOverride));
         }
 
-        private int GetFirstDayOfWeek()
+        private int NlsGetFirstDayOfWeek()
         {
+            Debug.Assert(GlobalizationMode.UseNls);
             Debug.Assert(_sWindowsName != null, "[CultureData.DoGetLocaleInfoInt] Expected _sWindowsName to be populated by already");
 
             int result = GetLocaleInfoExInt(_sWindowsName, Interop.Kernel32.LOCALE_IFIRSTDAYOFWEEK | (!UseUserOverride ? Interop.Kernel32.LOCALE_NOUSEROVERRIDE : 0));
@@ -232,18 +239,20 @@ namespace System.Globalization
             return ConvertFirstDayOfWeekMonToSun(result);
         }
 
-        private string[]? GetTimeFormats()
+        private string[]? NlsGetTimeFormats()
         {
             // Note that this gets overrides for us all the time
+            Debug.Assert(GlobalizationMode.UseNls);
             Debug.Assert(_sWindowsName != null, "[CultureData.DoEnumTimeFormats] Expected _sWindowsName to be populated by already");
             string[]? result = ReescapeWin32Strings(nativeEnumTimeFormats(_sWindowsName, 0, UseUserOverride));
 
             return result;
         }
 
-        private string[]? GetShortTimeFormats()
+        private string[]? NlsGetShortTimeFormats()
         {
             // Note that this gets overrides for us all the time
+            Debug.Assert(GlobalizationMode.UseNls);
             Debug.Assert(_sWindowsName != null, "[CultureData.DoEnumShortTimeFormats] Expected _sWindowsName to be populated by already");
             string[]? result = ReescapeWin32Strings(nativeEnumTimeFormats(_sWindowsName, Interop.Kernel32.TIME_NOSECONDS, UseUserOverride));
 
@@ -252,9 +261,10 @@ namespace System.Globalization
 
         // Enumerate all system cultures and then try to find out which culture has
         // region name match the requested region name
-        private static CultureData? GetCultureDataFromRegionName(string regionName)
+        private static CultureData? NlsGetCultureDataFromRegionName(string regionName)
         {
             Debug.Assert(!GlobalizationMode.Invariant);
+            Debug.Assert(GlobalizationMode.UseNls);
             Debug.Assert(regionName != null);
 
             EnumLocaleData context;
@@ -275,31 +285,35 @@ namespace System.Globalization
             return null;
         }
 
-        private string GetLanguageDisplayName(string cultureName)
+        private string NlsGetLanguageDisplayName(string cultureName)
         {
+            Debug.Assert(GlobalizationMode.UseNls);
+
             // Usually the UI culture shouldn't be different than what we got from WinRT except
             // if DefaultThreadCurrentUICulture was set
             CultureInfo? ci;
 
             if (CultureInfo.DefaultThreadCurrentUICulture != null &&
-                ((ci = CultureInfo.GetUserDefaultCulture()) != null) &&
+                ((ci = CultureInfo.NlsGetUserDefaultCulture()) != null) &&
                 !CultureInfo.DefaultThreadCurrentUICulture.Name.Equals(ci.Name))
             {
                 return NativeName;
             }
             else
             {
-                return GetLocaleInfo(cultureName, LocaleStringData.LocalizedDisplayName);
+                return NlsGetLocaleInfo(cultureName, LocaleStringData.LocalizedDisplayName);
             }
         }
 
-        private string GetRegionDisplayName()
+        private string NlsGetRegionDisplayName()
         {
+            Debug.Assert(GlobalizationMode.UseNls);
+
             // If the current UI culture matching the OS UI language, we'll get the display name from the OS.
             // otherwise, we use the native name as we don't carry resources for the region display names anyway.
             if (CultureInfo.CurrentUICulture.Name.Equals(CultureInfo.UserDefaultUICulture.Name))
             {
-                return GetLocaleInfo(LocaleStringData.LocalizedCountryName);
+                return NlsGetLocaleInfo(LocaleStringData.LocalizedCountryName);
             }
 
             return NativeCountryName;
@@ -580,16 +594,17 @@ namespace System.Globalization
             return null;
         }
 
-        private static int LocaleNameToLCID(string cultureName)
+        private static int NlsLocaleNameToLCID(string cultureName)
         {
             Debug.Assert(!GlobalizationMode.Invariant);
 
             return Interop.Kernel32.LocaleNameToLCID(cultureName, Interop.Kernel32.LOCALE_ALLOW_NEUTRAL_NAMES);
         }
 
-        private static unsafe string? LCIDToLocaleName(int culture)
+        private static unsafe string? NlsLCIDToLocaleName(int culture)
         {
             Debug.Assert(!GlobalizationMode.Invariant);
+            Debug.Assert(GlobalizationMode.UseNls);
 
             char* pBuffer = stackalloc char[Interop.Kernel32.LOCALE_NAME_MAX_LENGTH + 1]; // +1 for the null termination
             int length = Interop.Kernel32.LCIDToLocaleName(culture, pBuffer, Interop.Kernel32.LOCALE_NAME_MAX_LENGTH + 1, Interop.Kernel32.LOCALE_ALLOW_NEUTRAL_NAMES);
@@ -602,44 +617,52 @@ namespace System.Globalization
             return null;
         }
 
-        private int GetAnsiCodePage(string cultureName)
+        private int NlsGetAnsiCodePage(string cultureName)
         {
-            return GetLocaleInfo(LocaleNumberData.AnsiCodePage);
+            Debug.Assert(GlobalizationMode.UseNls);
+            return NlsGetLocaleInfo(LocaleNumberData.AnsiCodePage);
         }
 
-        private int GetOemCodePage(string cultureName)
+        private int NlsGetOemCodePage(string cultureName)
         {
-            return GetLocaleInfo(LocaleNumberData.OemCodePage);
+            Debug.Assert(GlobalizationMode.UseNls);
+            return NlsGetLocaleInfo(LocaleNumberData.OemCodePage);
         }
 
-        private int GetMacCodePage(string cultureName)
+        private int NlsGetMacCodePage(string cultureName)
         {
-            return GetLocaleInfo(LocaleNumberData.MacCodePage);
+            Debug.Assert(GlobalizationMode.UseNls);
+            return NlsGetLocaleInfo(LocaleNumberData.MacCodePage);
         }
 
-        private int GetEbcdicCodePage(string cultureName)
+        private int NlsGetEbcdicCodePage(string cultureName)
         {
-            return GetLocaleInfo(LocaleNumberData.EbcdicCodePage);
+            Debug.Assert(GlobalizationMode.UseNls);
+            return NlsGetLocaleInfo(LocaleNumberData.EbcdicCodePage);
         }
 
-        private int GetGeoId(string cultureName)
+        private int NlsGetGeoId(string cultureName)
         {
-            return GetLocaleInfo(LocaleNumberData.GeoId);
+            Debug.Assert(GlobalizationMode.UseNls);
+            return NlsGetLocaleInfo(LocaleNumberData.GeoId);
         }
 
-        private int GetDigitSubstitution(string cultureName)
+        private int NlsGetDigitSubstitution(string cultureName)
         {
-            return GetLocaleInfo(LocaleNumberData.DigitSubstitution);
+            Debug.Assert(GlobalizationMode.UseNls);
+            return NlsGetLocaleInfo(LocaleNumberData.DigitSubstitution);
         }
 
-        private string GetThreeLetterWindowsLanguageName(string cultureName)
+        private string NlsGetThreeLetterWindowsLanguageName(string cultureName)
         {
-            return GetLocaleInfo(cultureName, LocaleStringData.AbbreviatedWindowsLanguageName);
+            Debug.Assert(GlobalizationMode.UseNls);
+            return NlsGetLocaleInfo(cultureName, LocaleStringData.AbbreviatedWindowsLanguageName);
         }
 
-        private static CultureInfo[] EnumCultures(CultureTypes types)
+        private static CultureInfo[] NlsEnumCultures(CultureTypes types)
         {
             Debug.Assert(!GlobalizationMode.Invariant);
+            Debug.Assert(GlobalizationMode.UseNls);
 
             uint flags = 0;
 
@@ -687,17 +710,17 @@ namespace System.Globalization
             return cultures;
         }
 
-        private string GetConsoleFallbackName(string cultureName)
+        private string NlsGetConsoleFallbackName(string cultureName)
         {
-            return GetLocaleInfo(cultureName, LocaleStringData.ConsoleFallbackName);
+            Debug.Assert(GlobalizationMode.UseNls);
+            return NlsGetLocaleInfo(cultureName, LocaleStringData.ConsoleFallbackName);
         }
 
-        internal bool IsWin32Installed => true;
-
-        internal bool IsReplacementCulture
+        internal bool NlsIsReplacementCulture
         {
             get
             {
+                Debug.Assert(GlobalizationMode.UseNls);
                 EnumData context = default;
                 context.strings = new List<string>();
 
@@ -716,8 +739,9 @@ namespace System.Globalization
             }
         }
 
-        internal static unsafe CultureData GetCurrentRegionData()
+        internal static unsafe CultureData NlsGetCurrentRegionData()
         {
+            Debug.Assert(GlobalizationMode.UseNls);
             Span<char> geoIso2Letters = stackalloc char[10];
 
             int geoId = Interop.Kernel32.GetUserGeoID(Interop.Kernel32.GEOCLASS_NATION);
index 70fc788..011befa 100644 (file)
@@ -474,7 +474,7 @@ namespace System.Globalization
             // If not found in the hard coded table we'll have to find a culture that works for us
             if (!GlobalizationMode.Invariant && (retVal == null || retVal.IsNeutralCulture))
             {
-                retVal = GetCultureDataFromRegionName(cultureName);
+                retVal = GlobalizationMode.UseNls ? NlsGetCultureDataFromRegionName(cultureName) : IcuGetCultureDataFromRegionName(cultureName);
             }
 
             // If we found one we can use, then cache it for next time
@@ -540,7 +540,7 @@ namespace System.Globalization
             }
 
 #pragma warning restore 618
-            return EnumCultures(types);
+            return GlobalizationMode.UseNls ? NlsEnumCultures(types) : IcuEnumCultures(types);
         }
 
         private static CultureData CreateCultureWithInvariantData()
@@ -802,7 +802,7 @@ namespace System.Globalization
             culture._sRealName = cultureName;
 
             // Ask native code if that one's real
-            if (!culture.InitCultureData() && !culture.InitCompatibilityCultureData())
+            if (!culture.InitCultureDataCore() && !culture.InitCompatibilityCultureData())
             {
                 return null;
             }
@@ -832,7 +832,7 @@ namespace System.Globalization
             }
 
             _sRealName = fallbackCultureName;
-            if (!InitCultureData())
+            if (!InitCultureDataCore())
             {
                 return false;
             }
@@ -844,6 +844,10 @@ namespace System.Globalization
             return true;
         }
 
+        private bool InitCultureDataCore() => GlobalizationMode.UseNls ?
+                                                NlsInitCultureData() :
+                                                IcuInitCultureData();
+
         /// We'd rather people use the named version since this doesn't allow custom locales
         internal static CultureData GetCultureData(int culture, bool bUseUserOverride)
         {
@@ -863,7 +867,7 @@ namespace System.Globalization
 
             // Convert the lcid to a name, then use that
             // Note that this will return neutral names (unlike Vista native API)
-            localeName = LCIDToLocaleName(culture);
+            localeName = GlobalizationMode.UseNls ? NlsLCIDToLocaleName(culture) : IcuLCIDToLocaleName(culture);
 
             if (!string.IsNullOrEmpty(localeName))
             {
@@ -914,7 +918,7 @@ namespace System.Globalization
 
         // Parent name (which may be a custom locale/culture)
         // Ask using the real name, so that we get parents of neutrals
-        internal string ParentName => _sParent ??= GetLocaleInfo(_sRealName!, LocaleStringData.ParentName);
+        internal string ParentName => _sParent ??= GetLocaleInfoCore(_sRealName!, LocaleStringData.ParentName);
 
         // Localized pretty name for this locale (ie: Inglis (estados Unitos))
         internal string DisplayName
@@ -944,15 +948,15 @@ namespace System.Globalization
 
                             if (Name.Equals(ZH_CHT, StringComparison.OrdinalIgnoreCase))
                             {
-                                localizedDisplayName = GetLanguageDisplayName("zh-Hant");
+                                localizedDisplayName = GetLanguageDisplayNameCore("zh-Hant");
                             }
                             else if (Name.Equals(ZH_CHS, StringComparison.OrdinalIgnoreCase))
                             {
-                                localizedDisplayName = GetLanguageDisplayName("zh-Hans");
+                                localizedDisplayName = GetLanguageDisplayNameCore("zh-Hans");
                             }
                             else
                             {
-                                localizedDisplayName = GetLanguageDisplayName(Name);
+                                localizedDisplayName = GetLanguageDisplayNameCore(Name);
                             }
                         }
                         catch
@@ -983,7 +987,7 @@ namespace System.Globalization
                             }
                             else
                             {
-                                localizedDisplayName = GetLocaleInfo(LocaleStringData.LocalizedDisplayName);
+                                localizedDisplayName = GetLocaleInfoCore(LocaleStringData.LocalizedDisplayName);
                             }
                         }
                     }
@@ -995,6 +999,10 @@ namespace System.Globalization
             }
         }
 
+        private string GetLanguageDisplayNameCore(string cultureName) => GlobalizationMode.UseNls ?
+                                                                            NlsGetLanguageDisplayName(cultureName) :
+                                                                            IcuGetLanguageDisplayName(cultureName);
+
         /// <summary>
         /// English pretty name for this locale (ie: English (United States))
         /// </summary>
@@ -1020,7 +1028,7 @@ namespace System.Globalization
                     }
                     else
                     {
-                        englishDisplayName = GetLocaleInfo(LocaleStringData.EnglishDisplayName);
+                        englishDisplayName = GetLocaleInfoCore(LocaleStringData.EnglishDisplayName);
 
                         // if it isn't found build one:
                         if (string.IsNullOrEmpty(englishDisplayName))
@@ -1079,7 +1087,7 @@ namespace System.Globalization
                     }
                     else
                     {
-                        nativeDisplayName = GetLocaleInfo(LocaleStringData.NativeDisplayName);
+                        nativeDisplayName = GetLocaleInfoCore(LocaleStringData.NativeDisplayName);
 
                         // if it isn't found build one:
                         if (string.IsNullOrEmpty(nativeDisplayName))
@@ -1112,17 +1120,19 @@ namespace System.Globalization
         /// <summary>
         /// iso 639 language name, ie: en
         /// </summary>
-        internal string TwoLetterISOLanguageName => _sISO639Language ??= GetLocaleInfo(LocaleStringData.Iso639LanguageTwoLetterName);
+        internal string TwoLetterISOLanguageName => _sISO639Language ??= GetLocaleInfoCore(LocaleStringData.Iso639LanguageTwoLetterName);
 
         /// <summary>
         /// iso 639 language name, ie: eng
         /// </summary>
-        internal string ThreeLetterISOLanguageName => _sISO639Language2 ??= GetLocaleInfo(LocaleStringData.Iso639LanguageThreeLetterName);
+        internal string ThreeLetterISOLanguageName => _sISO639Language2 ??= GetLocaleInfoCore(LocaleStringData.Iso639LanguageThreeLetterName);
 
         /// <summary>
         /// abbreviated windows language name (ie: enu) (non-standard, avoid this)
         /// </summary>
-        internal string ThreeLetterWindowsLanguageName => _sAbbrevLang ??= GetThreeLetterWindowsLanguageName(_sRealName!);
+        internal string ThreeLetterWindowsLanguageName => _sAbbrevLang ??= GlobalizationMode.UseNls ?
+                                                                            NlsGetThreeLetterWindowsLanguageName(_sRealName!) :
+                                                                            IcuGetThreeLetterWindowsLanguageName(_sRealName!);
 
         /// <summary>
         /// Localized name for this language (Windows Only) ie: Inglis
@@ -1146,7 +1156,7 @@ namespace System.Globalization
                     }
                     else
                     {
-                        _sLocalizedLanguage = GetLocaleInfo(LocaleStringData.LocalizedLanguageName);
+                        _sLocalizedLanguage = GetLocaleInfoCore(LocaleStringData.LocalizedLanguageName);
                     }
                 }
 
@@ -1157,17 +1167,17 @@ namespace System.Globalization
         /// <summary>
         /// English name for this language (Windows Only) ie: German
         /// </summary>
-        private string EnglishLanguageName => _sEnglishLanguage ??= GetLocaleInfo(LocaleStringData.EnglishLanguageName);
+        private string EnglishLanguageName => _sEnglishLanguage ??= GetLocaleInfoCore(LocaleStringData.EnglishLanguageName);
 
         /// <summary>
         /// Native name of this language (Windows Only) ie: Deutsch
         /// </summary>
-        private string NativeLanguageName => _sNativeLanguage ??= GetLocaleInfo(LocaleStringData.NativeLanguageName);
+        private string NativeLanguageName => _sNativeLanguage ??= GetLocaleInfoCore(LocaleStringData.NativeLanguageName);
 
         /// <summary>
         /// region name (eg US)
         /// </summary>
-        internal string RegionName => _sRegionName ??= GetLocaleInfo(LocaleStringData.Iso3166CountryName);
+        internal string RegionName => _sRegionName ??= GetLocaleInfoCore(LocaleStringData.Iso3166CountryName);
 
         internal int GeoId
         {
@@ -1175,7 +1185,7 @@ namespace System.Globalization
             {
                 if (_iGeoId == undef)
                 {
-                    _iGeoId = GetGeoId(_sRealName!);
+                    _iGeoId = GlobalizationMode.UseNls ? NlsGetGeoId(_sRealName!) : IcuGetGeoId(_sRealName!);
                 }
                 return _iGeoId;
             }
@@ -1193,7 +1203,7 @@ namespace System.Globalization
                 {
                     try
                     {
-                        localizedCountry = GetRegionDisplayName();
+                        localizedCountry = GlobalizationMode.UseNls ? NlsGetRegionDisplayName() : IcuGetRegionDisplayName();
                     }
                     catch
                     {
@@ -1211,22 +1221,22 @@ namespace System.Globalization
         /// <summary>
         /// english country name (RegionInfo) ie: Germany
         /// </summary>
-        internal string EnglishCountryName => _sEnglishCountry ??= GetLocaleInfo(LocaleStringData.EnglishCountryName);
+        internal string EnglishCountryName => _sEnglishCountry ??= GetLocaleInfoCore(LocaleStringData.EnglishCountryName);
 
         /// <summary>
         /// native country name (RegionInfo) ie: Deutschland
         /// </summary>
-        internal string NativeCountryName => _sNativeCountry ??= GetLocaleInfo(LocaleStringData.NativeCountryName);
+        internal string NativeCountryName => _sNativeCountry ??= GetLocaleInfoCore(LocaleStringData.NativeCountryName);
 
         /// <summary>
         /// ISO 3166 Country Name
         /// </summary>
-        internal string TwoLetterISOCountryName => _sISO3166CountryName ??= GetLocaleInfo(LocaleStringData.Iso3166CountryName);
+        internal string TwoLetterISOCountryName => _sISO3166CountryName ??= GetLocaleInfoCore(LocaleStringData.Iso3166CountryName);
 
         /// <summary>
         /// 3 letter ISO 3166 country code
         /// </summary>
-        internal string ThreeLetterISOCountryName => _sISO3166CountryName2 ??= GetLocaleInfo(LocaleStringData.Iso3166CountryName2);
+        internal string ThreeLetterISOCountryName => _sISO3166CountryName2 ??= GetLocaleInfoCore(LocaleStringData.Iso3166CountryName2);
 
         internal int KeyboardLayoutId
         {
@@ -1251,27 +1261,29 @@ namespace System.Globalization
         /// <summary>
         /// Console fallback name (ie: locale to use for console apps for unicode-only locales)
         /// </summary>
-        internal string SCONSOLEFALLBACKNAME => _sConsoleFallbackName ??= GetConsoleFallbackName(_sRealName!);
+        internal string SCONSOLEFALLBACKNAME => _sConsoleFallbackName ??= GlobalizationMode.UseNls ?
+                                                                            NlsGetConsoleFallbackName(_sRealName!) :
+                                                                            IcuGetConsoleFallbackName(_sRealName!);
 
         /// <summary>
         /// (user can override) grouping of digits
         /// </summary>
-        internal int[] NumberGroupSizes => _waGrouping ??= GetLocaleInfo(LocaleGroupingData.Digit);
+        internal int[] NumberGroupSizes => _waGrouping ??= GetLocaleInfoCore(LocaleGroupingData.Digit);
 
         /// <summary>
         /// Not a Number
         /// </summary>
-        private string NaNSymbol => _sNaN ??= GetLocaleInfo(LocaleStringData.NaNSymbol);
+        private string NaNSymbol => _sNaN ??= GetLocaleInfoCore(LocaleStringData.NaNSymbol);
 
         /// <summary>
         /// + Infinity
         /// </summary>
-        private string PositiveInfinitySymbol => _sPositiveInfinity ??= GetLocaleInfo(LocaleStringData.PositiveInfinitySymbol);
+        private string PositiveInfinitySymbol => _sPositiveInfinity ??= GetLocaleInfoCore(LocaleStringData.PositiveInfinitySymbol);
 
         /// <summary>
         /// - Infinity
         /// </summary>
-        private string NegativeInfinitySymbol => _sNegativeInfinity ??= GetLocaleInfo(LocaleStringData.NegativeInfinitySymbol);
+        private string NegativeInfinitySymbol => _sNegativeInfinity ??= GetLocaleInfoCore(LocaleStringData.NegativeInfinitySymbol);
 
         /// <summary>
         /// Negative Percent (0-3)
@@ -1283,7 +1295,7 @@ namespace System.Globalization
                 if (_iNegativePercent == undef)
                 {
                     // Note that <= Windows Vista this is synthesized by native code
-                    _iNegativePercent = GetLocaleInfo(LocaleNumberData.NegativePercentFormat);
+                    _iNegativePercent = GetLocaleInfoCore(LocaleNumberData.NegativePercentFormat);
                 }
                 return _iNegativePercent;
             }
@@ -1299,7 +1311,7 @@ namespace System.Globalization
                 if (_iPositivePercent == undef)
                 {
                     // Note that <= Windows Vista this is synthesized by native code
-                    _iPositivePercent = GetLocaleInfo(LocaleNumberData.PositivePercentFormat);
+                    _iPositivePercent = GetLocaleInfoCore(LocaleNumberData.PositivePercentFormat);
                 }
                 return _iPositivePercent;
             }
@@ -1308,37 +1320,37 @@ namespace System.Globalization
         /// <summary>
         /// Percent (%) symbol
         /// </summary>
-        private string PercentSymbol => _sPercent ??= GetLocaleInfo(LocaleStringData.PercentSymbol);
+        private string PercentSymbol => _sPercent ??= GetLocaleInfoCore(LocaleStringData.PercentSymbol);
 
         /// <summary>
         /// PerMille symbol
         /// </summary>
-        private string PerMilleSymbol => _sPerMille ??= GetLocaleInfo(LocaleStringData.PerMilleSymbol);
+        private string PerMilleSymbol => _sPerMille ??= GetLocaleInfoCore(LocaleStringData.PerMilleSymbol);
 
         /// <summary>
         /// (user can override) local monetary symbol, eg: $
         /// </summary>
-        internal string CurrencySymbol => _sCurrency ??= GetLocaleInfo(LocaleStringData.MonetarySymbol);
+        internal string CurrencySymbol => _sCurrency ??= GetLocaleInfoCore(LocaleStringData.MonetarySymbol);
 
         /// <summary>
         /// international monetary symbol (RegionInfo), eg: USD
         /// </summary>
-        internal string ISOCurrencySymbol => _sIntlMonetarySymbol ??= GetLocaleInfo(LocaleStringData.Iso4217MonetarySymbol);
+        internal string ISOCurrencySymbol => _sIntlMonetarySymbol ??= GetLocaleInfoCore(LocaleStringData.Iso4217MonetarySymbol);
 
         /// <summary>
         /// English name for this currency (RegionInfo), eg: US Dollar
         /// </summary>
-        internal string CurrencyEnglishName => _sEnglishCurrency ??= GetLocaleInfo(LocaleStringData.CurrencyEnglishName);
+        internal string CurrencyEnglishName => _sEnglishCurrency ??= GetLocaleInfoCore(LocaleStringData.CurrencyEnglishName);
 
         /// <summary>
         /// Native name for this currency (RegionInfo), eg: Schweiz Frank
         /// </summary>
-        internal string CurrencyNativeName => _sNativeCurrency ??= GetLocaleInfo(LocaleStringData.CurrencyNativeName);
+        internal string CurrencyNativeName => _sNativeCurrency ??= GetLocaleInfoCore(LocaleStringData.CurrencyNativeName);
 
         /// <summary>
         /// (user can override) monetary grouping of digits
         /// </summary>
-        internal int[] CurrencyGroupSizes => _waMonetaryGrouping ??= GetLocaleInfo(LocaleGroupingData.Monetary);
+        internal int[] CurrencyGroupSizes => _waMonetaryGrouping ??= GetLocaleInfoCore(LocaleGroupingData.Monetary);
 
         /// <summary>
         /// (user can override) system of measurement 0=metric, 1=US (RegionInfo)
@@ -1349,7 +1361,7 @@ namespace System.Globalization
             {
                 if (_iMeasure == undef)
                 {
-                    _iMeasure = GetLocaleInfo(LocaleNumberData.MeasurementSystem);
+                    _iMeasure = GetLocaleInfoCore(LocaleNumberData.MeasurementSystem);
                 }
                 return _iMeasure;
             }
@@ -1358,17 +1370,17 @@ namespace System.Globalization
         /// <summary>
         /// (user can override) list Separator
         /// </summary>
-        internal string ListSeparator => _sListSeparator ??= GetLocaleInfo(LocaleStringData.ListSeparator);
+        internal string ListSeparator => _sListSeparator ??= GetLocaleInfoCore(LocaleStringData.ListSeparator);
 
         /// <summary>
         /// (user can override) AM designator
         /// </summary>
-        internal string AMDesignator => _sAM1159 ??= GetLocaleInfo(LocaleStringData.AMDesignator);
+        internal string AMDesignator => _sAM1159 ??= GetLocaleInfoCore(LocaleStringData.AMDesignator);
 
         /// <summary>
         /// (user can override) PM designator
         /// </summary>
-        internal string PMDesignator => _sPM2359 ??= GetLocaleInfo(LocaleStringData.PMDesignator);
+        internal string PMDesignator => _sPM2359 ??= GetLocaleInfoCore(LocaleStringData.PMDesignator);
 
         /// <summary>
         /// (user can override) time format
@@ -1381,7 +1393,7 @@ namespace System.Globalization
                 {
                     Debug.Assert(!GlobalizationMode.Invariant);
 
-                    string[]? longTimes = GetTimeFormats();
+                    string[]? longTimes = GlobalizationMode.UseNls ? NlsGetTimeFormats() : IcuGetTimeFormats();
                     if (longTimes == null || longTimes.Length == 0)
                     {
                         _saLongTimes = Invariant._saLongTimes!;
@@ -1408,7 +1420,7 @@ namespace System.Globalization
                     Debug.Assert(!GlobalizationMode.Invariant);
 
                     // Try to get the short times from the OS/culture.dll
-                    string[]? shortTimes = GetShortTimeFormats();
+                    string[]? shortTimes = GlobalizationMode.UseNls ? NlsGetShortTimeFormats() : IcuGetShortTimeFormats();
 
                     if (shortTimes == null || shortTimes.Length == 0)
                     {
@@ -1559,7 +1571,7 @@ namespace System.Globalization
             {
                 if (_iFirstDayOfWeek == undef)
                 {
-                    _iFirstDayOfWeek = GetFirstDayOfWeek();
+                    _iFirstDayOfWeek = GlobalizationMode.UseNls ? NlsGetFirstDayOfWeek() : IcuGetFirstDayOfWeek();
                 }
                 return _iFirstDayOfWeek;
             }
@@ -1572,7 +1584,7 @@ namespace System.Globalization
             {
                 if (_iFirstWeekOfYear == undef)
                 {
-                    _iFirstWeekOfYear = GetLocaleInfo(LocaleNumberData.FirstWeekOfYear);
+                    _iFirstWeekOfYear = GetLocaleInfoCore(LocaleNumberData.FirstWeekOfYear);
                 }
                 return _iFirstWeekOfYear;
             }
@@ -1665,7 +1677,10 @@ namespace System.Globalization
                     // Default calendar should be first
                     CalendarId[] calendars = new CalendarId[23];
                     Debug.Assert(_sWindowsName != null, "[CultureData.CalendarIds] Expected _sWindowsName to be populated by already");
-                    int count = CalendarData.GetCalendars(_sWindowsName, _bUseOverrides, calendars);
+
+                    int count = GlobalizationMode.UseNls
+                        ? CalendarData.NlsGetCalendars(_sWindowsName, _bUseOverrides, calendars)
+                        : CalendarData.IcuGetCalendars(_sWindowsName, _bUseOverrides, calendars);
 
                     // See if we had a calendar to add.
                     if (count == 0)
@@ -1711,7 +1726,7 @@ namespace System.Globalization
                         // Prior to Vista the enumeration didn't have default calendar first
                         if (temp.Length > 1)
                         {
-                            CalendarId i = (CalendarId)GetLocaleInfo(LocaleNumberData.CalendarType);
+                            CalendarId i = (CalendarId)GetLocaleInfoCore(LocaleNumberData.CalendarType);
                             if (temp[1] == i)
                             {
                                 temp[1] = temp[0];
@@ -1784,7 +1799,7 @@ namespace System.Globalization
                 if (_iReadingLayout == undef)
                 {
                     Debug.Assert(_sRealName != null, "[CultureData.IsRightToLeft] Expected _sRealName to be populated by already");
-                    _iReadingLayout = GetLocaleInfo(LocaleNumberData.ReadingLayout);
+                    _iReadingLayout = GetLocaleInfoCore(LocaleNumberData.ReadingLayout);
                 }
 
                 return _iReadingLayout;
@@ -1835,7 +1850,7 @@ namespace System.Globalization
             {
                 if (_iDefaultAnsiCodePage == undef)
                 {
-                    _iDefaultAnsiCodePage = GetAnsiCodePage(_sRealName!);
+                    _iDefaultAnsiCodePage = GlobalizationMode.UseNls ? NlsGetAnsiCodePage(_sRealName!) : IcuGetAnsiCodePage(_sRealName!);
                 }
                 return _iDefaultAnsiCodePage;
             }
@@ -1850,7 +1865,7 @@ namespace System.Globalization
             {
                 if (_iDefaultOemCodePage == undef)
                 {
-                    _iDefaultOemCodePage = GetOemCodePage(_sRealName!);
+                    _iDefaultOemCodePage = GlobalizationMode.UseNls ? NlsGetOemCodePage(_sRealName!) : IcuGetOemCodePage(_sRealName!);
                 }
                 return _iDefaultOemCodePage;
             }
@@ -1865,7 +1880,7 @@ namespace System.Globalization
             {
                 if (_iDefaultMacCodePage == undef)
                 {
-                    _iDefaultMacCodePage = GetMacCodePage(_sRealName!);
+                    _iDefaultMacCodePage = GlobalizationMode.UseNls ? NlsGetMacCodePage(_sRealName!) : IcuGetMacCodePage(_sRealName!);
                 }
                 return _iDefaultMacCodePage;
             }
@@ -1880,7 +1895,7 @@ namespace System.Globalization
             {
                 if (_iDefaultEbcdicCodePage == undef)
                 {
-                    _iDefaultEbcdicCodePage = GetEbcdicCodePage(_sRealName!);
+                    _iDefaultEbcdicCodePage = GlobalizationMode.UseNls ? NlsGetEbcdicCodePage(_sRealName!) : IcuGetEbcdicCodePage(_sRealName!);
                 }
                 return _iDefaultEbcdicCodePage;
             }
@@ -1893,7 +1908,7 @@ namespace System.Globalization
                 if (_iLanguage == 0)
                 {
                     Debug.Assert(_sRealName != null, "[CultureData.LCID] Expected this.sRealName to be populated already");
-                    _iLanguage = LocaleNameToLCID(_sRealName);
+                    _iLanguage = GlobalizationMode.UseNls ? NlsLocaleNameToLCID(_sRealName) : IcuLocaleNameToLCID(_sRealName);
                 }
                 return _iLanguage;
             }
@@ -1905,6 +1920,14 @@ namespace System.Globalization
 
         internal bool IsInvariantCulture => string.IsNullOrEmpty(Name);
 
+        internal bool IsWin32Installed => GlobalizationMode.UseNls;
+
+        internal bool IsReplacementCulture => GlobalizationMode.UseNls ? NlsIsReplacementCulture : false;
+
+        internal static unsafe CultureData GetCurrentRegionData() => GlobalizationMode.UseNls ?
+                                                                        NlsGetCurrentRegionData() :
+                                                                        CultureInfo.CurrentCulture._cultureData;
+
         /// <summary>
         /// Get an instance of our default calendar
         /// </summary>
@@ -1917,7 +1940,7 @@ namespace System.Globalization
                     return CultureInfo.GetCalendarInstance(CalendarIds[0]);
                 }
 
-                CalendarId defaultCalId = (CalendarId)GetLocaleInfo(LocaleNumberData.CalendarType);
+                CalendarId defaultCalId = (CalendarId)GetLocaleInfoCore(LocaleNumberData.CalendarType);
 
                 if (defaultCalId == 0)
                 {
@@ -1958,7 +1981,7 @@ namespace System.Globalization
             {
                 if (_sTimeSeparator == null)
                 {
-                    string? longTimeFormat = GetTimeFormatString();
+                    string? longTimeFormat = GlobalizationMode.UseNls ? NlsGetTimeFormatString() : IcuGetTimeFormatString();
                     if (string.IsNullOrEmpty(longTimeFormat))
                     {
                         longTimeFormat = LongTimes[0];
@@ -2160,24 +2183,24 @@ namespace System.Globalization
             {
                 Debug.Assert(_sWindowsName != null, "[CultureData.GetNFIValues] Expected _sWindowsName to be populated by already");
                 // String values
-                nfi._positiveSign = GetLocaleInfo(LocaleStringData.PositiveSign);
-                nfi._negativeSign = GetLocaleInfo(LocaleStringData.NegativeSign);
+                nfi._positiveSign = GetLocaleInfoCore(LocaleStringData.PositiveSign);
+                nfi._negativeSign = GetLocaleInfoCore(LocaleStringData.NegativeSign);
 
-                nfi._numberDecimalSeparator = GetLocaleInfo(LocaleStringData.DecimalSeparator);
-                nfi._numberGroupSeparator = GetLocaleInfo(LocaleStringData.ThousandSeparator);
-                nfi._currencyGroupSeparator = GetLocaleInfo(LocaleStringData.MonetaryThousandSeparator);
-                nfi._currencyDecimalSeparator = GetLocaleInfo(LocaleStringData.MonetaryDecimalSeparator);
-                nfi._currencySymbol = GetLocaleInfo(LocaleStringData.MonetarySymbol);
+                nfi._numberDecimalSeparator = GetLocaleInfoCore(LocaleStringData.DecimalSeparator);
+                nfi._numberGroupSeparator = GetLocaleInfoCore(LocaleStringData.ThousandSeparator);
+                nfi._currencyGroupSeparator = GetLocaleInfoCore(LocaleStringData.MonetaryThousandSeparator);
+                nfi._currencyDecimalSeparator = GetLocaleInfoCore(LocaleStringData.MonetaryDecimalSeparator);
+                nfi._currencySymbol = GetLocaleInfoCore(LocaleStringData.MonetarySymbol);
 
                 // Numeric values
-                nfi._numberDecimalDigits = GetLocaleInfo(LocaleNumberData.FractionalDigitsCount);
-                nfi._currencyDecimalDigits = GetLocaleInfo(LocaleNumberData.MonetaryFractionalDigitsCount);
-                nfi._currencyPositivePattern = GetLocaleInfo(LocaleNumberData.PositiveMonetaryNumberFormat);
-                nfi._currencyNegativePattern = GetLocaleInfo(LocaleNumberData.NegativeMonetaryNumberFormat);
-                nfi._numberNegativePattern = GetLocaleInfo(LocaleNumberData.NegativeNumberFormat);
+                nfi._numberDecimalDigits = GetLocaleInfoCore(LocaleNumberData.FractionalDigitsCount);
+                nfi._currencyDecimalDigits = GetLocaleInfoCore(LocaleNumberData.MonetaryFractionalDigitsCount);
+                nfi._currencyPositivePattern = GetLocaleInfoCore(LocaleNumberData.PositiveMonetaryNumberFormat);
+                nfi._currencyNegativePattern = GetLocaleInfoCore(LocaleNumberData.NegativeMonetaryNumberFormat);
+                nfi._numberNegativePattern = GetLocaleInfoCore(LocaleNumberData.NegativeNumberFormat);
 
                 // LOCALE_SNATIVEDIGITS (array of 10 single character strings).
-                string digits = GetLocaleInfo(LocaleStringData.Digits);
+                string digits = GetLocaleInfoCore(LocaleStringData.Digits);
                 nfi._nativeDigits = new string[10];
                 for (int i = 0; i < nfi._nativeDigits.Length; i++)
                 {
@@ -2185,7 +2208,7 @@ namespace System.Globalization
                 }
 
                 Debug.Assert(_sRealName != null);
-                nfi._digitSubstitution = GetDigitSubstitution(_sRealName);
+                nfi._digitSubstitution = GlobalizationMode.UseNls ? NlsGetDigitSubstitution(_sRealName) : IcuGetDigitSubstitution(_sRealName);
             }
 
             // Gather additional data
@@ -2230,6 +2253,22 @@ namespace System.Globalization
         /// </remarks>
         internal static string AnsiToLower(string testString) => TextInfo.ToLowerAsciiInvariant(testString);
 
+        private int GetLocaleInfoCore(LocaleNumberData type) => GlobalizationMode.UseNls ?
+                                                                    NlsGetLocaleInfo(type) :
+                                                                    IcuGetLocaleInfo(type);
+
+        private string GetLocaleInfoCore(LocaleStringData type) => GlobalizationMode.UseNls ?
+                                                                    NlsGetLocaleInfo(type) :
+                                                                    IcuGetLocaleInfo(type);
+
+        private string GetLocaleInfoCore(string localeName, LocaleStringData type) => GlobalizationMode.UseNls ?
+                                                                                        NlsGetLocaleInfo(localeName, type) :
+                                                                                        IcuGetLocaleInfo(localeName, type);
+
+        private int[] GetLocaleInfoCore(LocaleGroupingData type) => GlobalizationMode.UseNls ?
+                                                                       NlsGetLocaleInfo(type) :
+                                                                       IcuGetLocaleInfo(type);
+
         /// <remarks>
         /// The numeric values of the enum members match their Win32 counterparts.  The CultureData Win32 PAL implementation
         /// takes a dependency on this fact, in order to prevent having to construct a mapping from internal values to LCTypes.
@@ -8,8 +8,10 @@ namespace System.Globalization
 {
     public partial class CultureInfo : IFormatProvider
     {
-        internal static CultureInfo GetUserDefaultCulture()
+        internal static CultureInfo IcuGetUserDefaultCulture()
         {
+            Debug.Assert(!GlobalizationMode.UseNls);
+
             if (GlobalizationMode.Invariant)
                 return CultureInfo.InvariantCulture;
 
@@ -28,8 +30,10 @@ namespace System.Globalization
             return cultureInfo;
         }
 
-        private static CultureInfo GetPredefinedCultureInfo(string name)
+        private static CultureInfo IcuGetPredefinedCultureInfo(string name)
         {
+            Debug.Assert(!GlobalizationMode.UseNls);
+
             if (!Interop.Globalization.IsPredefinedLocale(name))
             {
                 throw new CultureNotFoundException(nameof(name), SR.Format(SR.Argument_InvalidPredefinedCultureName, name));
@@ -38,8 +42,10 @@ namespace System.Globalization
             return GetCultureInfo(name);
         }
 
-        private static CultureInfo GetUserDefaultUICulture()
+        private static CultureInfo IcuGetUserDefaultUICulture()
         {
+            Debug.Assert(!GlobalizationMode.UseNls);
+
             return InitializeUserDefaultCulture();
         }
     }
@@ -2,12 +2,16 @@
 // The .NET Foundation licenses this file to you under the MIT license.
 // See the LICENSE file in the project root for more information.
 
+using System.Diagnostics;
+
 namespace System.Globalization
 {
     public partial class CultureInfo : IFormatProvider
     {
-        internal static CultureInfo GetUserDefaultCulture()
+        internal static CultureInfo NlsGetUserDefaultCulture()
         {
+            Debug.Assert(GlobalizationMode.UseNls);
+
             if (GlobalizationMode.Invariant)
                 return CultureInfo.InvariantCulture;
 
@@ -26,8 +30,10 @@ namespace System.Globalization
             return GetCultureByName(strDefault);
         }
 
-        private static CultureInfo GetPredefinedCultureInfo(string name)
+        private static CultureInfo NlsGetPredefinedCultureInfo(string name)
         {
+            Debug.Assert(GlobalizationMode.UseNls);
+
             CultureInfo culture = GetCultureInfo(name);
             string englishName = culture.EnglishName;
 
@@ -44,8 +50,10 @@ namespace System.Globalization
             return culture;
         }
 
-        private static unsafe CultureInfo GetUserDefaultUICulture()
+        private static unsafe CultureInfo NlsGetUserDefaultUICulture()
         {
+            Debug.Assert(GlobalizationMode.UseNls);
+
             if (GlobalizationMode.Invariant)
                 return CultureInfo.InvariantCulture;
 
index e853316..f4daf5c 100644 (file)
@@ -819,6 +819,14 @@ namespace System.Globalization
             return new GregorianCalendar();
         }
 
+        internal static CultureInfo GetUserDefaultCulture() => GlobalizationMode.UseNls ?
+                                                                   NlsGetUserDefaultCulture() :
+                                                                   IcuGetUserDefaultCulture();
+
+        private static CultureInfo GetUserDefaultUICulture() => GlobalizationMode.UseNls ?
+                                                                    NlsGetUserDefaultUICulture() :
+                                                                    IcuGetUserDefaultUICulture();
+
         /// <summary>
         /// Return/set the default calendar used by this culture.
         /// This value can be overridden by regional option if this is a current culture.
@@ -1123,7 +1131,9 @@ namespace System.Globalization
 
             if (predefinedOnly)
             {
-                return GetPredefinedCultureInfo(name);
+                return GlobalizationMode.UseNls ?
+                    NlsGetPredefinedCultureInfo(name) :
+                    IcuGetPredefinedCultureInfo(name);
             }
 
             return GetCultureInfo(name);
@@ -10,7 +10,7 @@ using System.Diagnostics;
 
 namespace System.Globalization
 {
-    internal enum LocaleDataParts
+    internal enum IcuLocaleDataParts
     {
         Lcid = 0,
         AnsiCodePage = 1,
@@ -23,7 +23,7 @@ namespace System.Globalization
         ConsoleLocaleIndex = 8
     }
 
-    internal static class LocaleData
+    internal static class IcuLocaleData
     {
         // this is done rather than using a large readonly array of strings to avoid
         // generating a large amount of code in the static constructor.
@@ -4454,7 +4454,7 @@ namespace System.Globalization
             return null;
         }
 
-        internal static int GetLocaleDataNumericPart(string cultureName, LocaleDataParts part)
+        internal static int GetLocaleDataNumericPart(string cultureName, IcuLocaleDataParts part)
         {
             int index = SearchCultureName(cultureName);
             if (index < 0)
@@ -4480,7 +4480,7 @@ namespace System.Globalization
             return c_threeLetterWindowsLanguageName.Substring(index * 3, 3);
         }
 
-        internal static string GetLocaleDataMappedCulture(string cultureName, LocaleDataParts part)
+        internal static string GetLocaleDataMappedCulture(string cultureName, IcuLocaleDataParts part)
         {
             int indexToIndicesTable = GetLocaleDataNumericPart(cultureName, part);
             if (indexToIndicesTable < 0)
@@ -4496,12 +4496,12 @@ namespace System.Globalization
 
         internal static string GetSpecificCultureName(string cultureName)
         {
-            return GetLocaleDataMappedCulture(cultureName, LocaleDataParts.SpecificLocaleIndex);
+            return GetLocaleDataMappedCulture(cultureName, IcuLocaleDataParts.SpecificLocaleIndex);
         }
 
         internal static string GetConsoleUICulture(string cultureName)
         {
-            return GetLocaleDataMappedCulture(cultureName, LocaleDataParts.ConsoleLocaleIndex);
+            return GetLocaleDataMappedCulture(cultureName, IcuLocaleDataParts.ConsoleLocaleIndex);
         }
 
         // SearchCultureName will binary search c_localeNames using s_localeNamesIndices.
@@ -8,12 +8,13 @@ namespace System.Globalization
 {
     public sealed partial class IdnMapping
     {
-        private unsafe string GetAsciiCore(string unicodeString, char* unicode, int count)
+        private unsafe string IcuGetAsciiCore(string unicodeString, char* unicode, int count)
         {
             Debug.Assert(!GlobalizationMode.Invariant);
+            Debug.Assert(!GlobalizationMode.UseNls);
             Debug.Assert(unicodeString != null && unicodeString.Length >= count);
 
-            uint flags = Flags;
+            uint flags = IcuFlags;
             CheckInvalidIdnCharacters(unicode, count, flags, nameof(unicode));
 
             const int StackallocThreshold = 512;
@@ -51,33 +52,35 @@ namespace System.Globalization
             }
         }
 
-        private unsafe string GetUnicodeCore(string asciiString, char* ascii, int count)
+        private unsafe string IcuGetUnicodeCore(string asciiString, char* ascii, int count)
         {
             Debug.Assert(!GlobalizationMode.Invariant);
+            Debug.Assert(!GlobalizationMode.UseNls);
             Debug.Assert(asciiString != null && asciiString.Length >= count);
 
-            uint flags = Flags;
+            uint flags = IcuFlags;
             CheckInvalidIdnCharacters(ascii, count, flags, nameof(ascii));
 
             const int StackAllocThreshold = 512;
             if (count < StackAllocThreshold)
             {
                 char* output = stackalloc char[count];
-                return GetUnicodeCore(asciiString, ascii, count, flags, output, count, reattempt: true);
+                return IcuGetUnicodeCore(asciiString, ascii, count, flags, output, count, reattempt: true);
             }
             else
             {
                 char[] output = new char[count];
                 fixed (char* pOutput = &output[0])
                 {
-                    return GetUnicodeCore(asciiString, ascii, count, flags, pOutput, count, reattempt: true);
+                    return IcuGetUnicodeCore(asciiString, ascii, count, flags, pOutput, count, reattempt: true);
                 }
             }
         }
 
-        private unsafe string GetUnicodeCore(string asciiString, char* ascii, int count, uint flags, char* output, int outputLength, bool reattempt)
+        private unsafe string IcuGetUnicodeCore(string asciiString, char* ascii, int count, uint flags, char* output, int outputLength, bool reattempt)
         {
             Debug.Assert(!GlobalizationMode.Invariant);
+            Debug.Assert(!GlobalizationMode.UseNls);
             Debug.Assert(asciiString != null && asciiString.Length >= count);
 
             int realLen = Interop.Globalization.ToUnicode(flags, ascii, count, output, outputLength);
@@ -95,7 +98,7 @@ namespace System.Globalization
                 char[] newOutput = new char[realLen];
                 fixed (char* pNewOutput = newOutput)
                 {
-                    return GetUnicodeCore(asciiString, ascii, count, flags, pNewOutput, realLen, reattempt: false);
+                    return IcuGetUnicodeCore(asciiString, ascii, count, flags, pNewOutput, realLen, reattempt: false);
                 }
             }
 
@@ -106,7 +109,7 @@ namespace System.Globalization
         // ---- PAL layer ends here ----
         // -----------------------------
 
-        private uint Flags
+        private uint IcuFlags
         {
             get
             {
@@ -10,12 +10,13 @@ namespace System.Globalization
 {
     public sealed partial class IdnMapping
     {
-        private unsafe string GetAsciiCore(string unicodeString, char* unicode, int count)
+        private unsafe string NlsGetAsciiCore(string unicodeString, char* unicode, int count)
         {
             Debug.Assert(!GlobalizationMode.Invariant);
+            Debug.Assert(GlobalizationMode.UseNls);
             Debug.Assert(unicodeString != null && unicodeString.Length >= count);
 
-            uint flags = Flags;
+            uint flags = NlsFlags;
 
             // Determine the required length
             int length = Interop.Normaliz.IdnToAscii(flags, unicode, count, null, 0);
@@ -29,21 +30,22 @@ namespace System.Globalization
             if (length < StackAllocThreshold)
             {
                 char* output = stackalloc char[length];
-                return GetAsciiCore(unicodeString, unicode, count, flags, output, length);
+                return NlsGetAsciiCore(unicodeString, unicode, count, flags, output, length);
             }
             else
             {
                 char[] output = new char[length];
                 fixed (char* pOutput = &output[0])
                 {
-                    return GetAsciiCore(unicodeString, unicode, count, flags, pOutput, length);
+                    return NlsGetAsciiCore(unicodeString, unicode, count, flags, pOutput, length);
                 }
             }
         }
 
-        private unsafe string GetAsciiCore(string unicodeString, char* unicode, int count, uint flags, char* output, int outputLength)
+        private unsafe string NlsGetAsciiCore(string unicodeString, char* unicode, int count, uint flags, char* output, int outputLength)
         {
             Debug.Assert(!GlobalizationMode.Invariant);
+            Debug.Assert(GlobalizationMode.UseNls);
             Debug.Assert(unicodeString != null && unicodeString.Length >= count);
 
             int length = Interop.Normaliz.IdnToAscii(flags, unicode, count, output, outputLength);
@@ -55,12 +57,13 @@ namespace System.Globalization
             return GetStringForOutput(unicodeString, unicode, count, output, length);
         }
 
-        private unsafe string GetUnicodeCore(string asciiString, char* ascii, int count)
+        private unsafe string NlsGetUnicodeCore(string asciiString, char* ascii, int count)
         {
             Debug.Assert(!GlobalizationMode.Invariant);
+            Debug.Assert(GlobalizationMode.UseNls);
             Debug.Assert(asciiString != null && asciiString.Length >= count);
 
-            uint flags = Flags;
+            uint flags = NlsFlags;
 
             // Determine the required length
             int length = Interop.Normaliz.IdnToUnicode(flags, ascii, count, null, 0);
@@ -74,21 +77,22 @@ namespace System.Globalization
             if (length < StackAllocThreshold)
             {
                 char* output = stackalloc char[length];
-                return GetUnicodeCore(asciiString, ascii, count, flags, output, length);
+                return NlsGetUnicodeCore(asciiString, ascii, count, flags, output, length);
             }
             else
             {
                 char[] output = new char[length];
                 fixed (char* pOutput = &output[0])
                 {
-                    return GetUnicodeCore(asciiString, ascii, count, flags, pOutput, length);
+                    return NlsGetUnicodeCore(asciiString, ascii, count, flags, pOutput, length);
                 }
             }
         }
 
-        private unsafe string GetUnicodeCore(string asciiString, char* ascii, int count, uint flags, char* output, int outputLength)
+        private unsafe string NlsGetUnicodeCore(string asciiString, char* ascii, int count, uint flags, char* output, int outputLength)
         {
             Debug.Assert(!GlobalizationMode.Invariant);
+            Debug.Assert(GlobalizationMode.UseNls);
             Debug.Assert(asciiString != null && asciiString.Length >= count);
 
             int length = Interop.Normaliz.IdnToUnicode(flags, ascii, count, output, outputLength);
@@ -104,7 +108,7 @@ namespace System.Globalization
         // ---- PAL layer ends here ----
         // -----------------------------
 
-        private uint Flags
+        private uint NlsFlags
         {
             get
             {
index 3ec32b2..f309a42 100644 (file)
@@ -92,7 +92,9 @@ namespace System.Globalization
             {
                 fixed (char* pUnicode = unicode)
                 {
-                    return GetAsciiCore(unicode, pUnicode + index, count);
+                    return GlobalizationMode.UseNls ?
+                        NlsGetAsciiCore(unicode, pUnicode + index, count) :
+                        IcuGetAsciiCore(unicode, pUnicode + index, count);
                 }
             }
         }
@@ -134,7 +136,9 @@ namespace System.Globalization
             {
                 fixed (char* pAscii = ascii)
                 {
-                    return GetUnicodeCore(ascii, pAscii + index, count);
+                    return GlobalizationMode.UseNls ?
+                        NlsGetUnicodeCore(ascii, pAscii + index, count) :
+                        IcuGetUnicodeCore(ascii, pAscii + index, count);
                 }
             }
         }
@@ -9,13 +9,15 @@ namespace System.Globalization
 {
     public partial class JapaneseCalendar : Calendar
     {
-        private static EraInfo[]? GetJapaneseEras()
+        private static EraInfo[]? IcuGetJapaneseEras()
         {
             if (GlobalizationMode.Invariant)
             {
                 return null;
             }
 
+            Debug.Assert(!GlobalizationMode.UseNls);
+
             string[]? eraNames;
             if (!CalendarData.EnumCalendarInfo("ja-JP", CalendarId.JAPAN, CalendarDataType.EraNames, out eraNames))
             {
@@ -2,12 +2,17 @@
 // The .NET Foundation licenses this file to you under the MIT license.
 // See the LICENSE file in the project root for more information.
 
+using System.Diagnostics;
+
+#if TARGET_WINDOWS
 using Internal.Win32;
+#endif
 
 namespace System.Globalization
 {
     public partial class JapaneseCalendar : Calendar
     {
+#if TARGET_WINDOWS
         private const string JapaneseErasHive = @"System\CurrentControlSet\Control\Nls\Calendars\Japanese\Eras";
 
         // We know about 4 built-in eras, however users may add additional era(s) from the
@@ -25,8 +30,10 @@ namespace System.Globalization
         // . is a delimiter, but the value of . doesn't matter.
         // '_' marks the space between the japanese era name, japanese abbreviated era name
         //     english name, and abbreviated english names.
-        private static EraInfo[]? GetJapaneseEras()
+        private static EraInfo[]? NlsGetJapaneseEras()
         {
+            Debug.Assert(GlobalizationMode.UseNls);
+
             // Look in the registry key and see if we can find any ranges
             int iFoundEras = 0;
             EraInfo[]? registryEraRanges = null;
@@ -114,6 +121,17 @@ namespace System.Globalization
             // Return our ranges
             return registryEraRanges;
         }
+#else
+        // no-op, in Unix we never call this function.
+        // the reason to have it is to simplify the build
+        // this way we avoid having to include RegistryKey
+        // and all it's windows PInvokes.
+        private static EraInfo[]? NlsGetJapaneseEras()
+        {
+            Debug.Fail("Should never be called non-Windows platforms.");
+            throw new PlatformNotSupportedException();
+        }
+#endif
 
         //
         // Compare two era ranges, eg just the ticks
index 02b15af..2ed5af5 100644 (file)
@@ -69,7 +69,7 @@ namespace System.Globalization
         {
             // See if we need to build it
             return s_japaneseEraInfo ??
-                (s_japaneseEraInfo = GetJapaneseEras()) ??
+                (s_japaneseEraInfo = GlobalizationMode.UseNls ? NlsGetJapaneseEras() : IcuGetJapaneseEras()) ??
                 // See if we have to use the built-in eras
                 (s_japaneseEraInfo = new EraInfo[]
                 {
@@ -11,14 +11,10 @@ namespace System.Globalization
 {
     internal static partial class Normalization
     {
-        internal static unsafe bool IsNormalized(string strInput, NormalizationForm normalizationForm)
+        private static unsafe bool IcuIsNormalized(string strInput, NormalizationForm normalizationForm)
         {
-            if (GlobalizationMode.Invariant)
-            {
-                // In Invariant mode we assume all characters are normalized.
-                // This is because we don't support any linguistic operation on the strings
-                return true;
-            }
+            Debug.Assert(!GlobalizationMode.Invariant);
+            Debug.Assert(!GlobalizationMode.UseNls);
 
             ValidateArguments(strInput, normalizationForm);
 
@@ -36,14 +32,10 @@ namespace System.Globalization
             return ret == 1;
         }
 
-        internal static unsafe string Normalize(string strInput, NormalizationForm normalizationForm)
+        private static unsafe string IcuNormalize(string strInput, NormalizationForm normalizationForm)
         {
-            if (GlobalizationMode.Invariant)
-            {
-                // In Invariant mode we assume all characters are normalized.
-                // This is because we don't support any linguistic operation on the strings
-                return strInput;
-            }
+            Debug.Assert(!GlobalizationMode.Invariant);
+            Debug.Assert(!GlobalizationMode.UseNls);
 
             ValidateArguments(strInput, normalizationForm);
 
@@ -11,15 +11,10 @@ namespace System.Globalization
 {
     internal static partial class Normalization
     {
-        internal static unsafe bool IsNormalized(string strInput, NormalizationForm normalizationForm)
+        private static unsafe bool NlsIsNormalized(string strInput, NormalizationForm normalizationForm)
         {
-            if (GlobalizationMode.Invariant)
-            {
-                // In Invariant mode we assume all characters are normalized.
-                // This is because we don't support any linguistic operation on the strings
-                return true;
-            }
-
+            Debug.Assert(!GlobalizationMode.Invariant);
+            Debug.Assert(GlobalizationMode.UseNls);
             Debug.Assert(strInput != null);
 
             // The only way to know if IsNormalizedString failed is through checking the Win32 last error
@@ -59,15 +54,10 @@ namespace System.Globalization
             return result != Interop.BOOL.FALSE;
         }
 
-        internal static unsafe string Normalize(string strInput, NormalizationForm normalizationForm)
+        private static unsafe string NlsNormalize(string strInput, NormalizationForm normalizationForm)
         {
-            if (GlobalizationMode.Invariant)
-            {
-                // In Invariant mode we assume all characters are normalized.
-                // This is because we don't support any linguistic operation on the strings
-                return strInput;
-            }
-
+            Debug.Assert(!GlobalizationMode.Invariant);
+            Debug.Assert(GlobalizationMode.UseNls);
             Debug.Assert(strInput != null);
 
             if (strInput.Length == 0)
diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/Normalization.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/Normalization.cs
new file mode 100644 (file)
index 0000000..d71b338
--- /dev/null
@@ -0,0 +1,40 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Diagnostics;
+using System.Text;
+
+namespace System.Globalization
+{
+    internal static partial class Normalization
+    {
+        internal static bool IsNormalized(string strInput, NormalizationForm normalizationForm)
+        {
+            if (GlobalizationMode.Invariant)
+            {
+                // In Invariant mode we assume all characters are normalized.
+                // This is because we don't support any linguistic operation on the strings
+                return true;
+            }
+
+            return GlobalizationMode.UseNls ?
+                NlsIsNormalized(strInput, normalizationForm) :
+                IcuIsNormalized(strInput, normalizationForm);
+        }
+
+        internal static string Normalize(string strInput, NormalizationForm normalizationForm)
+        {
+            if (GlobalizationMode.Invariant)
+            {
+                // In Invariant mode we assume all characters are normalized.
+                // This is because we don't support any linguistic operation on the strings
+                return strInput;
+            }
+
+            return GlobalizationMode.UseNls ?
+                NlsNormalize(strInput, normalizationForm) :
+                IcuNormalize(strInput, normalizationForm);
+        }
+    }
+}
@@ -10,8 +10,6 @@ namespace System.Globalization
     {
         private Tristate _needsTurkishCasing = Tristate.NotInitialized;
 
-        private void FinishInitialization() { }
-
         // -----------------------------
         // ---- PAL layer ends here ----
         // -----------------------------
@@ -25,9 +23,10 @@ namespace System.Globalization
 
         private bool IsInvariant { get { return _cultureName.Length == 0; } }
 
-        internal unsafe void ChangeCase(char* src, int srcLen, char* dstBuffer, int dstBufferCapacity, bool bToUpper)
+        internal unsafe void IcuChangeCase(char* src, int srcLen, char* dstBuffer, int dstBufferCapacity, bool bToUpper)
         {
             Debug.Assert(!GlobalizationMode.Invariant);
+            Debug.Assert(!GlobalizationMode.UseNls);
 
             if (IsInvariant)
             {
@@ -8,14 +8,10 @@ namespace System.Globalization
 {
     public partial class TextInfo
     {
-        private unsafe void FinishInitialization()
-        {
-            _sortHandle = CompareInfo.GetSortHandle(_textInfoName);
-        }
-
-        private unsafe void ChangeCase(char* pSource, int pSourceLen, char* pResult, int pResultLen, bool toUpper)
+        private unsafe void NlsChangeCase(char* pSource, int pSourceLen, char* pResult, int pResultLen, bool toUpper)
         {
             Debug.Assert(!GlobalizationMode.Invariant);
+            Debug.Assert(GlobalizationMode.UseNls);
             Debug.Assert(pSource != null);
             Debug.Assert(pResult != null);
             Debug.Assert(pSourceLen >= 0);
index 7e8d1ff..14d5642 100644 (file)
@@ -54,7 +54,10 @@ namespace System.Globalization
             _cultureName = _cultureData.CultureName;
             _textInfoName = _cultureData.TextInfoName;
 
-            FinishInitialization();
+            if (GlobalizationMode.UseNls)
+            {
+                _sortHandle = CompareInfo.NlsGetSortHandle(_textInfoName);
+            }
         }
 
         private TextInfo(CultureData cultureData, bool readOnly)
@@ -176,7 +179,7 @@ namespace System.Globalization
             Debug.Assert(!GlobalizationMode.Invariant);
 
             char dst = default;
-            ChangeCase(&c, 1, &dst, 1, toUpper);
+            ChangeCaseCore(&c, 1, &dst, 1, toUpper);
             return dst;
         }
 
@@ -301,7 +304,7 @@ namespace System.Globalization
                 // has a case conversion that's different from the invariant culture, even for ASCII data (e.g., tr-TR converts
                 // 'i' (U+0069) to Latin Capital Letter I With Dot Above (U+0130)).
 
-                ChangeCase(pSource + currIdx, charCount, pDestination + currIdx, charCount, toUpper);
+                ChangeCaseCore(pSource + currIdx, charCount, pDestination + currIdx, charCount, toUpper);
             }
 
         Return:
@@ -406,7 +409,7 @@ namespace System.Globalization
                     // and run the culture-aware logic over the remainder of the data
                     fixed (char* pResult = result)
                     {
-                        ChangeCase(pSource + currIdx, source.Length - (int)currIdx, pResult + currIdx, result.Length - (int)currIdx, toUpper);
+                        ChangeCaseCore(pSource + currIdx, source.Length - (int)currIdx, pResult + currIdx, result.Length - (int)currIdx, toUpper);
                     }
                     return result;
                 }
@@ -815,6 +818,18 @@ namespace System.Globalization
             return inputIndex;
         }
 
+        private unsafe void ChangeCaseCore(char* src, int srcLen, char* dstBuffer, int dstBufferCapacity, bool bToUpper)
+        {
+            if (GlobalizationMode.UseNls)
+            {
+                NlsChangeCase(src, srcLen, dstBuffer, dstBufferCapacity, bToUpper);
+            }
+            else
+            {
+                IcuChangeCase(src, srcLen, dstBuffer, dstBufferCapacity, bToUpper);
+            }
+        }
+
         // Used in ToTitleCase():
         // When we find a starting letter, the following array decides if a category should be
         // considered as word seprator or not.
diff --git a/src/libraries/System.Runtime/tests/NlsTests/System.Runtime.Nls.Tests.csproj b/src/libraries/System.Runtime/tests/NlsTests/System.Runtime.Nls.Tests.csproj
new file mode 100644 (file)
index 0000000..fa46b81
--- /dev/null
@@ -0,0 +1,61 @@
+<Project Sdk="Microsoft.NET.Sdk">
+  <PropertyGroup>
+    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+    <TestRuntime>true</TestRuntime>
+    <IncludeRemoteExecutor>true</IncludeRemoteExecutor>
+    <TargetFrameworks>$(NetCoreAppCurrent)-Windows_NT</TargetFrameworks>
+  </PropertyGroup>
+  <ItemGroup>
+    <Compile Include="..\Helpers.cs">
+      <Link>Helpers.cs</Link>
+    </Compile>
+    <Compile Include="..\System\ArrayTests.cs">
+      <Link>System\ArrayTests.cs</Link>
+    </Compile>
+    <Compile Include="..\System\String.SplitTests.cs">
+      <Link>System\String.SplitTests.cs</Link>
+    </Compile>
+    <Compile Include="..\System\StringComparerTests.cs">
+      <Link>System\StringComparerTests.cs</Link>
+    </Compile>
+    <Compile Include="..\System\StringGetHashCodeTests.cs">
+      <Link>System\StringGetHashCodeTests.cs</Link>
+    </Compile>
+    <Compile Include="..\System\StringSplitExtensions.cs">
+      <Link>System\StringSplitExtensions.cs</Link>
+    </Compile>
+    <Compile Include="..\System\StringTests.cs">
+      <Link>System\StringTests.cs</Link>
+    </Compile>
+    <Compile Include="..\System\Text\RuneTests.cs">
+      <Link>System\Text\RuneTests.cs</Link>
+    </Compile>
+    <Compile Include="..\System\Text\RuneTests.TestData.cs">
+      <Link>System\Text\RuneTests.TestData.cs</Link>
+    </Compile>
+    <Compile Include="..\System\Text\StringBuilderTests.cs">
+      <Link>System\Text\StringBuilderTests.cs</Link>
+    </Compile>
+    <Compile Include="..\System\Uri.CreateStringTests.cs">
+      <Link>System\Uri.CreateStringTests.cs</Link>
+    </Compile>
+    <Compile Include="..\System\Uri.CreateUriTests.cs">
+      <Link>System\Uri.CreateUriTests.cs</Link>
+    </Compile>
+    <Compile Include="..\System\Uri.MethodsTests.cs">
+      <Link>System\Uri.MethodsTests.cs</Link>
+    </Compile>
+    <Compile Include="$(CommonTestPath)System\EnumTypes.cs">
+      <Link>Common\System\EnumTypes.cs</Link>
+    </Compile>
+    <Compile Include="$(CommonTestPath)System\MockType.cs">
+      <Link>Common\System\MockType.cs</Link>
+    </Compile>
+    <Compile Include="$(CommonTestPath)Tests\System\StringTests.cs">
+      <Link>Common\System\StringTests.cs</Link>
+    </Compile>
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="$(CommonTestPath)CoreFx.Private.TestUtilities.Unicode\CoreFx.Private.TestUtilities.Unicode.csproj" />
+  </ItemGroup>
+</Project>
diff --git a/src/libraries/System.Runtime/tests/NlsTests/runtimeconfig.template.json b/src/libraries/System.Runtime/tests/NlsTests/runtimeconfig.template.json
new file mode 100644 (file)
index 0000000..f93c603
--- /dev/null
@@ -0,0 +1,5 @@
+{
+    "configProperties": {
+        "System.Globalization.UseNls": true
+    }
+}
index bff03ab..f517e55 100644 (file)
@@ -32,7 +32,7 @@
       <Link>Common\System\Collections\TestBase.NonGeneric.cs</Link>
     </Compile>
     <Compile Include="$(CommonTestPath)Tests\System\StringTests.cs">
-      <Link>System\StringTests.cs</Link>
+      <Link>Common\System\StringTests.cs</Link>
     </Compile>
     <Compile Include="$(CommonTestPath)System\Collections\IDictionary.NonGeneric.Tests.cs">
       <Link>Common\System\Collections\IDictionary.NonGeneric.Tests.cs</Link>
index 6772d74..e22f802 100644 (file)
@@ -12,7 +12,7 @@ namespace System.Text.Tests
 {
     public static partial class RuneTests
     {
-        [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsWindows8xOrLater))] // the localization tables used by our test data only exist on Win8+
+        [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsWindows8xOrLater), nameof(PlatformDetection.IsNlsGlobalization))] // the localization tables used by our test data only exist on Win8+
         [PlatformSpecific(TestPlatforms.Windows)]
         [InlineData('0', '0', '0', "en-US")]
         [InlineData('a', 'A', 'a', "en-US")]
@@ -38,7 +38,7 @@ namespace System.Text.Tests
         }
 
         // Invariant ToUpper / ToLower doesn't modify Turkish I or majuscule Eszett
-        [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsWindows8xOrLater))] // the localization tables used by our test data only exist on Win8+
+        [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsWindows8xOrLater), nameof(PlatformDetection.IsNlsGlobalization))] // the localization tables used by our test data only exist on Win8+
         [PlatformSpecific(TestPlatforms.Windows)]
         [InlineData('0', '0', '0')]
         [InlineData('a', 'A', 'a')]
index 444aca4..a6fce5f 100644 (file)
@@ -3,6 +3,7 @@
 // See the LICENSE file in the project root for more information.
 
 using System.Collections.Generic;
+using System.Globalization;
 using System.Linq;
 using System.Runtime.InteropServices;
 using Xunit;
@@ -536,7 +537,7 @@ namespace System.Tests
 
             Uri invalidPunicodeUri = new Uri("http://xn--\u1234pck.com");
             yield return new object[] { invalidPunicodeUri, UriComponents.Host, "xn--\u1234pck.com" };
-            if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) // expected platform differences, see https://github.com/dotnet/runtime/issues/17190
+            if (PlatformDetection.IsNlsGlobalization) // expected platform differences, see https://github.com/dotnet/runtime/issues/17190
             {
                 yield return new object[] { invalidPunicodeUri, UriComponents.NormalizedHost, "xn--\u1234pck.com" };
             }
index 8e139fb..7d0c5ba 100644 (file)
@@ -47,8 +47,8 @@ namespace System.Text.Tests
             AssertEqualOrdinal(Utf8Span.Empty, u8(""));
         }
 
-        [Theory]
         [PlatformSpecific(TestPlatforms.Windows)]
+        [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNlsGlobalization))]
         [InlineData(null, null, StringComparison.OrdinalIgnoreCase, null, true)]
         [InlineData("encyclopaedia", "encyclopædia", StringComparison.OrdinalIgnoreCase, null, false)]
         [InlineData("encyclopaedia", "encyclopædia", StringComparison.InvariantCulture, null, true)]
index 36b1842..a9c4102 100644 (file)
@@ -80,8 +80,8 @@ namespace System.Text.Tests
             }
         }
 
-        [Theory]
         [PlatformSpecific(TestPlatforms.Windows)]
+        [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNlsGlobalization))]
         [MemberData(nameof(TryFindData_Char_WithComparison))]
         public static void TryFind_Char_WithComparison(ustring source, char searchTerm, StringComparison comparison, CultureInfo currentCulture, Range? expectedForwardMatch, Range? expectedBackwardMatch)
         {
@@ -227,8 +227,8 @@ namespace System.Text.Tests
             }
         }
 
-        [Theory]
         [PlatformSpecific(TestPlatforms.Windows)]
+        [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNlsGlobalization))]
         [MemberData(nameof(TryFindData_Rune_WithComparison))]
         public static void TryFind_Rune_WithComparison(ustring source, Rune searchTerm, StringComparison comparison, CultureInfo currentCulture, Range? expectedForwardMatch, Range? expectedBackwardMatch)
         {
@@ -374,8 +374,8 @@ namespace System.Text.Tests
             }
         }
 
-        [Theory]
         [PlatformSpecific(TestPlatforms.Windows)]
+        [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNlsGlobalization))]
         [MemberData(nameof(TryFindData_Utf8Span_WithComparison))]
         public static void TryFind_Utf8Span_WithComparison(ustring source, ustring searchTerm, StringComparison comparison, CultureInfo currentCulture, Range? expectedForwardMatch, Range? expectedBackwardMatch)
         {
index ab1b528..cb169fd 100644 (file)
@@ -76,8 +76,8 @@ namespace System.Text.Tests
             }
         }
 
-        [Fact]
         [PlatformSpecific(TestPlatforms.Windows)]
+        [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNlsGlobalization))]
         public static void GetHashCode_WithComparison()
         {
             // Since hash code generation is randomized, it's possible (though unlikely) that
index 245877d..f25d0e9 100644 (file)
@@ -87,8 +87,8 @@ namespace System.Tests
 
         public static IEnumerable<object[]> TryFindData_Char_WithComparison() => Utf8SpanTests.TryFindData_Char_WithComparison();
 
-        [Theory]
         [PlatformSpecific(TestPlatforms.Windows)]
+        [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNlsGlobalization))]
         [MemberData(nameof(TryFindData_Char_WithComparison))]
         public static void TryFind_Char_WithComparison(ustring source, char searchTerm, StringComparison comparison, CultureInfo currentCulture, Range? expectedForwardMatch, Range? expectedBackwardMatch)
         {
@@ -240,8 +240,8 @@ namespace System.Tests
 
         public static IEnumerable<object[]> TryFindData_Rune_WithComparison() => Utf8SpanTests.TryFindData_Rune_WithComparison();
 
-        [Theory]
         [PlatformSpecific(TestPlatforms.Windows)]
+        [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNlsGlobalization))]
         [MemberData(nameof(TryFindData_Rune_WithComparison))]
         public static void TryFind_Rune_WithComparison(ustring source, Rune searchTerm, StringComparison comparison, CultureInfo currentCulture, Range? expectedForwardMatch, Range? expectedBackwardMatch)
         {
@@ -393,8 +393,8 @@ namespace System.Tests
 
         public static IEnumerable<object[]> TryFindData_Utf8String_WithComparison() => Utf8SpanTests.TryFindData_Utf8Span_WithComparison();
 
-        [Theory]
         [PlatformSpecific(TestPlatforms.Windows)]
+        [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNlsGlobalization))]
         [MemberData(nameof(TryFindData_Utf8String_WithComparison))]
         public static void TryFind_Utf8String_WithComparison(ustring source, ustring searchTerm, StringComparison comparison, CultureInfo currentCulture, Range? expectedForwardMatch, Range? expectedBackwardMatch)
         {
index 2603823..773dea5 100644 (file)
@@ -78,8 +78,8 @@ namespace System.Tests
             }
         }
 
-        [Fact]
         [PlatformSpecific(TestPlatforms.Windows)]
+        [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNlsGlobalization))]
         public static void GetHashCode_WithComparison()
         {
             // Since hash code generation is randomized, it's possible (though unlikely) that
index c31e584..e3ca7ce 100644 (file)
@@ -8,6 +8,8 @@ namespace System.Globalization
 {
     internal partial class GlobalizationMode
     {
+        internal static bool UseNls => false;
+
         private static bool GetGlobalizationInvariantMode()
         {
             bool invariantEnabled = GetInvariantSwitchValue();
@@ -19,7 +21,7 @@ namespace System.Globalization
         }
 
         // Keep this in a separate method to avoid loading the native lib in invariant mode
-        [MethodImplAttribute(MethodImplOptions.NoInlining)]
+        [MethodImpl(MethodImplOptions.NoInlining)]
         private static void LoadICU()
         {
             int res = Interop.Globalization.LoadICU();
index 3267239..5aaa143 100644 (file)
@@ -6,9 +6,11 @@ namespace System.Globalization
 {
     internal partial class GlobalizationMode
     {
+        internal static bool UseNls => true;
+
         private static bool GetGlobalizationInvariantMode()
         {
             return GetInvariantSwitchValue();
         }
     }
-}
\ No newline at end of file
+}