diff --git a/src/libraries/System.Globalization/tests/CultureInfo/CultureInfoCtor.cs b/src/libraries/System.Globalization/tests/CultureInfo/CultureInfoCtor.cs index 787f9e37f719db..83a3bd57cbfe1b 100644 --- a/src/libraries/System.Globalization/tests/CultureInfo/CultureInfoCtor.cs +++ b/src/libraries/System.Globalization/tests/CultureInfo/CultureInfoCtor.cs @@ -439,5 +439,14 @@ public void TestCreationWithTemporaryLCID(int lcid) Assert.NotEqual(lcid, new CultureInfo(lcid).LCID); } + + [InlineData("zh-TW-u-co-zhuyin")] + [InlineData("de-DE-u-co-phoneb")] + [InlineData("de-u-co-phonebk")] + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsIcuGlobalization))] + public void TestCreationWithMangledSortName(string cultureName) + { + Assert.True(CultureInfo.GetCultureInfo(cultureName).CompareInfo.Name.Equals(cultureName, StringComparison.OrdinalIgnoreCase)); + } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/CompareInfo.Icu.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/CompareInfo.Icu.cs index a9b9274952ae0a..02c846c457beea 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/CompareInfo.Icu.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/CompareInfo.Icu.cs @@ -14,7 +14,7 @@ public partial class CompareInfo [NonSerialized] private bool _isAsciiEqualityOrdinal; - private void IcuInitSortHandle() + private void IcuInitSortHandle(string interopCultureName) { if (GlobalizationMode.Invariant) { @@ -23,6 +23,7 @@ private void IcuInitSortHandle() else { Debug.Assert(!GlobalizationMode.UseNls); + Debug.Assert(interopCultureName != null); // Inline the following condition to avoid potential implementation cycles within globalization // @@ -31,7 +32,7 @@ private void IcuInitSortHandle() _isAsciiEqualityOrdinal = _sortName.Length == 0 || (_sortName.Length >= 2 && _sortName[0] == 'e' && _sortName[1] == 'n' && (_sortName.Length == 2 || _sortName[2] == '-')); - _sortHandle = SortHandleCache.GetCachedSortHandle(_sortName); + _sortHandle = SortHandleCache.GetCachedSortHandle(interopCultureName); } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/CompareInfo.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/CompareInfo.cs index b8e7d60a24ed18..bf68d1214d3616 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/CompareInfo.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/CompareInfo.cs @@ -191,7 +191,7 @@ private void InitSort(CultureInfo culture) } else { - IcuInitSortHandle(); + IcuInitSortHandle(culture.InteropName!); } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.Icu.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.Icu.cs index 0d2035b9b40e14..85bf08e06095ac 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.Icu.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.Icu.cs @@ -50,7 +50,11 @@ private bool InitIcuCultureDataCore() index = _sWindowsName.IndexOf(ICU_COLLATION_KEYWORD, StringComparison.Ordinal); if (index >= 0) { - _sName = string.Concat(_sWindowsName.AsSpan(0, index), "_", alternateSortName); + // Use original culture name if alternateSortName is not set, which is possible even if the normalized + // culture name has "@collation=". + // "zh-TW-u-co-zhuyin" is a good example. The term "u-co-" means the following part will be the sort name + // and it will be treated in ICU as "zh-TW@collation=zhuyin". + _sName = alternateSortName.Length == 0 ? realNameBuffer : string.Concat(_sWindowsName.AsSpan(0, index), "_", alternateSortName); } else { diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.cs index 6b0c66fbd361a2..d92419490f16f6 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.cs @@ -413,6 +413,12 @@ internal sealed partial class CultureData private static volatile Dictionary? s_cachedRegions; private static volatile Dictionary? s_regionNames; + /// + /// The culture name to use to interop with the underlying native globalization libraries like ICU or Windows NLS APIs. + /// For example, we can have the name de_DE@collation=phonebook when using ICU for the German culture de-DE with the phonebook sorting behavior. + /// + internal string? InteropName => _sWindowsName; + internal static CultureData? GetCultureDataForRegion(string? cultureName, bool useUserOverride) { // First do a shortcut for Invariant diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureInfo.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureInfo.cs index c0870e1f7a863e..baf1e6dd215017 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureInfo.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureInfo.cs @@ -580,6 +580,12 @@ public static CultureInfo[] GetCultures(CultureTypes types) /// internal string SortName => _sortName ??= _cultureData.SortName; + /// + /// The culture name to use to interop with the underlying native globalization libraries like ICU or Windows NLS APIs. + /// For example, we can have the name de_DE@collation=phonebook when using ICU for the German culture de-DE with the phonebook sorting behavior. + /// + internal string? InteropName => _cultureData.InteropName; + public string IetfLanguageTag => // special case the compatibility cultures Name switch