Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[OSX] HybridGlobalization implement locale native functions #84417

Merged
merged 18 commits into from
Apr 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 14 additions & 2 deletions src/libraries/Common/src/Interop/Interop.Locale.OSX.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,21 @@ internal static partial class Interop
internal static partial class Globalization
{
[LibraryImport(Libraries.GlobalizationNative, EntryPoint = "GlobalizationNative_GetLocaleNameNative", StringMarshalling = StringMarshalling.Utf8)]
internal static unsafe partial string GetLocaleNameNative(string localeName);
internal static partial string GetLocaleNameNative(string localeName);

[LibraryImport(Libraries.GlobalizationNative, EntryPoint = "GlobalizationNative_GetLocaleInfoStringNative", StringMarshalling = StringMarshalling.Utf8)]
internal static unsafe partial string GetLocaleInfoStringNative(string localeName, uint localeStringData);
internal static partial string GetLocaleInfoStringNative(string localeName, uint localeStringData);

[LibraryImport(Libraries.GlobalizationNative, EntryPoint = "GlobalizationNative_GetLocaleInfoIntNative", StringMarshalling = StringMarshalling.Utf8)]
internal static partial int GetLocaleInfoIntNative(string localeName, uint localeNumberData);

[LibraryImport(Libraries.GlobalizationNative, EntryPoint = "GlobalizationNative_GetLocaleInfoPrimaryGroupingSizeNative", StringMarshalling = StringMarshalling.Utf8)]
internal static partial int GetLocaleInfoPrimaryGroupingSizeNative(string localeName, uint localeGroupingData);

[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);
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,36 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>$(NetCoreAppCurrent)-ios;$(NetCoreAppCurrent)-tvos;$(NetCoreAppCurrent)-maccatalyst;$(NetCoreAppCurrent)-osx</TargetFrameworks>
<TargetFrameworks>$(NetCoreAppCurrent)-ios;$(NetCoreAppCurrent)-tvos;$(NetCoreAppCurrent)-maccatalyst</TargetFrameworks>
<TestRuntime>true</TestRuntime>
<HybridGlobalization>true</HybridGlobalization>
</PropertyGroup>
<ItemGroup>
<Compile Include="HybridMode.cs" />
<Compile Include="..\CultureInfo\CultureInfoNumberFormat.cs" />
<Compile Include="..\CultureInfo\CultureInfoNames.cs" />
<Compile Include="..\DateTimeFormatInfo\DateTimeFormatInfoData.cs" />
<Compile Include="..\DateTimeFormatInfo\DateTimeFormatInfoFirstDayOfWeek.cs" />
<Compile Include="..\DateTimeFormatInfo\DateTimeFormatInfoCalendarWeekRule.cs" />
<Compile Include="..\NumberFormatInfo\NumberFormatInfoValidateParseStyle.cs" />
<Compile Include="..\NumberFormatInfo\NumberFormatInfoData.cs" />
<Compile Include="..\NumberFormatInfo\NumberFormatInfoCurrencySymbol.cs" />
<Compile Include="..\NumberFormatInfo\NumberFormatInfoNaNSymbol.cs" />
<Compile Include="..\NumberFormatInfo\NumberFormatInfoPercentGroupSeparator.cs" />
<Compile Include="..\NumberFormatInfo\NumberFormatInfoPercentDecimalSeparator.cs" />
<Compile Include="..\NumberFormatInfo\NumberFormatInfoPercentDecimalDigits.cs" />
<Compile Include="..\NumberFormatInfo\NumberFormatInfoPerMilleSymbol.cs" />
<Compile Include="..\NumberFormatInfo\NumberFormatInfoPercentSymbol.cs" />
<Compile Include="..\NumberFormatInfo\NumberFormatInfoPositiveSign.cs" />
<Compile Include="..\NumberFormatInfo\NumberFormatInfoPositiveInfinitySymbol.cs" />
<Compile Include="..\NumberFormatInfo\NumberFormatInfoNegativeSign.cs" />
<Compile Include="..\NumberFormatInfo\NumberFormatInfoNegativeInfinitySymbol.cs" />
<Compile Include="..\NumberFormatInfo\NumberFormatInfoNumberGroupSeparator.cs" />
<Compile Include="..\NumberFormatInfo\NumberFormatInfoNumberDecimalSeparator.cs" />
<Compile Include="..\NumberFormatInfo\NumberFormatInfoNumberDecimalDigits.cs" />
<Compile Include="..\NumberFormatInfo\NumberFormatInfoNumberGroupSizes.cs" />
<Compile Include="..\NumberFormatInfo\NumberFormatInfoNumberNegativePattern.cs" />
<Compile Include="..\NumberFormatInfo\NumberFormatInfoPercentNegativePattern.cs" />
<Compile Include="..\NumberFormatInfo\NumberFormatInfoPercentGroupSizes.cs" />
<Compile Include="..\NumberFormatInfo\NumberFormatInfoPercentPositivePattern.cs" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,23 @@ namespace System.Globalization
{
internal sealed partial class CultureData
{
private const int LOC_FULLNAME_CAPACITY = 157; // max size of locale name

/// <summary>
/// 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 bool InitNativeCultureDataCore()
private bool InitAppleCultureDataCore()
{
Debug.Assert(_sRealName != null);
Debug.Assert(!GlobalizationMode.Invariant);
string realNameBuffer = _sRealName;

_sWindowsName = GetLocaleNameNative(realNameBuffer);
_sWindowsName = _sName = _sRealName = GetLocaleNameNative(realNameBuffer);
return true;
}

internal static unsafe string GetLocaleNameNative(string localeName)
internal static string GetLocaleNameNative(string localeName)
ilonatommy marked this conversation as resolved.
Show resolved Hide resolved
{
return Interop.Globalization.GetLocaleNameNative(localeName);
}
Expand All @@ -36,11 +38,111 @@ private string GetLocaleInfoNative(LocaleStringData 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 static unsafe string GetLocaleInfoNative(string localeName, LocaleStringData type)
private static string GetLocaleInfoNative(string localeName, LocaleStringData type)
{
Debug.Assert(localeName != null, "[CultureData.GetLocaleInfoNative] Expected localeName to be not be null");

return Interop.Globalization.GetLocaleInfoStringNative(localeName, (uint)type);
}

private int GetLocaleInfoNative(LocaleNumberData type)
{
Debug.Assert(_sWindowsName != null, "[CultureData.GetLocaleInfoNative(LocaleNumberData)] Expected _sWindowsName to be populated already");

// returning 0 will cause the first supported calendar to be returned, which is the preferred calendar
if (type == LocaleNumberData.CalendarType)
return 0;

int value = Interop.Globalization.GetLocaleInfoIntNative(_sWindowsName, (uint)type);
const int DEFAULT_VALUE = 0;
if (value < 0)
{
Debug.Fail("[CultureData.GetLocaleInfoNative(LocaleNumberData)] failed");
return DEFAULT_VALUE;
}

return value;
}

private int[] GetLocaleInfoNative(LocaleGroupingData type)
{
Debug.Assert(_sWindowsName != null, "[CultureData.GetLocaleInfoNative(LocaleGroupingData)] Expected _sWindowsName to be populated already");

int primaryGroupingSize = Interop.Globalization.GetLocaleInfoPrimaryGroupingSizeNative(_sWindowsName, (uint)type);
int secondaryGroupingSize = Interop.Globalization.GetLocaleInfoSecondaryGroupingSizeNative(_sWindowsName, (uint)type);

if (secondaryGroupingSize == 0)
{
return new int[] { primaryGroupingSize };
}

return new int[] { primaryGroupingSize, secondaryGroupingSize };
}

private string GetTimeFormatStringNative() => GetTimeFormatStringNative(shortFormat: false);

private string GetTimeFormatStringNative(bool shortFormat)
{
Debug.Assert(_sWindowsName != null, "[CultureData.GetTimeFormatStringNative(bool shortFormat)] Expected _sWindowsName to be populated already");

string result = Interop.Globalization.GetLocaleTimeFormatNative(_sWindowsName, shortFormat);

return ConvertNativeTimeFormatString(result);
}

private static string ConvertNativeTimeFormatString(string nativeFormatString)
{
Span<char> result = stackalloc char[LOC_FULLNAME_CAPACITY];

bool amPmAdded = false;
int resultPos = 0;

for (int i = 0; i < nativeFormatString.Length; i++)
{
switch (nativeFormatString[i])
{
case '\'':
result[resultPos++] = nativeFormatString[i++];
while (i < nativeFormatString.Length)
{
char current = nativeFormatString[i];
result[resultPos++] = current;
if (current == '\'')
{
break;
}
i++;
}
break;

case ':':
case '.':
case 'H':
case 'h':
case 'm':
case 's':
result[resultPos++] = nativeFormatString[i];
break;

case ' ':
case '\u00A0':
// Convert nonbreaking spaces into regular spaces
result[resultPos++] = ' ';
break;

case 'a': // AM/PM
if (!amPmAdded)
{
amPmAdded = true;
result[resultPos++] = 't';
result[resultPos++] = 't';
}
break;

}
}

return result.Slice(0, resultPos).ToString();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,12 @@ namespace System.Globalization
{
internal sealed partial class CultureData
{
private bool InitCultureDataCore() => InitIcuCultureDataCore();
private bool InitCultureDataCore() =>
#if TARGET_OSX || TARGET_MACCATALYST || TARGET_IOS || TARGET_TVOS
GlobalizationMode.Hybrid ? InitAppleCultureDataCore() : InitIcuCultureDataCore();
#else
InitIcuCultureDataCore();
#endif

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

private string[]? GetTimeFormatsCore(bool shortFormat)
{
#if TARGET_OSX || TARGET_MACCATALYST || TARGET_IOS || TARGET_TVOS
string format = GlobalizationMode.Hybrid ? GetTimeFormatStringNative(shortFormat) : IcuGetTimeFormatString(shortFormat);
#else
string format = IcuGetTimeFormatString(shortFormat);
#endif
return new string[] { format };
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1541,7 +1541,11 @@ internal int FirstDayOfWeek
{
if (_iFirstDayOfWeek == undef && !GlobalizationMode.Invariant)
{
#if TARGET_OSX || TARGET_MACCATALYST || TARGET_IOS || TARGET_TVOS
_iFirstDayOfWeek = GlobalizationMode.Hybrid ? GetLocaleInfoNative(LocaleNumberData.FirstDayOfWeek) : IcuGetLocaleInfo(LocaleNumberData.FirstDayOfWeek);
#else
_iFirstDayOfWeek = ShouldUseUserOverrideNlsData ? NlsGetFirstDayOfWeek() : IcuGetLocaleInfo(LocaleNumberData.FirstDayOfWeek);
#endif
}
return _iFirstDayOfWeek;
}
Expand Down Expand Up @@ -1949,7 +1953,11 @@ internal string TimeSeparator
}
else
{
#if TARGET_OSX || TARGET_MACCATALYST || TARGET_IOS || TARGET_TVOS
string? longTimeFormat = GlobalizationMode.Hybrid ? GetTimeFormatStringNative() : IcuGetTimeFormatString();
#else
string? longTimeFormat = ShouldUseUserOverrideNlsData ? NlsGetTimeFormatString() : IcuGetTimeFormatString();
#endif
if (string.IsNullOrEmpty(longTimeFormat))
{
longTimeFormat = LongTimes[0];
Expand Down Expand Up @@ -2285,17 +2293,23 @@ private int GetLocaleInfoCore(LocaleNumberData type)
// This is never reached but helps illinker statically remove dependencies
if (GlobalizationMode.Invariant)
return 0;

#if TARGET_OSX || TARGET_MACCATALYST || TARGET_IOS || TARGET_TVOS
return GlobalizationMode.Hybrid ? GetLocaleInfoNative(type) : IcuGetLocaleInfo(type);
#else
return GlobalizationMode.UseNls ? NlsGetLocaleInfo(type) : IcuGetLocaleInfo(type);
#endif
}

private int GetLocaleInfoCoreUserOverride(LocaleNumberData type)
{
// This is never reached but helps illinker statically remove dependencies
if (GlobalizationMode.Invariant)
return 0;

#if TARGET_OSX || TARGET_MACCATALYST || TARGET_IOS || TARGET_TVOS
return GlobalizationMode.Hybrid ? GetLocaleInfoNative(type) : IcuGetLocaleInfo(type);
#else
return ShouldUseUserOverrideNlsData ? NlsGetLocaleInfo(type) : IcuGetLocaleInfo(type);
#endif
}

private string GetLocaleInfoCoreUserOverride(LocaleStringData type)
Expand Down Expand Up @@ -2343,7 +2357,11 @@ private int[] GetLocaleInfoCoreUserOverride(LocaleGroupingData type)
if (GlobalizationMode.Invariant)
return null!;

#if TARGET_OSX || TARGET_MACCATALYST || TARGET_IOS || TARGET_TVOS
return GlobalizationMode.Hybrid ? GetLocaleInfoNative(type) : IcuGetLocaleInfo(type);
#else
return ShouldUseUserOverrideNlsData ? NlsGetLocaleInfo(type) : IcuGetLocaleInfo(type);
#endif
}

/// <remarks>
Expand Down
4 changes: 4 additions & 0 deletions src/native/libs/System.Globalization.Native/entrypoints.c
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,10 @@ static const Entry s_globalizationNative[] =
#ifdef __APPLE__
DllImportEntry(GlobalizationNative_GetLocaleNameNative)
DllImportEntry(GlobalizationNative_GetLocaleInfoStringNative)
DllImportEntry(GlobalizationNative_GetLocaleInfoIntNative)
DllImportEntry(GlobalizationNative_GetLocaleInfoPrimaryGroupingSizeNative)
DllImportEntry(GlobalizationNative_GetLocaleInfoSecondaryGroupingSizeNative)
DllImportEntry(GlobalizationNative_GetLocaleTimeFormatNative)
#endif
};

Expand Down
22 changes: 22 additions & 0 deletions src/native/libs/System.Globalization.Native/pal_locale.c
Original file line number Diff line number Diff line change
Expand Up @@ -277,3 +277,25 @@ int32_t GlobalizationNative_IsPredefinedLocale(const UChar* localeName)

return err == U_ZERO_ERROR;
}

/*
PAL Function:
GetLocaleTimeFormat

Obtains time format information (in ICU format, it needs to be converted to .NET's format).
Returns 1 for success, 0 otherwise
*/
int32_t GlobalizationNative_GetLocaleTimeFormat(const UChar* localeName,
int shortFormat,
UChar* value,
int32_t valueLength)
{
UErrorCode err = U_ZERO_ERROR;
char locale[ULOC_FULLNAME_CAPACITY];
GetLocale(localeName, locale, ULOC_FULLNAME_CAPACITY, false, &err);
UDateFormatStyle style = (shortFormat != 0) ? UDAT_SHORT : UDAT_MEDIUM;
UDateFormat* pFormat = udat_open(style, UDAT_NONE, locale, NULL, 0, NULL, 0, &err);
udat_toPattern(pFormat, false, value, valueLength, &err);
udat_close(pFormat);
return UErrorCodeToBool(err);
}
14 changes: 10 additions & 4 deletions src/native/libs/System.Globalization.Native/pal_locale.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,16 @@ PALEXPORT int32_t GlobalizationNative_GetLocales(UChar *value, int32_t valueLeng

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

#ifdef __APPLE__
PALEXPORT const char* GlobalizationNative_GetLocaleNameNative(const char* localeName);
#endif

PALEXPORT int32_t GlobalizationNative_GetDefaultLocaleName(UChar* value, int32_t valueLength);

PALEXPORT int32_t GlobalizationNative_IsPredefinedLocale(const UChar* localeName);

PALEXPORT int32_t GlobalizationNative_GetLocaleTimeFormat(const UChar* localeName,
int shortFormat, UChar* value,
int32_t valueLength);

#ifdef __APPLE__
PALEXPORT const char* GlobalizationNative_GetLocaleNameNative(const char* localeName);

PALEXPORT const char* GlobalizationNative_GetLocaleTimeFormatNative(const char* localeName, int shortFormat);
#endif
Loading