Skip to content

Commit eafd523

Browse files
authored
[OSX] HybridGlobalization implement locale native functions (#84417)
Implementations for native functions local int, grouping size and timeformat
1 parent 8cb3bf8 commit eafd523

File tree

13 files changed

+648
-64
lines changed

13 files changed

+648
-64
lines changed

src/libraries/Common/src/Interop/Interop.Locale.OSX.cs

+14-2
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,21 @@ internal static partial class Interop
88
internal static partial class Globalization
99
{
1010
[LibraryImport(Libraries.GlobalizationNative, EntryPoint = "GlobalizationNative_GetLocaleNameNative", StringMarshalling = StringMarshalling.Utf8)]
11-
internal static unsafe partial string GetLocaleNameNative(string localeName);
11+
internal static partial string GetLocaleNameNative(string localeName);
1212

1313
[LibraryImport(Libraries.GlobalizationNative, EntryPoint = "GlobalizationNative_GetLocaleInfoStringNative", StringMarshalling = StringMarshalling.Utf8)]
14-
internal static unsafe partial string GetLocaleInfoStringNative(string localeName, uint localeStringData);
14+
internal static partial string GetLocaleInfoStringNative(string localeName, uint localeStringData);
15+
16+
[LibraryImport(Libraries.GlobalizationNative, EntryPoint = "GlobalizationNative_GetLocaleInfoIntNative", StringMarshalling = StringMarshalling.Utf8)]
17+
internal static partial int GetLocaleInfoIntNative(string localeName, uint localeNumberData);
18+
19+
[LibraryImport(Libraries.GlobalizationNative, EntryPoint = "GlobalizationNative_GetLocaleInfoPrimaryGroupingSizeNative", StringMarshalling = StringMarshalling.Utf8)]
20+
internal static partial int GetLocaleInfoPrimaryGroupingSizeNative(string localeName, uint localeGroupingData);
21+
22+
[LibraryImport(Libraries.GlobalizationNative, EntryPoint = "GlobalizationNative_GetLocaleInfoSecondaryGroupingSizeNative", StringMarshalling = StringMarshalling.Utf8)]
23+
internal static partial int GetLocaleInfoSecondaryGroupingSizeNative(string localeName, uint localeGroupingData);
24+
25+
[LibraryImport(Libraries.GlobalizationNative, EntryPoint = "GlobalizationNative_GetLocaleTimeFormatNative", StringMarshalling = StringMarshalling.Utf8)]
26+
internal static partial string GetLocaleTimeFormatNative(string localeName, [MarshalAs(UnmanagedType.Bool)] bool shortFormat);
1527
}
1628
}
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,36 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22
<PropertyGroup>
3-
<TargetFrameworks>$(NetCoreAppCurrent)-ios;$(NetCoreAppCurrent)-tvos;$(NetCoreAppCurrent)-maccatalyst;$(NetCoreAppCurrent)-osx</TargetFrameworks>
3+
<TargetFrameworks>$(NetCoreAppCurrent)-ios;$(NetCoreAppCurrent)-tvos;$(NetCoreAppCurrent)-maccatalyst</TargetFrameworks>
44
<TestRuntime>true</TestRuntime>
55
<HybridGlobalization>true</HybridGlobalization>
66
</PropertyGroup>
77
<ItemGroup>
88
<Compile Include="HybridMode.cs" />
9+
<Compile Include="..\CultureInfo\CultureInfoNumberFormat.cs" />
10+
<Compile Include="..\CultureInfo\CultureInfoNames.cs" />
11+
<Compile Include="..\DateTimeFormatInfo\DateTimeFormatInfoData.cs" />
12+
<Compile Include="..\DateTimeFormatInfo\DateTimeFormatInfoFirstDayOfWeek.cs" />
13+
<Compile Include="..\DateTimeFormatInfo\DateTimeFormatInfoCalendarWeekRule.cs" />
14+
<Compile Include="..\NumberFormatInfo\NumberFormatInfoValidateParseStyle.cs" />
15+
<Compile Include="..\NumberFormatInfo\NumberFormatInfoData.cs" />
16+
<Compile Include="..\NumberFormatInfo\NumberFormatInfoCurrencySymbol.cs" />
17+
<Compile Include="..\NumberFormatInfo\NumberFormatInfoNaNSymbol.cs" />
18+
<Compile Include="..\NumberFormatInfo\NumberFormatInfoPercentGroupSeparator.cs" />
19+
<Compile Include="..\NumberFormatInfo\NumberFormatInfoPercentDecimalSeparator.cs" />
20+
<Compile Include="..\NumberFormatInfo\NumberFormatInfoPercentDecimalDigits.cs" />
21+
<Compile Include="..\NumberFormatInfo\NumberFormatInfoPerMilleSymbol.cs" />
22+
<Compile Include="..\NumberFormatInfo\NumberFormatInfoPercentSymbol.cs" />
23+
<Compile Include="..\NumberFormatInfo\NumberFormatInfoPositiveSign.cs" />
24+
<Compile Include="..\NumberFormatInfo\NumberFormatInfoPositiveInfinitySymbol.cs" />
25+
<Compile Include="..\NumberFormatInfo\NumberFormatInfoNegativeSign.cs" />
26+
<Compile Include="..\NumberFormatInfo\NumberFormatInfoNegativeInfinitySymbol.cs" />
27+
<Compile Include="..\NumberFormatInfo\NumberFormatInfoNumberGroupSeparator.cs" />
28+
<Compile Include="..\NumberFormatInfo\NumberFormatInfoNumberDecimalSeparator.cs" />
29+
<Compile Include="..\NumberFormatInfo\NumberFormatInfoNumberDecimalDigits.cs" />
30+
<Compile Include="..\NumberFormatInfo\NumberFormatInfoNumberGroupSizes.cs" />
31+
<Compile Include="..\NumberFormatInfo\NumberFormatInfoNumberNegativePattern.cs" />
32+
<Compile Include="..\NumberFormatInfo\NumberFormatInfoPercentNegativePattern.cs" />
33+
<Compile Include="..\NumberFormatInfo\NumberFormatInfoPercentGroupSizes.cs" />
34+
<Compile Include="..\NumberFormatInfo\NumberFormatInfoPercentPositivePattern.cs" />
935
</ItemGroup>
1036
</Project>

