From 68dde1949c759bf67b42a2a2d805efed075ea0e2 Mon Sep 17 00:00:00 2001 From: Meri Khamoyan <96171496+mkhamoyan@users.noreply.github.com> Date: Wed, 1 Nov 2023 11:17:34 +0100 Subject: [PATCH] [iOS][non-icu] Implement missing Locale and Casing functions (#94038) Implement missing Locale and Casing function --- .../Common/src/Interop/Interop.Locale.iOS.cs | 15 ++- .../TestUtilities/System/PlatformDetection.cs | 4 + .../CompareInfo/CompareInfoTests.IsSuffix.cs | 2 +- .../tests/CompareInfo/CompareInfoTests.cs | 16 +-- .../tests/CompareInfo/CompareInfoTestsBase.cs | 1 + .../tests/CultureInfo/CultureInfoAll.cs | 7 +- .../tests/CultureInfo/CultureInfoCtor.cs | 50 ++++---- .../tests/CultureInfo/GetCultureInfo.cs | 23 +++- ...DateTimeFormatInfoGetAbbreviatedEraName.cs | 2 +- .../DateTimeFormatInfoGetEra.cs | 2 +- .../System.Globalization.IOS.Tests.csproj | 121 +++++++++++++----- .../System.Globalization/tests/IcuTests.cs | 2 +- .../NumberFormatInfo/NumberFormatInfoData.cs | 2 +- .../System/Globalization/RegionInfoTests.cs | 8 +- .../System/Globalization/CultureData.Icu.cs | 56 ++++++-- .../System/Globalization/CultureData.Unix.cs | 7 +- .../System/Globalization/CultureData.iOS.cs | 14 -- src/mono/mono/mini/CMakeLists.txt | 1 + .../CMakeLists.txt | 1 + .../System.Globalization.Native/pal_casing.c | 18 --- .../System.Globalization.Native/pal_common.c | 21 +++ .../System.Globalization.Native/pal_locale.h | 4 + .../System.Globalization.Native/pal_locale.m | 45 +++++++ 23 files changed, 289 insertions(+), 133 deletions(-) create mode 100644 src/native/libs/System.Globalization.Native/pal_common.c diff --git a/src/libraries/Common/src/Interop/Interop.Locale.iOS.cs b/src/libraries/Common/src/Interop/Interop.Locale.iOS.cs index 6725436b4f094..3062de8918dc3 100644 --- a/src/libraries/Common/src/Interop/Interop.Locale.iOS.cs +++ b/src/libraries/Common/src/Interop/Interop.Locale.iOS.cs @@ -7,8 +7,8 @@ internal static partial class Interop { internal static partial class Globalization { - [LibraryImport(Libraries.GlobalizationNative, EntryPoint = "GlobalizationNative_GetLocaleNameNative", StringMarshalling = StringMarshalling.Utf8)] - internal static partial string GetLocaleNameNative(string localeName); + [LibraryImport(Libraries.GlobalizationNative, EntryPoint = "GlobalizationNative_GetDefaultLocaleNameNative", StringMarshalling = StringMarshalling.Utf8)] + internal static partial string GetDefaultLocaleNameNative(); [LibraryImport(Libraries.GlobalizationNative, EntryPoint = "GlobalizationNative_GetLocaleInfoStringNative", StringMarshalling = StringMarshalling.Utf8)] internal static partial string GetLocaleInfoStringNative(string localeName, uint localeStringData); @@ -22,10 +22,17 @@ internal static partial class Globalization [LibraryImport(Libraries.GlobalizationNative, EntryPoint = "GlobalizationNative_GetLocaleInfoSecondaryGroupingSizeNative", StringMarshalling = StringMarshalling.Utf8)] internal static partial int GetLocaleInfoSecondaryGroupingSizeNative(string localeName, uint localeGroupingData); - [LibraryImport(Libraries.GlobalizationNative, EntryPoint = "GlobalizationNative_GetLocaleTimeFormatNative", StringMarshalling = StringMarshalling.Utf8)] - internal static partial string GetLocaleTimeFormatNative(string localeName, [MarshalAs(UnmanagedType.Bool)] bool shortFormat); + [LibraryImport(Libraries.GlobalizationNative, EntryPoint = "GlobalizationNative_GetLocaleNameNative", StringMarshalling = StringMarshalling.Utf8)] + internal static partial string GetLocaleNameNative(string localeName); [LibraryImport(Libraries.GlobalizationNative, EntryPoint = "GlobalizationNative_GetLocalesNative", StringMarshalling = StringMarshalling.Utf16)] internal static partial int GetLocalesNative([Out] char[]? value, int valueLength); + + [LibraryImport(Libraries.GlobalizationNative, EntryPoint = "GlobalizationNative_GetLocaleTimeFormatNative", StringMarshalling = StringMarshalling.Utf8)] + internal static partial string GetLocaleTimeFormatNative(string localeName, [MarshalAs(UnmanagedType.Bool)] bool shortFormat); + + [LibraryImport(Libraries.GlobalizationNative, EntryPoint = "GlobalizationNative_IsPredefinedLocaleNative", StringMarshalling = StringMarshalling.Utf8)] + [return: MarshalAs(UnmanagedType.Bool)] + internal static partial bool IsPredefinedLocaleNative(string localeName); } } diff --git a/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs b/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs index 3d35765aee173..a33f7a09f2df2 100644 --- a/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs +++ b/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs @@ -366,12 +366,16 @@ public static string GetDistroVersionString() public static Version ICUVersion => m_icuVersion.Value; public static bool IsInvariantGlobalization => m_isInvariant.Value; + public static bool IsHybridGlobalization => m_isHybrid.Value; public static bool IsHybridGlobalizationOnBrowser => m_isHybrid.Value && IsBrowser; public static bool IsHybridGlobalizationOnOSX => m_isHybrid.Value && (IsOSX || IsMacCatalyst || IsiOS || IstvOS); public static bool IsNotHybridGlobalizationOnBrowser => !IsHybridGlobalizationOnBrowser; public static bool IsNotInvariantGlobalization => !IsInvariantGlobalization; + public static bool IsNotHybridGlobalization => !IsHybridGlobalization; + public static bool IsNotHybridGlobalizationOnOSX => !IsHybridGlobalizationOnOSX; public static bool IsIcuGlobalization => ICUVersion > new Version(0, 0, 0, 0); public static bool IsIcuGlobalizationAndNotHybridOnBrowser => IsIcuGlobalization && IsNotHybridGlobalizationOnBrowser; + public static bool IsIcuGlobalizationAndNotHybrid => IsIcuGlobalization && IsNotHybridGlobalization; public static bool IsNlsGlobalization => IsNotInvariantGlobalization && !IsIcuGlobalization; public static bool IsSubstAvailable diff --git a/src/libraries/System.Globalization/tests/CompareInfo/CompareInfoTests.IsSuffix.cs b/src/libraries/System.Globalization/tests/CompareInfo/CompareInfoTests.IsSuffix.cs index 8b83094efe3be..906392ba3fed9 100644 --- a/src/libraries/System.Globalization/tests/CompareInfo/CompareInfoTests.IsSuffix.cs +++ b/src/libraries/System.Globalization/tests/CompareInfo/CompareInfoTests.IsSuffix.cs @@ -162,7 +162,7 @@ public void IsSuffix(CompareInfo compareInfo, string source, string value, Compa [Fact] public void IsSuffix_UnassignedUnicode() { - bool result = PlatformDetection.IsIcuGlobalization ? false : true; + bool result = PlatformDetection.IsIcuGlobalization || PlatformDetection.IsHybridGlobalizationOnOSX ? false : true; int expectedMatchLength = (result) ? 6 : 0; IsSuffix(s_invariantCompare, "FooBar", "Foo\uFFFFBar", CompareOptions.None, result, expectedMatchLength); diff --git a/src/libraries/System.Globalization/tests/CompareInfo/CompareInfoTests.cs b/src/libraries/System.Globalization/tests/CompareInfo/CompareInfoTests.cs index 24ef09380db06..cbd011b96fa0c 100644 --- a/src/libraries/System.Globalization/tests/CompareInfo/CompareInfoTests.cs +++ b/src/libraries/System.Globalization/tests/CompareInfo/CompareInfoTests.cs @@ -60,7 +60,7 @@ public void EqualsTest(CompareInfo compare1, object value, bool expected) new object[] { "", CompareOptions.None, "\u200c", CompareOptions.None, true }, // see comment at bottom of SortKey_TestData }; - [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalization))] [MemberData(nameof(GetHashCodeTestData))] public void GetHashCodeTest(string source1, CompareOptions options1, string source2, CompareOptions options2, bool expected) { @@ -345,14 +345,14 @@ public static void LcidTest(string cultureName, int lcid) Assert.Equal(lcid, ci.LCID); } - [ConditionalTheory(typeof(CompareInfoTests), nameof(IsNotWindowsKanaRegressedVersionAndNotHybridGlobalizationOnWasm))] + [ConditionalTheory(typeof(CompareInfoTests), nameof(IsNotWindowsKanaRegressedVersionAndNotHybridGlobalization))] [MemberData(nameof(SortKey_Kana_TestData))] public void SortKeyKanaTest(CompareInfo compareInfo, string string1, string string2, CompareOptions options, int expected) { SortKeyTest(compareInfo, string1, string2, options, expected); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsHybridGlobalizationOnBrowser))] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsHybridGlobalization))] public void SortKeyTestNotSupported() { try @@ -395,7 +395,7 @@ public void SortKeyTestNotSupported() private static bool WindowsVersionHasTheCompareStringRegression => PlatformDetection.IsNlsGlobalization && CompareStringEx("", NORM_LINGUISTIC_CASING, "", 0, "\u200C", 1, IntPtr.Zero, IntPtr.Zero, 0) != 2; - [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalization))] [MemberData(nameof(SortKey_TestData))] public void SortKeyTest(CompareInfo compareInfo, string string1, string string2, CompareOptions options, int expectedSign) { @@ -444,7 +444,7 @@ unsafe static void RunSpanSortKeyTest(CompareInfo compareInfo, ReadOnlySpan !PlatformDetection.I s_invariantCompare.Compare("\u3060", "\uFF80\uFF9E", CompareOptions.IgnoreKanaType | CompareOptions.IgnoreWidth | CompareOptions.IgnoreCase) == 0; protected static bool IsNotWindowsKanaRegressedVersionAndNotHybridGlobalizationOnWasm() => !PlatformDetection.IsHybridGlobalizationOnBrowser && IsNotWindowsKanaRegressedVersion(); + protected static bool IsNotWindowsKanaRegressedVersionAndNotHybridGlobalization() => IsNotWindowsKanaRegressedVersion() && PlatformDetection.IsNotHybridGlobalization; } } diff --git a/src/libraries/System.Globalization/tests/CultureInfo/CultureInfoAll.cs b/src/libraries/System.Globalization/tests/CultureInfo/CultureInfoAll.cs index fb4692da1a0ab..c707ff005faf3 100644 --- a/src/libraries/System.Globalization/tests/CultureInfo/CultureInfoAll.cs +++ b/src/libraries/System.Globalization/tests/CultureInfo/CultureInfoAll.cs @@ -635,13 +635,11 @@ public static IEnumerable CultureInfo_TestData() yield return new object[] { 0x2009, new [] { "en-jm" }, "en-JM", "eng", "ENJ", "en-JM", "en-JM" }; yield return new object[] { 0x241a, new [] { "sr-latn-rs" }, "sr-Latn-RS", "srp", "SRM", "sr-Latn-RS", "sr-Latn-RS" }; yield return new object[] { 0x2809, new [] { "en-bz" }, "en-BZ", "eng", "ENL", "en-BZ", "en-BZ" }; - yield return new object[] { 0x281a, new [] { "sr-cyrl-rs" }, "sr-Cyrl-RS", "srp", "SRO", "sr-Cyrl-RS", "sr-Cyrl-RS" }; yield return new object[] { 0x2c09, new [] { "en-tt" }, "en-TT", "eng", "ENT", "en-TT", "en-TT" }; yield return new object[] { 0x3009, new [] { "en-zw" }, "en-ZW", "eng", "ENW", "en-ZW", "en-ZW" }; yield return new object[] { 0x3409, new [] { "en-ph" }, "en-PH", "eng", "ENP", "en-PH", "en-PH" }; yield return new object[] { 0x4009, new [] { "en-in" }, "en-IN", "eng", "ENN", "en-IN", "en-IN" }; yield return new object[] { 0x4809, new [] { "en-sg" }, "en-SG", "eng", "ENE", "en-SG", "en-SG" }; - yield return new object[] { 0x6c1a, new [] { "sr-cyrl" }, "sr-Cyrl-RS", "srp", "SRO", "sr-Cyrl", "sr-Cyrl-RS" }; yield return new object[] { 0x701a, new [] { "sr-latn" }, "sr-Latn-RS", "srp", "SRM", "sr-Latn", "sr-Latn-RS" }; yield return new object[] { 0x7804, new [] { "zh" }, "zh-CN", "zho", "CHS", "zh", "zh-CN" }; yield return new object[] { 0x7c04, new [] { "zh-cht", "zh-hant" }, "zh-HK", "zho", "CHT", "zh-Hant", "zh-HK" }; @@ -654,6 +652,11 @@ public static IEnumerable CultureInfo_TestData() yield return new object[] { 0x40404, new [] { "zh-tw_radstr", "zh-tw" }, "zh-TW", "zho", "CHT", "zh-Hant-TW", "zh-TW" }; yield return new object[] { 0x40411, new [] { "ja-jp_radstr", "ja-jp" }, "ja-JP", "jpn", "JPN", "ja-JP", "ja-JP" }; yield return new object[] { 0x40c04, new [] { "zh-hk_radstr", "zh-hk" }, "zh-HK", "zho", "ZHH", "zh-Hant-HK", "zh-HK" }; + if (!PlatformDetection.IsHybridGlobalizationOnOSX) + { + yield return new object[] { 0x281a, new [] { "sr-cyrl-rs" }, "sr-Cyrl-RS", "srp", "SRO", "sr-Cyrl-RS", "sr-Cyrl-RS" }; + yield return new object[] { 0x6c1a, new [] { "sr-cyrl" }, "sr-Cyrl-RS", "srp", "SRO", "sr-Cyrl", "sr-Cyrl-RS" }; + } } [Theory] diff --git a/src/libraries/System.Globalization/tests/CultureInfo/CultureInfoCtor.cs b/src/libraries/System.Globalization/tests/CultureInfo/CultureInfoCtor.cs index 37175600777b4..9edf9c5dfe6cf 100644 --- a/src/libraries/System.Globalization/tests/CultureInfo/CultureInfoCtor.cs +++ b/src/libraries/System.Globalization/tests/CultureInfo/CultureInfoCtor.cs @@ -40,8 +40,6 @@ public static IEnumerable Ctor_String_TestData() yield return new object[] { "az", new [] { "az" }}; yield return new object[] { "az-Cyrl", new [] { "az-Cyrl" }}; yield return new object[] { "az-Cyrl-AZ", new [] { "az-Cyrl-AZ" }}; - yield return new object[] { "az-Latn", new [] { "az-Latn" }}; - yield return new object[] { "az-Latn-AZ", new [] { "az-Latn-AZ" }}; yield return new object[] { "ba", new [] { "ba" }}; yield return new object[] { "ba-RU", new [] { "ba-RU" }}; yield return new object[] { "be", new [] { "be" }}; @@ -58,8 +56,6 @@ public static IEnumerable Ctor_String_TestData() yield return new object[] { "bs", new [] { "bs" }}; yield return new object[] { "bs-Cyrl", new [] { "bs-Cyrl" }}; yield return new object[] { "bs-Cyrl-BA", new [] { "bs-Cyrl-BA" }}; - yield return new object[] { "bs-Latn", new [] { "bs-Latn" }}; - yield return new object[] { "bs-Latn-BA", new [] { "bs-Latn-BA" }}; yield return new object[] { "ca", new [] { "ca" } }; yield return new object[] { "ca-ES", new [] { "ca-ES" } }; yield return new object[] { "co", new [] { "co" }}; @@ -154,8 +150,6 @@ public static IEnumerable Ctor_String_TestData() yield return new object[] { "gu", new [] { "gu" } }; yield return new object[] { "gu-IN", new [] { "gu-IN" } }; yield return new object[] { "ha", new [] { "ha" }}; - yield return new object[] { "ha-Latn", new [] { "ha-Latn" }}; - yield return new object[] { "ha-Latn-NG", new [] { "ha-Latn-NG" }}; yield return new object[] { "he", new [] { "he" } }; yield return new object[] { "he-IL", new [] { "he-IL" } }; yield return new object[] { "hi", new [] { "hi" } }; @@ -221,7 +215,6 @@ public static IEnumerable Ctor_String_TestData() yield return new object[] { "ml", new [] { "ml" } }; yield return new object[] { "ml-IN", new [] { "ml-IN" } }; yield return new object[] { "mn", new [] { "mn" }}; - yield return new object[] { "mn-Cyrl", new [] { "mn-Cyrl" }}; yield return new object[] { "mn-MN", new [] { "mn-MN" }}; yield return new object[] { "mn-Mong", new [] { "mn-Mong" }}; yield return new object[] { "mn-Mong-CN", new [] { "mn-Mong-CN" }}; @@ -243,7 +236,6 @@ public static IEnumerable Ctor_String_TestData() yield return new object[] { "nl-NL", new [] { "nl-NL" } }; yield return new object[] { "nn", new [] { "nn" }}; yield return new object[] { "nn-NO", new [] { "nn-NO" }}; - yield return new object[] { "no", new [] { "no" } }; yield return new object[] { "nso", new [] { "nso" }}; yield return new object[] { "nso-ZA", new [] { "nso-ZA" }}; yield return new object[] { "oc", new [] { "oc" }}; @@ -300,14 +292,8 @@ public static IEnumerable Ctor_String_TestData() yield return new object[] { "sq", new [] { "sq" }}; yield return new object[] { "sq-AL", new [] { "sq-AL" }}; yield return new object[] { "sr", new [] { "sr" } }; - yield return new object[] { "sr-Cyrl", new [] { "sr-Cyrl" } }; - yield return new object[] { "sr-Cyrl-BA", new [] { "sr-Cyrl-BA" }}; - yield return new object[] { "sr-Cyrl-CS", new [] { "sr-Cyrl-CS" }}; - yield return new object[] { "sr-Cyrl-ME", new [] { "sr-Cyrl-ME" }}; - yield return new object[] { "sr-Cyrl-RS", new [] { "sr-Cyrl-RS" } }; yield return new object[] { "sr-Latn", new [] { "sr-Latn" } }; yield return new object[] { "sr-Latn-BA", new [] { "sr-Latn-BA" }}; - yield return new object[] { "sr-Latn-CS", new [] { "sr-Latn-CS" }}; yield return new object[] { "sr-Latn-ME", new [] { "sr-Latn-ME" }}; yield return new object[] { "sr-Latn-RS", new [] { "sr-Latn-RS" } }; yield return new object[] { "sv", new [] { "sv" } }; @@ -322,8 +308,6 @@ public static IEnumerable Ctor_String_TestData() yield return new object[] { "te", new [] { "te" } }; yield return new object[] { "te-IN", new [] { "te-IN" } }; yield return new object[] { "tg", new [] { "tg" }}; - yield return new object[] { "tg-Cyrl", new [] { "tg-Cyrl" }}; - yield return new object[] { "tg-Cyrl-TJ", new [] { "tg-Cyrl-TJ" }}; yield return new object[] { "th", new [] { "th" } }; yield return new object[] { "th-TH", new [] { "th-TH" } }; yield return new object[] { "tk", new [] { "tk" }}; @@ -335,8 +319,6 @@ public static IEnumerable Ctor_String_TestData() yield return new object[] { "tt", new [] { "tt" }}; yield return new object[] { "tt-RU", new [] { "tt-RU" }}; yield return new object[] { "tzm", new [] { "tzm" }}; - yield return new object[] { "tzm-Latn", new [] { "tzm-Latn" }}; - yield return new object[] { "tzm-Latn-DZ", new [] { "tzm-Latn-DZ" }}; yield return new object[] { "ug", new [] { "ug" }}; yield return new object[] { "ug-CN", new [] { "ug-CN" }}; yield return new object[] { "uk", new [] { "uk" } }; @@ -346,8 +328,6 @@ public static IEnumerable Ctor_String_TestData() yield return new object[] { "uz", new [] { "uz" }}; yield return new object[] { "uz-Cyrl", new [] { "uz-Cyrl" }}; yield return new object[] { "uz-Cyrl-UZ", new [] { "uz-Cyrl-UZ" }}; - yield return new object[] { "uz-Latn", new [] { "uz-Latn" }}; - yield return new object[] { "uz-Latn-UZ", new [] { "uz-Latn-UZ" }}; yield return new object[] { "vi", new [] { "vi" } }; yield return new object[] { "vi-VN", new [] { "vi-VN" } }; yield return new object[] { "wo", new [] { "wo" }}; @@ -377,6 +357,30 @@ public static IEnumerable Ctor_String_TestData() yield return new object[] { "zu-ZA", new [] { "zu-ZA" }}; yield return new object[] { CultureInfo.CurrentCulture.Name, new [] { CultureInfo.CurrentCulture.Name } }; + if (!PlatformDetection.IsHybridGlobalizationOnOSX) + { + yield return new object[] { "az-Latn", new [] { "az-Latn" }}; + yield return new object[] { "az-Latn-AZ", new [] { "az-Latn-AZ" }}; + yield return new object[] { "bs-Latn", new [] { "bs-Latn" }}; + yield return new object[] { "bs-Latn-BA", new [] { "bs-Latn-BA" }}; + yield return new object[] { "ha-Latn", new [] { "ha-Latn" }}; + yield return new object[] { "ha-Latn-NG", new [] { "ha-Latn-NG" }}; + yield return new object[] { "mn-Cyrl", new [] { "mn-Cyrl" }}; + yield return new object[] { "no", new [] { "no" } }; + yield return new object[] { "sr-Cyrl", new [] { "sr-Cyrl" } }; + yield return new object[] { "sr-Cyrl-BA", new [] { "sr-Cyrl-BA" }}; + yield return new object[] { "sr-Cyrl-CS", new [] { "sr-Cyrl-CS" }}; + yield return new object[] { "sr-Cyrl-ME", new [] { "sr-Cyrl-ME" }}; + yield return new object[] { "sr-Cyrl-RS", new [] { "sr-Cyrl-RS" } }; + yield return new object[] { "sr-Latn-CS", new [] { "sr-Latn-CS" }}; + yield return new object[] { "tg-Cyrl", new [] { "tg-Cyrl" }}; + yield return new object[] { "tg-Cyrl-TJ", new [] { "tg-Cyrl-TJ" }}; + yield return new object[] { "tzm-Latn", new [] { "tzm-Latn" }}; + yield return new object[] { "tzm-Latn-DZ", new [] { "tzm-Latn-DZ" }}; + yield return new object[] { "uz-Latn", new [] { "uz-Latn" }}; + yield return new object[] { "uz-Latn-UZ", new [] { "uz-Latn-UZ" }}; + } + if ((!PlatformDetection.IsWindows || PlatformDetection.WindowsVersion >= 10) && (PlatformDetection.IsNotBrowser)) { yield return new object[] { "en-US-CUSTOM", new [] { "en-US-CUSTOM", "en-US-custom" } }; @@ -396,7 +400,7 @@ public void Ctor_String(string name, string[] expectedNames) Assert.Equal(cultureName, culture.ToString(), ignoreCase: true); } - [Fact] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnOSX))] public void Ctor_String_Invalid() { AssertExtensions.Throws("name", () => new CultureInfo(null)); // Name is null @@ -442,7 +446,7 @@ public void TestCreationWithTemporaryLCID(int lcid) [InlineData("de-DE-u-co-phonebk-t-xx", "de-DE-t-xx", "de-DE-t-xx_phoneboo")] [InlineData("de-DE-u-co-phonebk-t-xx-u-yy", "de-DE-t-xx-u-yy", "de-DE-t-xx-u-yy_phoneboo")] [InlineData("de-DE", "de-DE", "de-DE")] - [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsIcuGlobalizationAndNotHybridOnBrowser))] + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsIcuGlobalizationAndNotHybrid))] public void TestCreationWithMangledSortName(string cultureName, string expectedCultureName, string expectedSortName) { CultureInfo ci = CultureInfo.GetCultureInfo(cultureName); @@ -457,7 +461,7 @@ public void TestCreationWithMangledSortName(string cultureName, string expectedC [InlineData("qps-plocm", "qps-PLOCM")] // ICU normalize this name to "qps--plocm" which we normalize it back to "qps-plocm" [InlineData("zh_CN", "zh_cn")] [InlineData("km_KH", "km_kh")] - [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsIcuGlobalizationAndNotHybridOnBrowser), nameof(PlatformDetection.IsNotWindowsServerCore))] + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsIcuGlobalizationAndNotHybrid), nameof(PlatformDetection.IsNotWindowsServerCore))] public void TestCreationWithICUNormalizedNames(string cultureName, string expectedCultureName) { CultureInfo ci = CultureInfo.GetCultureInfo(cultureName); diff --git a/src/libraries/System.Globalization/tests/CultureInfo/GetCultureInfo.cs b/src/libraries/System.Globalization/tests/CultureInfo/GetCultureInfo.cs index 0e7873853ebd7..a1dc7c2c9c7a8 100644 --- a/src/libraries/System.Globalization/tests/CultureInfo/GetCultureInfo.cs +++ b/src/libraries/System.Globalization/tests/CultureInfo/GetCultureInfo.cs @@ -21,7 +21,10 @@ public static IEnumerable GetCultureInfoTestData() yield return new object[] { "ja-JP" }; yield return new object[] { "ar-SA" }; yield return new object[] { "xx-XX" }; - yield return new object[] { "de-AT-1901" }; + if (!PlatformDetection.IsHybridGlobalizationOnOSX) + { + yield return new object[] { "de-AT-1901" }; + } yield return new object[] { "zh-Hans" }; yield return new object[] { "zh-Hans-HK" }; yield return new object[] { "zh-Hans-MO" }; @@ -30,16 +33,22 @@ public static IEnumerable GetCultureInfoTestData() yield return new object[] { "zh-Hant-CN" }; yield return new object[] { "zh-Hant-SG" }; - if (PlatformDetection.IsIcuGlobalization) + if (PlatformDetection.IsIcuGlobalization || PlatformDetection.IsHybridGlobalizationOnOSX) { if (PlatformDetection.IsNotWindows) { yield return new object[] { "x\u0000X-Yy", "x" }; // Null byte - yield return new object[] { "zh-cmn", "zh-CMN" }; - yield return new object[] { "zh-CMN-HANS" }; - yield return new object[] { "zh-cmn-Hant", "zh-CMN-HANT" }; + if (!PlatformDetection.IsHybridGlobalizationOnOSX) + { + yield return new object[] { "zh-cmn", "zh-CMN" }; + yield return new object[] { "zh-CMN-HANS" }; + yield return new object[] { "zh-cmn-Hant", "zh-CMN-HANT" }; + } + } + if (!PlatformDetection.IsHybridGlobalizationOnOSX) + { + yield return new object[] { "sgn-BE-FR" }; } - yield return new object[] { "sgn-BE-FR" }; yield return new object[] { "zh-min-nan", "nan" }; yield return new object[] { "zh-gan", "gan" }; yield return new object[] { "zh-Hans-CN" }; @@ -98,7 +107,7 @@ public void GetCultureInfo(string name, string expected = null) [InlineData("foo_-bar")] [InlineData("foo/bar")] [InlineData("/")] - [InlineData("0123456789012345678901234567890123456789012345678901234567890123456789012345678901234")] // > 85 characters + [InlineData("01234567890123456789012345678901234567890123456789012345678901234567890123456789012345")] // > 85 characters public void TestInvalidCultureNames(string name) { Assert.Throws(() => CultureInfo.GetCultureInfo(name)); diff --git a/src/libraries/System.Globalization/tests/DateTimeFormatInfo/DateTimeFormatInfoGetAbbreviatedEraName.cs b/src/libraries/System.Globalization/tests/DateTimeFormatInfo/DateTimeFormatInfoGetAbbreviatedEraName.cs index 0efefdd7d6a69..8dd07f32a85bc 100644 --- a/src/libraries/System.Globalization/tests/DateTimeFormatInfo/DateTimeFormatInfoGetAbbreviatedEraName.cs +++ b/src/libraries/System.Globalization/tests/DateTimeFormatInfo/DateTimeFormatInfoGetAbbreviatedEraName.cs @@ -208,7 +208,7 @@ public static IEnumerable GetAbbreviatedEraName_TestData() } } - [Theory] + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnOSX))] [MemberData(nameof(GetAbbreviatedEraName_TestData))] public void GetAbbreviatedEraName_Invoke_ReturnsExpected(DateTimeFormatInfo format, int era, string expected) { diff --git a/src/libraries/System.Globalization/tests/DateTimeFormatInfo/DateTimeFormatInfoGetEra.cs b/src/libraries/System.Globalization/tests/DateTimeFormatInfo/DateTimeFormatInfoGetEra.cs index e4c75e1bb7601..6182546199a92 100644 --- a/src/libraries/System.Globalization/tests/DateTimeFormatInfo/DateTimeFormatInfoGetEra.cs +++ b/src/libraries/System.Globalization/tests/DateTimeFormatInfo/DateTimeFormatInfoGetEra.cs @@ -49,7 +49,7 @@ public static IEnumerable GetEra_TestData() yield return new object[] { frFRFormat, "ap J-C", -1 }; } - [Theory] + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnOSX))] [MemberData(nameof(GetEra_TestData))] public void GetEra_Invoke_ReturnsExpected(DateTimeFormatInfo format, string eraName, int expected) { diff --git a/src/libraries/System.Globalization/tests/Hybrid/System.Globalization.IOS.Tests.csproj b/src/libraries/System.Globalization/tests/Hybrid/System.Globalization.IOS.Tests.csproj index bb38f00b88f9d..e0ff97362fe7e 100644 --- a/src/libraries/System.Globalization/tests/Hybrid/System.Globalization.IOS.Tests.csproj +++ b/src/libraries/System.Globalization/tests/Hybrid/System.Globalization.IOS.Tests.csproj @@ -3,22 +3,65 @@ $(NetCoreAppCurrent)-ios;$(NetCoreAppCurrent)-tvos;$(NetCoreAppCurrent)-maccatalyst true true + true + true + 15.0 - - - - - - - - + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -31,8 +74,10 @@ + + @@ -52,28 +97,44 @@ - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + - + + + + + + + + + + + + + + + + + CharUnicodeInfo\UnicodeData.$(UnicodeUcdVersion).txt + UnicodeData.txt + + + CharUnicodeInfo\GraphemeBreakTest-$(UnicodeUcdVersion).0.txt + GraphemeBreakTest.txt + + + + diff --git a/src/libraries/System.Globalization/tests/IcuTests.cs b/src/libraries/System.Globalization/tests/IcuTests.cs index 0990c710f6d63..05b0e950d7432 100644 --- a/src/libraries/System.Globalization/tests/IcuTests.cs +++ b/src/libraries/System.Globalization/tests/IcuTests.cs @@ -9,7 +9,7 @@ namespace System.Globalization.Tests { public class IcuTests { - private static bool IsIcuCompatiblePlatform => PlatformDetection.IsNotWindows || + private static bool IsIcuCompatiblePlatform => !PlatformDetection.IsHybridGlobalizationOnOSX && PlatformDetection.IsNotWindows || ((PlatformDetection.IsWindowsServer2019 || PlatformDetection.IsWindows10Version1903OrGreater) && // Server core doesn't have icu.dll on SysWOW64 !(PlatformDetection.IsWindowsServerCore && PlatformDetection.IsX86Process)); diff --git a/src/libraries/System.Globalization/tests/NumberFormatInfo/NumberFormatInfoData.cs b/src/libraries/System.Globalization/tests/NumberFormatInfo/NumberFormatInfoData.cs index 36ff2977b807d..96911b398714b 100644 --- a/src/libraries/System.Globalization/tests/NumberFormatInfo/NumberFormatInfoData.cs +++ b/src/libraries/System.Globalization/tests/NumberFormatInfo/NumberFormatInfoData.cs @@ -7,7 +7,7 @@ internal static class NumberFormatInfoData { public static int[] UrINNumberGroupSizes() { - if (PlatformDetection.WindowsVersion >= 10 || PlatformDetection.ICUVersion.Major >= 55) + if (PlatformDetection.WindowsVersion >= 10 || PlatformDetection.ICUVersion.Major >= 55 || PlatformDetection.IsHybridGlobalizationOnOSX) { return new int[] { 3 }; } diff --git a/src/libraries/System.Globalization/tests/System/Globalization/RegionInfoTests.cs b/src/libraries/System.Globalization/tests/System/Globalization/RegionInfoTests.cs index 6fd30317d7f06..a5e4641290aad 100644 --- a/src/libraries/System.Globalization/tests/System/Globalization/RegionInfoTests.cs +++ b/src/libraries/System.Globalization/tests/System/Globalization/RegionInfoTests.cs @@ -112,7 +112,7 @@ public void DisplayName(string name, string expected) public static IEnumerable NativeName_TestData() { // Android has its own ICU, which doesn't 100% map to UsingLimitedCultures - if (PlatformDetection.IsNotUsingLimitedCultures || PlatformDetection.IsAndroid) + if (PlatformDetection.IsNotUsingLimitedCultures || PlatformDetection.IsAndroid || PlatformDetection.IsHybridGlobalizationOnOSX) { yield return new object[] { "GB", "United Kingdom" }; yield return new object[] { "SE", "Sverige" }; @@ -137,7 +137,7 @@ public void NativeName(string name, string expected) public static IEnumerable EnglishName_TestData() { // Android has its own ICU, which doesn't 100% map to UsingLimitedCultures - if (PlatformDetection.IsNotUsingLimitedCultures || PlatformDetection.IsAndroid) + if (PlatformDetection.IsNotUsingLimitedCultures || PlatformDetection.IsAndroid || PlatformDetection.IsHybridGlobalizationOnOSX) { yield return new object[] { "en-US", new string[] { "United States" } }; yield return new object[] { "US", new string[] { "United States" } }; @@ -208,7 +208,7 @@ public static IEnumerable RegionInfo_TestData() "SAU", "SAU" }; yield return new object[] { 0x412, 134, "South Korean Won", "KRW", "Korean Won", PlatformDetection.IsNlsGlobalization ? "\uc6d0" : "\ub300\ud55c\ubbfc\uad6d\u0020\uc6d0", "KOR", "KOR" }; yield return new object[] { 0x40d, 117, "Israeli New Shekel", "ILS", "Israeli New Sheqel", - PlatformDetection.IsNlsGlobalization || PlatformDetection.ICUVersion.Major >= 58 ? "\u05e9\u05e7\u05dc\u0020\u05d7\u05d3\u05e9" : "\u05e9\u05f4\u05d7", "ISR", "ISR" }; + PlatformDetection.IsNlsGlobalization || PlatformDetection.ICUVersion.Major >= 58 || PlatformDetection.IsHybridGlobalizationOnOSX ? "\u05e9\u05e7\u05dc\u0020\u05d7\u05d3\u05e9" : "\u05e9\u05f4\u05d7", "ISR", "ISR" }; } [Theory] @@ -219,7 +219,7 @@ public void MiscTest(int lcid, int geoId, string currencyEnglishName, string cur Assert.Equal(geoId, ri.GeoId); // Android has its own ICU, which doesn't 100% map to UsingLimitedCultures - if (PlatformDetection.IsUsingLimitedCultures && !PlatformDetection.IsAndroid) + if (PlatformDetection.IsUsingLimitedCultures && !PlatformDetection.IsAndroid && !PlatformDetection.IsHybridGlobalizationOnOSX) { Assert.Equal(currencyShortName, ri.CurrencyEnglishName); Assert.Equal(currencyShortName, ri.CurrencyNativeName); 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 12af791d392e2..add21d8ce3f4d 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 @@ -120,10 +120,23 @@ private bool InitIcuCultureDataCore() realNameBuffer = string.Concat(realNameBuffer.AsSpan(0, index), ICU_COLLATION_KEYWORD, alternateSortName); } - // Get the locale name from ICU - if (!GetLocaleName(realNameBuffer, out _sWindowsName)) +#if TARGET_MACCATALYST || TARGET_IOS || TARGET_TVOS + if (GlobalizationMode.Hybrid) { - return false; // fail + _sWindowsName = GetLocaleNameNative(realNameBuffer); + if (_sWindowsName == null || _sWindowsName.Length == 0) + { + return false; + } + } + else +#endif + { + // Get the locale name from ICU + if (!GetLocaleName(realNameBuffer, out _sWindowsName)) + { + return false; + } } Debug.Assert(_sWindowsName != null); @@ -162,17 +175,27 @@ internal static unsafe bool GetLocaleName(string localeName, out string? windows internal static unsafe bool GetDefaultLocaleName([NotNullWhen(true)] out string? windowsName) { - // Get the default (system) locale name from ICU - char* buffer = stackalloc char[ICU_ULOC_FULLNAME_CAPACITY]; - if (!Interop.Globalization.GetDefaultLocaleName(buffer, ICU_ULOC_FULLNAME_CAPACITY)) +#if TARGET_MACCATALYST || TARGET_IOS || TARGET_TVOS + if (GlobalizationMode.Hybrid) { - windowsName = null; - return false; // fail + windowsName = Interop.Globalization.GetDefaultLocaleNameNative(); + return windowsName != null && windowsName.Length > 0; } + else +#endif + { + // Get the default (system) locale name from ICU + char* buffer = stackalloc char[ICU_ULOC_FULLNAME_CAPACITY]; + if (!Interop.Globalization.GetDefaultLocaleName(buffer, ICU_ULOC_FULLNAME_CAPACITY)) + { + windowsName = null; + return false; // fail + } - // Success - use the locale name returned which may be different than realNameBuffer (casing) - windowsName = new string(buffer); // the name passed to subsequent ICU calls - return true; + // Success - use the locale name returned which may be different than realNameBuffer (casing) + windowsName = new string(buffer); // the name passed to subsequent ICU calls + return true; + } } private string IcuGetLocaleInfo(LocaleStringData type, string? uiCultureName = null) @@ -287,7 +310,16 @@ private unsafe string IcuGetTimeFormatString(bool shortFormat) internal static bool IcuIsEnsurePredefinedLocaleName(string name) { Debug.Assert(!GlobalizationMode.UseNls); - return Interop.Globalization.IsPredefinedLocale(name); +#if TARGET_MACCATALYST || TARGET_IOS || TARGET_TVOS + if (GlobalizationMode.Hybrid) + { + return Interop.Globalization.IsPredefinedLocaleNative(name); + } + else +#endif + { + return Interop.Globalization.IsPredefinedLocale(name); + } } private static string ConvertIcuTimeFormatString(ReadOnlySpan icuFormatString) diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.Unix.cs index 533e97b3127d3..6291881ba96e0 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.Unix.cs @@ -7,12 +7,7 @@ namespace System.Globalization { internal sealed partial class CultureData { - private bool InitCultureDataCore() => -#if TARGET_MACCATALYST || TARGET_IOS || TARGET_TVOS - GlobalizationMode.Hybrid ? InitAppleCultureDataCore() : InitIcuCultureDataCore(); -#else - InitIcuCultureDataCore(); -#endif + private bool InitCultureDataCore() => InitIcuCultureDataCore(); // Unix doesn't support user overrides partial void InitUserOverride(bool useUserOverride); diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.iOS.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.iOS.cs index cdbdd65a81426..6e1b43bfd0898 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.iOS.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.iOS.cs @@ -9,20 +9,6 @@ internal sealed partial class CultureData { private const int LOC_FULLNAME_CAPACITY = 157; // max size of locale name - /// - /// 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. - /// - private bool InitAppleCultureDataCore() - { - Debug.Assert(_sRealName != null); - Debug.Assert(!GlobalizationMode.Invariant); - string realNameBuffer = _sRealName; - - _sWindowsName = _sName = _sRealName = GetLocaleNameNative(realNameBuffer); - return true; - } - internal static string GetLocaleNameNative(string localeName) { return Interop.Globalization.GetLocaleNameNative(localeName); diff --git a/src/mono/mono/mini/CMakeLists.txt b/src/mono/mono/mini/CMakeLists.txt index 98537044e3e63..931fb3ab15427 100644 --- a/src/mono/mono/mini/CMakeLists.txt +++ b/src/mono/mono/mini/CMakeLists.txt @@ -60,6 +60,7 @@ if(HAVE_SYS_ICU) pal_calendarData.c pal_casing.c pal_collation.c + pal_common.c pal_idna.c pal_locale.c pal_localeNumberData.c diff --git a/src/native/libs/System.Globalization.Native/CMakeLists.txt b/src/native/libs/System.Globalization.Native/CMakeLists.txt index 3996d9bb7e584..6e4dee71fe681 100644 --- a/src/native/libs/System.Globalization.Native/CMakeLists.txt +++ b/src/native/libs/System.Globalization.Native/CMakeLists.txt @@ -60,6 +60,7 @@ set(NATIVEGLOBALIZATION_SOURCES pal_calendarData.c pal_casing.c pal_collation.c + pal_common.c pal_idna.c pal_locale.c pal_localeNumberData.c diff --git a/src/native/libs/System.Globalization.Native/pal_casing.c b/src/native/libs/System.Globalization.Native/pal_casing.c index aad75f64da7d9..52c8986c76d1d 100644 --- a/src/native/libs/System.Globalization.Native/pal_casing.c +++ b/src/native/libs/System.Globalization.Native/pal_casing.c @@ -151,24 +151,6 @@ void GlobalizationNative_ChangeCaseTurkish( } } -void GlobalizationNative_InitOrdinalCasingPage(int32_t pageNumber, UChar* pTarget) -{ - pageNumber <<= 8; - for (int i = 0; i < 256; i++) - { - // Unfortunately, to ensure one-to-one simple mapping we have to call u_toupper on every character. - // Using string casing ICU APIs cannot give such results even when using NULL locale to force root behavior. - pTarget[i] = (UChar) u_toupper((UChar32)(pageNumber + i)); - } - - if (pageNumber == 0x0100) - { - // Disable Turkish I behavior on Ordinal operations - pTarget[0x31] = (UChar)0x0131; // Turkish lowercase i - pTarget[0x7F] = (UChar)0x017F; // // 017F;LATIN SMALL LETTER LONG S - } -} - #ifdef __clang__ #pragma clang diagnostic pop #endif diff --git a/src/native/libs/System.Globalization.Native/pal_common.c b/src/native/libs/System.Globalization.Native/pal_common.c new file mode 100644 index 0000000000000..5904a1c3bdbe2 --- /dev/null +++ b/src/native/libs/System.Globalization.Native/pal_common.c @@ -0,0 +1,21 @@ +#include +#include "pal_icushim_internal.h" +#include "pal_casing.h" + +void GlobalizationNative_InitOrdinalCasingPage(int32_t pageNumber, UChar* pTarget) +{ + pageNumber <<= 8; + for (int i = 0; i < 256; i++) + { + // Unfortunately, to ensure one-to-one simple mapping we have to call u_toupper on every character. + // Using string casing ICU APIs cannot give such results even when using NULL locale to force root behavior. + pTarget[i] = (UChar) u_toupper((UChar32)(pageNumber + i)); + } + + if (pageNumber == 0x0100) + { + // Disable Turkish I behavior on Ordinal operations + pTarget[0x31] = (UChar)0x0131; // Turkish lowercase i + pTarget[0x7F] = (UChar)0x017F; // // 017F;LATIN SMALL LETTER LONG S + } +} diff --git a/src/native/libs/System.Globalization.Native/pal_locale.h b/src/native/libs/System.Globalization.Native/pal_locale.h index 97d8933599a94..c3cb16ff2ee45 100644 --- a/src/native/libs/System.Globalization.Native/pal_locale.h +++ b/src/native/libs/System.Globalization.Native/pal_locale.h @@ -18,12 +18,16 @@ PALEXPORT int32_t GlobalizationNative_GetLocaleTimeFormat(const UChar* localeNam int32_t valueLength); #ifdef __APPLE__ +PALEXPORT const char* GlobalizationNative_GetDefaultLocaleNameNative(void); + PALEXPORT const char* GlobalizationNative_GetLocaleNameNative(const char* localeName); PALEXPORT const char* GlobalizationNative_GetLocaleTimeFormatNative(const char* localeName, int shortFormat); PALEXPORT int32_t GlobalizationNative_GetLocalesNative(UChar* locales, int32_t length); +PALEXPORT int32_t GlobalizationNative_IsPredefinedLocaleNative(const char* localeName); + /* ### Data tables **************************************************/ /** diff --git a/src/native/libs/System.Globalization.Native/pal_locale.m b/src/native/libs/System.Globalization.Native/pal_locale.m index b5acada569b01..fce8f03a4fc70 100644 --- a/src/native/libs/System.Globalization.Native/pal_locale.m +++ b/src/native/libs/System.Globalization.Native/pal_locale.m @@ -776,4 +776,49 @@ int32_t GlobalizationNative_GetLocalesNative(UChar* value, int32_t length) return strdup([dataPath UTF8String]); } } + +const char* GlobalizationNative_GetDefaultLocaleNameNative(void) +{ + @autoreleasepool + { + NSLocale *currentLocale = [NSLocale currentLocale]; + NSString *localeName = @""; + + if (!currentLocale) + { + return strdup([localeName UTF8String]); + } + + if ([currentLocale.languageCode length] > 0 && [currentLocale.countryCode length] > 0) + { + localeName = [NSString stringWithFormat:@"%@-%@", currentLocale.languageCode, currentLocale.countryCode]; + } + else + { + localeName = currentLocale.localeIdentifier; + } + + return strdup([localeName UTF8String]); + } +} + +// GlobalizationNative_IsPredefinedLocaleNative returns TRUE if localeName exists in availableLocaleIdentifiers. +// Otherwise it returns FALSE; + +int32_t GlobalizationNative_IsPredefinedLocaleNative(const char* localeName) +{ + @autoreleasepool + { + NSString *localeIdentifier = [NSString stringWithFormat:@"%s", localeName]; + NSString *localeIdentifierByRegionDesignator = [localeIdentifier stringByReplacingOccurrencesOfString:@"-" withString:@"_"]; + NSArray *availableLocales = [NSLocale availableLocaleIdentifiers]; + + if ([availableLocales containsObject:localeIdentifier] || [availableLocales containsObject:localeIdentifierByRegionDesignator]) + { + return true; + } + + return false; + } +} #endif