src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.OSX.cs

+106-4
Original file line numberDiff line numberDiff line change
@@ -7,21 +7,23 @@ namespace System.Globalization
77
{
88
internal sealed partial class CultureData
99
{
10+
private const int LOC_FULLNAME_CAPACITY = 157; // max size of locale name
11+
1012
/// <summary>
1113
/// This method uses the sRealName field (which is initialized by the constructor before this is called) to
1214
/// initialize the rest of the state of CultureData based on the underlying OS globalization library.
1315
/// </summary>
14-
private bool InitNativeCultureDataCore()
16+
private bool InitAppleCultureDataCore()
1517
{
1618
Debug.Assert(_sRealName != null);
1719
Debug.Assert(!GlobalizationMode.Invariant);
1820
string realNameBuffer = _sRealName;
1921

20-
_sWindowsName = GetLocaleNameNative(realNameBuffer);
22+
_sWindowsName = _sName = _sRealName = GetLocaleNameNative(realNameBuffer);
2123
return true;
2224
}
2325

24-
internal static unsafe string GetLocaleNameNative(string localeName)
26+
internal static string GetLocaleNameNative(string localeName)
2527
{
2628
return Interop.Globalization.GetLocaleNameNative(localeName);
2729
}
@@ -36,11 +38,111 @@ private string GetLocaleInfoNative(LocaleStringData type)
3638

3739
// For LOCALE_SPARENT we need the option of using the "real" name (forcing neutral names) instead of the
3840
// "windows" name, which can be specific for downlevel (< windows 7) os's.
39-
private static unsafe string GetLocaleInfoNative(string localeName, LocaleStringData type)
41+
private static string GetLocaleInfoNative(string localeName, LocaleStringData type)
4042
{
4143
Debug.Assert(localeName != null, "[CultureData.GetLocaleInfoNative] Expected localeName to be not be null");
4244

4345
return Interop.Globalization.GetLocaleInfoStringNative(localeName, (uint)type);
4446
}
47+
48+
private int GetLocaleInfoNative(LocaleNumberData type)
49+
{
50+
Debug.Assert(_sWindowsName != null, "[CultureData.GetLocaleInfoNative(LocaleNumberData)] Expected _sWindowsName to be populated already");
51+
52+
// returning 0 will cause the first supported calendar to be returned, which is the preferred calendar
53+
if (type == LocaleNumberData.CalendarType)
54+
return 0;
55+
56+
int value = Interop.Globalization.GetLocaleInfoIntNative(_sWindowsName, (uint)type);
57+
const int DEFAULT_VALUE = 0;
58+
if (value < 0)
59+
{
60+
Debug.Fail("[CultureData.GetLocaleInfoNative(LocaleNumberData)] failed");
61+
return DEFAULT_VALUE;
62+
}
63+
64+
return value;
65+
}
66+
67+
private int[] GetLocaleInfoNative(LocaleGroupingData type)
68+
{
69+
Debug.Assert(_sWindowsName != null, "[CultureData.GetLocaleInfoNative(LocaleGroupingData)] Expected _sWindowsName to be populated already");
70+
71+
int primaryGroupingSize = Interop.Globalization.GetLocaleInfoPrimaryGroupingSizeNative(_sWindowsName, (uint)type);
72+
int secondaryGroupingSize = Interop.Globalization.GetLocaleInfoSecondaryGroupingSizeNative(_sWindowsName, (uint)type);
73+
74+
if (secondaryGroupingSize == 0)
75+
{
76+
return new int[] { primaryGroupingSize };
77+
}
78+
79+
return new int[] { primaryGroupingSize, secondaryGroupingSize };
80+
}
81+
82+
private string GetTimeFormatStringNative() => GetTimeFormatStringNative(shortFormat: false);
83+
84+
private string GetTimeFormatStringNative(bool shortFormat)
85+
{
86+
Debug.Assert(_sWindowsName != null, "[CultureData.GetTimeFormatStringNative(bool shortFormat)] Expected _sWindowsName to be populated already");
87+
88+
string result = Interop.Globalization.GetLocaleTimeFormatNative(_sWindowsName, shortFormat);
89+
90+
return ConvertNativeTimeFormatString(result);
91+
}
92+
93+
private static string ConvertNativeTimeFormatString(string nativeFormatString)
94+
{
95+
Span<char> result = stackalloc char[LOC_FULLNAME_CAPACITY];
96+
97+
bool amPmAdded = false;
98+
int resultPos = 0;
99+
100+
for (int i = 0; i < nativeFormatString.Length; i++)
101+
{
102+
switch (nativeFormatString[i])
103+
{
104+
case '\'':
105+
result[resultPos++] = nativeFormatString[i++];
106+
while (i < nativeFormatString.Length)
107+
{
108+
char current = nativeFormatString[i];
109+
result[resultPos++] = current;
110+
if (current == '\'')
111+
{
112+
break;
113+
}
114+
i++;
115+
}
116+
break;
117+
118+
case ':':
119+
case '.':
120+
case 'H':
121+
case 'h':
122+
case 'm':
123+
case 's':
124+
result[resultPos++] = nativeFormatString[i];
125+
break;
126+
127+
case ' ':
128+
case '\u00A0':
129+
// Convert nonbreaking spaces into regular spaces
130+
result[resultPos++] = ' ';
131+
break;
132+
133+
case 'a': // AM/PM
134+
if (!amPmAdded)
135+
{
136+
amPmAdded = true;
137+
result[resultPos++] = 't';
138+
result[resultPos++] = 't';
139+
}
140+
break;
141+
142+
}
143+
}
144+
145+
return result.Slice(0, resultPos).ToString();
146+
}
45147
}
46148
}

src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.Unix.cs

+10-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,12 @@ namespace System.Globalization
77
{
88
internal sealed partial class CultureData
99
{
10-
private bool InitCultureDataCore() => InitIcuCultureDataCore();
10+
private bool InitCultureDataCore() =>
11+
#if TARGET_OSX || TARGET_MACCATALYST || TARGET_IOS || TARGET_TVOS
12+
GlobalizationMode.Hybrid ? InitAppleCultureDataCore() : InitIcuCultureDataCore();
13+
#else
14+
InitIcuCultureDataCore();
15+
#endif
1116

1217
// Unix doesn't support user overrides
1318
partial void InitUserOverride(bool useUserOverride);
@@ -20,7 +25,11 @@ internal sealed partial class CultureData
2025

2126
private string[]? GetTimeFormatsCore(bool shortFormat)
2227
{
28+
#if TARGET_OSX || TARGET_MACCATALYST || TARGET_IOS || TARGET_TVOS
29+
string format = GlobalizationMode.Hybrid ? GetTimeFormatStringNative(shortFormat) : IcuGetTimeFormatString(shortFormat);
30+
#else
2331
string format = IcuGetTimeFormatString(shortFormat);
32+
#endif
2433
return new string[] { format };
2534
}
2635

src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.cs

+20-2
Original file line numberDiff line numberDiff line change
@@ -1541,7 +1541,11 @@ internal int FirstDayOfWeek
15411541
{
15421542
if (_iFirstDayOfWeek == undef && !GlobalizationMode.Invariant)
15431543
{
1544+
#if TARGET_OSX || TARGET_MACCATALYST || TARGET_IOS || TARGET_TVOS
1545+
_iFirstDayOfWeek = GlobalizationMode.Hybrid ? GetLocaleInfoNative(LocaleNumberData.FirstDayOfWeek) : IcuGetLocaleInfo(LocaleNumberData.FirstDayOfWeek);
1546+
#else
15441547
_iFirstDayOfWeek = ShouldUseUserOverrideNlsData ? NlsGetFirstDayOfWeek() : IcuGetLocaleInfo(LocaleNumberData.FirstDayOfWeek);
1548+
#endif
15451549
}
15461550
return _iFirstDayOfWeek;
15471551
}
@@ -1949,7 +1953,11 @@ internal string TimeSeparator
19491953
}
19501954
else
19511955
{
1956+
#if TARGET_OSX || TARGET_MACCATALYST || TARGET_IOS || TARGET_TVOS
1957+
string? longTimeFormat = GlobalizationMode.Hybrid ? GetTimeFormatStringNative() : IcuGetTimeFormatString();
1958+
#else
19521959
string? longTimeFormat = ShouldUseUserOverrideNlsData ? NlsGetTimeFormatString() : IcuGetTimeFormatString();
1960+
#endif
19531961
if (string.IsNullOrEmpty(longTimeFormat))
19541962
{
19551963
longTimeFormat = LongTimes[0];
@@ -2285,17 +2293,23 @@ private int GetLocaleInfoCore(LocaleNumberData type)
22852293
// This is never reached but helps illinker statically remove dependencies
22862294
if (GlobalizationMode.Invariant)
22872295
return 0;
2288-
2296+
#if TARGET_OSX || TARGET_MACCATALYST || TARGET_IOS || TARGET_TVOS
2297+
return GlobalizationMode.Hybrid ? GetLocaleInfoNative(type) : IcuGetLocaleInfo(type);
2298+
#else
22892299
return GlobalizationMode.UseNls ? NlsGetLocaleInfo(type) : IcuGetLocaleInfo(type);
2300+
#endif
22902301
}
22912302

22922303
private int GetLocaleInfoCoreUserOverride(LocaleNumberData type)
22932304
{
22942305
// This is never reached but helps illinker statically remove dependencies
22952306
if (GlobalizationMode.Invariant)
22962307
return 0;
2297-
2308+
#if TARGET_OSX || TARGET_MACCATALYST || TARGET_IOS || TARGET_TVOS
2309+
return GlobalizationMode.Hybrid ? GetLocaleInfoNative(type) : IcuGetLocaleInfo(type);
2310+
#else
22982311
return ShouldUseUserOverrideNlsData ? NlsGetLocaleInfo(type) : IcuGetLocaleInfo(type);
2312+
#endif
22992313
}
23002314

23012315
private string GetLocaleInfoCoreUserOverride(LocaleStringData type)
@@ -2343,7 +2357,11 @@ private int[] GetLocaleInfoCoreUserOverride(LocaleGroupingData type)
23432357
if (GlobalizationMode.Invariant)
23442358
return null!;
23452359

2360+
#if TARGET_OSX || TARGET_MACCATALYST || TARGET_IOS || TARGET_TVOS
2361+
return GlobalizationMode.Hybrid ? GetLocaleInfoNative(type) : IcuGetLocaleInfo(type);
2362+
#else
23462363
return ShouldUseUserOverrideNlsData ? NlsGetLocaleInfo(type) : IcuGetLocaleInfo(type);
2364+
#endif
23472365
}
23482366

23492367
/// <remarks>

src/native/libs/System.Globalization.Native/entrypoints.c

+4
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,10 @@ static const Entry s_globalizationNative[] =
6161
#ifdef __APPLE__
6262
DllImportEntry(GlobalizationNative_GetLocaleNameNative)
6363
DllImportEntry(GlobalizationNative_GetLocaleInfoStringNative)
64+
DllImportEntry(GlobalizationNative_GetLocaleInfoIntNative)
65+
DllImportEntry(GlobalizationNative_GetLocaleInfoPrimaryGroupingSizeNative)
66+
DllImportEntry(GlobalizationNative_GetLocaleInfoSecondaryGroupingSizeNative)
67+
DllImportEntry(GlobalizationNative_GetLocaleTimeFormatNative)
6468
#endif
6569
};
6670

src/native/libs/System.Globalization.Native/pal_locale.c

+22
Original file line numberDiff line numberDiff line change
@@ -277,3 +277,25 @@ int32_t GlobalizationNative_IsPredefinedLocale(const UChar* localeName)
277277

278278
return err == U_ZERO_ERROR;
279279
}
280+
281+
/*
282+
PAL Function:
283+
GetLocaleTimeFormat
284+
285+
Obtains time format information (in ICU format, it needs to be converted to .NET's format).
286+
Returns 1 for success, 0 otherwise
287+
*/
288+
int32_t GlobalizationNative_GetLocaleTimeFormat(const UChar* localeName,
289+
int shortFormat,
290+
UChar* value,
291+
int32_t valueLength)
292+
{
293+
UErrorCode err = U_ZERO_ERROR;
294+
char locale[ULOC_FULLNAME_CAPACITY];
295+
GetLocale(localeName, locale, ULOC_FULLNAME_CAPACITY, false, &err);
296+
UDateFormatStyle style = (shortFormat != 0) ? UDAT_SHORT : UDAT_MEDIUM;
297+
UDateFormat* pFormat = udat_open(style, UDAT_NONE, locale, NULL, 0, NULL, 0, &err);
298+
udat_toPattern(pFormat, false, value, valueLength, &err);
299+
udat_close(pFormat);
300+
return UErrorCodeToBool(err);
301+
}

src/native/libs/System.Globalization.Native/pal_locale.h

+10-4
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,16 @@ PALEXPORT int32_t GlobalizationNative_GetLocales(UChar *value, int32_t valueLeng
99

1010
PALEXPORT int32_t GlobalizationNative_GetLocaleName(const UChar* localeName, UChar* value, int32_t valueLength);
1111

12-
#ifdef __APPLE__
13-
PALEXPORT const char* GlobalizationNative_GetLocaleNameNative(const char* localeName);
14-
#endif
15-
1612
PALEXPORT int32_t GlobalizationNative_GetDefaultLocaleName(UChar* value, int32_t valueLength);
1713

1814
PALEXPORT int32_t GlobalizationNative_IsPredefinedLocale(const UChar* localeName);
15+
16+
PALEXPORT int32_t GlobalizationNative_GetLocaleTimeFormat(const UChar* localeName,
17+
int shortFormat, UChar* value,
18+
int32_t valueLength);
19+
20+
#ifdef __APPLE__
21+
PALEXPORT const char* GlobalizationNative_GetLocaleNameNative(const char* localeName);
22+
23+
PALEXPORT const char* GlobalizationNative_GetLocaleTimeFormatNative(const char* localeName, int shortFormat);
24+
#endif

0 commit comments

Comments
 (0)