diff --git a/src/libraries/Common/src/Interop/Interop.Locale.OSX.cs b/src/libraries/Common/src/Interop/Interop.Locale.OSX.cs
index 52882346b63222..0f7b1763d58509 100644
--- a/src/libraries/Common/src/Interop/Interop.Locale.OSX.cs
+++ b/src/libraries/Common/src/Interop/Interop.Locale.OSX.cs
@@ -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);
}
}
diff --git a/src/libraries/System.Globalization/tests/Hybrid/Hybrid.IOS.Tests.csproj b/src/libraries/System.Globalization/tests/Hybrid/Hybrid.IOS.Tests.csproj
index b2622d1a6afb95..b9887c1021aaf4 100644
--- a/src/libraries/System.Globalization/tests/Hybrid/Hybrid.IOS.Tests.csproj
+++ b/src/libraries/System.Globalization/tests/Hybrid/Hybrid.IOS.Tests.csproj
@@ -1,10 +1,36 @@
- $(NetCoreAppCurrent)-ios;$(NetCoreAppCurrent)-tvos;$(NetCoreAppCurrent)-maccatalyst;$(NetCoreAppCurrent)-osx
+ $(NetCoreAppCurrent)-ios;$(NetCoreAppCurrent)-tvos;$(NetCoreAppCurrent)-maccatalyst
true
true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.OSX.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.OSX.cs
index 870d8504fb9678..cdbdd65a81426f 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.OSX.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.OSX.cs
@@ -7,21 +7,23 @@ namespace System.Globalization
{
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 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)
{
return Interop.Globalization.GetLocaleNameNative(localeName);
}
@@ -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 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();
+ }
}
}
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 2a253d5367be30..76c009beb6466f 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,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);
@@ -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 };
}
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 92037366b63891..12ea4ded8beedd 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.cs
@@ -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;
}
@@ -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];
@@ -2285,8 +2293,11 @@ 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)
@@ -2294,8 +2305,11 @@ 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)
@@ -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
}
///
diff --git a/src/native/libs/System.Globalization.Native/entrypoints.c b/src/native/libs/System.Globalization.Native/entrypoints.c
index 10254d6256db4a..c3c65b2196cde9 100644
--- a/src/native/libs/System.Globalization.Native/entrypoints.c
+++ b/src/native/libs/System.Globalization.Native/entrypoints.c
@@ -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
};
diff --git a/src/native/libs/System.Globalization.Native/pal_locale.c b/src/native/libs/System.Globalization.Native/pal_locale.c
index eada9f12ddd39f..ccae3e0c6d989f 100644
--- a/src/native/libs/System.Globalization.Native/pal_locale.c
+++ b/src/native/libs/System.Globalization.Native/pal_locale.c
@@ -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);
+}
diff --git a/src/native/libs/System.Globalization.Native/pal_locale.h b/src/native/libs/System.Globalization.Native/pal_locale.h
index 7ee6d1e7ee56e1..7fe89f667f2132 100644
--- a/src/native/libs/System.Globalization.Native/pal_locale.h
+++ b/src/native/libs/System.Globalization.Native/pal_locale.h
@@ -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
diff --git a/src/native/libs/System.Globalization.Native/pal_locale.m b/src/native/libs/System.Globalization.Native/pal_locale.m
index 7400c48b1c9ba6..66b951db76125c 100644
--- a/src/native/libs/System.Globalization.Native/pal_locale.m
+++ b/src/native/libs/System.Globalization.Native/pal_locale.m
@@ -4,6 +4,7 @@
#include
#include "pal_locale_internal.h"
#include "pal_localeStringData.h"
+#include "pal_localeNumberData.h"
#import
#import
@@ -34,10 +35,10 @@
const char* GlobalizationNative_GetLocaleNameNative(const char* localeName)
{
- NSString *locName = [NSString stringWithFormat:@"%s", localeName];
- NSLocale *currentLocale = [[NSLocale alloc] initWithLocaleIdentifier:locName];
- const char* value = [currentLocale.localeIdentifier UTF8String];
- return strdup(value);
+ NSString *locName = [NSString stringWithFormat:@"%s", localeName];
+ NSLocale *currentLocale = [[NSLocale alloc] initWithLocaleIdentifier:locName];
+ const char* value = [currentLocale.localeIdentifier UTF8String];
+ return strdup(value);
}
const char* GlobalizationNative_GetLocaleInfoStringNative(const char* localeName, LocaleStringData localeStringData)
@@ -51,7 +52,6 @@
[dateFormatter setLocale:currentLocale];
NSLocale *gbLocale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_GB"];
-
switch (localeStringData)
{
///// localized name of locale, eg "German (Germany)" in UI language (corresponds to LOCALE_SLOCALIZEDDISPLAYNAME)
@@ -148,9 +148,426 @@
value = "";
break;
}
-
return strdup(value);
}
+
+// invariant character definitions
+#define CHAR_CURRENCY ((char)0x00A4) // international currency
+#define CHAR_SPACE ((char)0x0020) // space
+#define CHAR_NBSPACE ((char)0x00A0) // no-break space
+#define CHAR_DIGIT ((char)0x0023) // '#'
+#define CHAR_MINUS ((char)0x002D) // '-'
+#define CHAR_PERCENT ((char)0x0025) // '%'
+#define CHAR_OPENPAREN ((char)0x0028) // '('
+#define CHAR_CLOSEPAREN ((char)0x0029) // ')'
+#define CHAR_ZERO ((char)0x0030) // '0'
+
+/*
+Function:
+NormalizeNumericPattern
+
+Returns a numeric string pattern in a format that we can match against the
+appropriate managed pattern. Examples:
+For PositiveMonetaryNumberFormat "ยค#,##0.00" becomes "Cn"
+For NegativeNumberFormat "#,##0.00;(#,##0.00)" becomes "(n)"
+*/
+static char* NormalizeNumericPattern(const char* srcPattern, int isNegative)
+{
+ int iStart = 0;
+ int iEnd = strlen(srcPattern);
+
+ // ';' separates positive and negative subpatterns.
+ // When there is no explicit negative subpattern,
+ // an implicit negative subpattern is formed from the positive pattern with a prefixed '-'.
+ char * ptrNegativePattern = strrchr(srcPattern,';');
+ if (ptrNegativePattern)
+ {
+ int32_t iNegativePatternStart = ptrNegativePattern - srcPattern;
+ if (isNegative)
+ {
+ iStart = iNegativePatternStart + 1;
+ }
+ else
+ {
+ iEnd = iNegativePatternStart - 1;
+ }
+ }
+
+ int minusAdded = false;
+
+ for (int i = iStart; i <= iEnd; i++)
+ {
+ switch (srcPattern[i])
+ {
+ case CHAR_MINUS:
+ case CHAR_OPENPAREN:
+ case CHAR_CLOSEPAREN:
+ minusAdded = true;
+ break;
+ }
+
+ if (minusAdded)
+ break;
+ }
+
+ // international currency symbol (CHAR_CURRENCY)
+ // The positive pattern comes first, then an optional negative pattern
+ // separated by a semicolon
+ // A destPattern example: "(C n)" where C represents the currency symbol, and
+ // n is the number
+ char* destPattern;
+ int index = 0;
+
+ // if there is no negative subpattern, prefix the minus sign
+ if (isNegative && !minusAdded)
+ {
+ int length = (iEnd - iStart) + 2;
+ destPattern = (char*)calloc((size_t)length, sizeof(char));
+ if (!destPattern)
+ {
+ return NULL;
+ }
+ destPattern[index++] = '-';
+ }
+ else
+ {
+ int length = (iEnd - iStart) + 1;
+ destPattern = (char*)calloc((size_t)length, sizeof(char));
+ if (!destPattern)
+ {
+ return NULL;
+ }
+ }
+
+ int digitAdded = false;
+ int currencyAdded = false;
+ int spaceAdded = false;
+
+ for (int i = iStart; i <= iEnd; i++)
+ {
+ char ch = srcPattern[i];
+ switch (ch)
+ {
+ case CHAR_DIGIT:
+ case CHAR_ZERO:
+ if (!digitAdded)
+ {
+ digitAdded = true;
+ destPattern[index++] = 'n';
+ }
+ break;
+
+ case CHAR_CURRENCY:
+ if (!currencyAdded)
+ {
+ currencyAdded = true;
+ destPattern[index++] = 'C';
+ }
+ break;
+
+ case CHAR_SPACE:
+ case CHAR_NBSPACE:
+ if (!spaceAdded)
+ {
+ spaceAdded = true;
+ destPattern[index++] = ' ';
+ }
+ break;
+
+ case CHAR_MINUS:
+ case CHAR_OPENPAREN:
+ case CHAR_CLOSEPAREN:
+ case CHAR_PERCENT:
+ destPattern[index++] = ch;
+ break;
+ }
+ }
+
+ const int MAX_DOTNET_NUMERIC_PATTERN_LENGTH = 6; // example: "(C n)" plus terminator
+
+ if (destPattern[0] == '\0' || strlen (destPattern) >= MAX_DOTNET_NUMERIC_PATTERN_LENGTH)
+ {
+ free (destPattern);
+ return NULL;
+ }
+
+ return destPattern;
+}
+
+/*
+Function:
+GetNumericPattern
+
+Determines the pattern from the decimalFormat and returns the matching pattern's
+index from patterns[].
+Returns index -1 if no pattern is found.
+*/
+static int GetPatternIndex(char* normalizedPattern,const char* patterns[], int patternsCount)
+{
+ const int INVALID_FORMAT = -1;
+
+ if (!normalizedPattern)
+ {
+ return INVALID_FORMAT;
+ }
+
+ for (int i = 0; i < patternsCount; i++)
+ {
+ if (strcmp(normalizedPattern, patterns[i]) == 0)
+ {
+ free(normalizedPattern);
+ return i;
+ }
+ }
+
+ assert(false); // should have found a valid pattern
+
+ free(normalizedPattern);
+ return INVALID_FORMAT;
+}
+
+static int32_t GetValueForNumberFormat(NSLocale *currentLocale, LocaleNumberData localeNumberData)
+{
+ NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init];
+ numberFormatter.locale = currentLocale;
+ const char *pFormat;
+ int32_t value;
+
+ switch(localeNumberData)
+ {
+ case LocaleNumber_PositiveMonetaryNumberFormat:
+ {
+ numberFormatter.numberStyle = NSNumberFormatterCurrencyStyle;
+ static const char* Patterns[] = {"Cn", "nC", "C n", "n C"};
+ pFormat = [[numberFormatter positiveFormat] UTF8String];
+ char* normalizedPattern = NormalizeNumericPattern(pFormat, false);
+ value = GetPatternIndex(normalizedPattern, Patterns, sizeof(Patterns)/sizeof(Patterns[0]));
+ break;
+ }
+ case LocaleNumber_NegativeMonetaryNumberFormat:
+ {
+ numberFormatter.numberStyle = NSNumberFormatterCurrencyStyle;
+ static const char* Patterns[] = {"(Cn)", "-Cn", "C-n", "Cn-", "(nC)", "-nC", "n-C", "nC-", "-n C",
+ "-C n", "n C-", "C n-", "C -n", "n- C", "(C n)", "(n C)", "C- n" };
+ pFormat = [[numberFormatter negativeFormat] UTF8String];
+ char* normalizedPattern = NormalizeNumericPattern(pFormat, true);
+ value = GetPatternIndex(normalizedPattern, Patterns, sizeof(Patterns)/sizeof(Patterns[0]));
+ break;
+ }
+ case LocaleNumber_NegativeNumberFormat:
+ {
+ numberFormatter.numberStyle = NSNumberFormatterDecimalStyle;
+ static const char* Patterns[] = {"(n)", "-n", "- n", "n-", "n -"};
+ pFormat = [[numberFormatter negativeFormat] UTF8String];
+ char* normalizedPattern = NormalizeNumericPattern(pFormat, true);
+ value = GetPatternIndex(normalizedPattern, Patterns, sizeof(Patterns)/sizeof(Patterns[0]));
+ break;
+ }
+ case LocaleNumber_NegativePercentFormat:
+ {
+ numberFormatter.numberStyle = NSNumberFormatterPercentStyle;
+ static const char* Patterns[] = {"-n %", "-n%", "-%n", "%-n", "%n-", "n-%", "n%-", "-% n", "n %-", "% n-", "% -n", "n- %"};
+ pFormat = [[numberFormatter negativeFormat] UTF8String];
+ char* normalizedPattern = NormalizeNumericPattern(pFormat, true);
+ value = GetPatternIndex(normalizedPattern, Patterns, sizeof(Patterns)/sizeof(Patterns[0]));
+ break;
+ }
+ case LocaleNumber_PositivePercentFormat:
+ {
+ numberFormatter.numberStyle = NSNumberFormatterPercentStyle;
+ static const char* Patterns[] = {"n %", "n%", "%n", "% n"};
+ pFormat = [[numberFormatter positiveFormat] UTF8String];
+ char* normalizedPattern = NormalizeNumericPattern(pFormat, false);
+ value = GetPatternIndex(normalizedPattern, Patterns, sizeof(Patterns)/sizeof(Patterns[0]));
+ break;
+ }
+ default:
+ return -1;
+ }
+
+ return value;
+}
+
+int32_t GlobalizationNative_GetLocaleInfoIntNative(const char* localeName, LocaleNumberData localeNumberData)
+{
+ bool isSuccess = true;
+ int32_t value;
+ NSString *locName = [NSString stringWithFormat:@"%s", localeName];
+ NSLocale *currentLocale = [[NSLocale alloc] initWithLocaleIdentifier:locName];
+
+ switch (localeNumberData)
+ {
+ case LocaleNumber_MeasurementSystem:
+ {
+ const char *measurementSystem = [[currentLocale objectForKey:NSLocaleMeasurementSystem] UTF8String];
+ NSLocale *usLocale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US"];
+ const char *us_measurementSystem = [[usLocale objectForKey:NSLocaleMeasurementSystem] UTF8String];
+ value = (measurementSystem == us_measurementSystem) ? 1 : 0;
+ break;
+ }
+ case LocaleNumber_FractionalDigitsCount:
+ {
+ NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init];
+ numberFormatter.locale = currentLocale;
+ numberFormatter.numberStyle = NSNumberFormatterDecimalStyle;
+ value = (int32_t)numberFormatter.maximumFractionDigits;
+ break;
+ }
+ case LocaleNumber_MonetaryFractionalDigitsCount:
+ {
+ NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init];
+ numberFormatter.locale = currentLocale;
+ numberFormatter.numberStyle = NSNumberFormatterCurrencyStyle;
+ value = (int32_t)numberFormatter.maximumFractionDigits;
+ break;
+ }
+ case LocaleNumber_PositiveMonetaryNumberFormat:
+ case LocaleNumber_NegativeMonetaryNumberFormat:
+ case LocaleNumber_NegativeNumberFormat:
+ case LocaleNumber_NegativePercentFormat:
+ case LocaleNumber_PositivePercentFormat:
+ {
+ value = GetValueForNumberFormat(currentLocale, localeNumberData);
+ if (value < 0)
+ {
+ isSuccess = false;
+ }
+ break;
+ }
+ case LocaleNumber_FirstWeekOfYear:
+ {
+ NSCalendar *calendar = [currentLocale objectForKey:NSLocaleCalendar];
+ int minDaysInWeek = (int32_t)[calendar minimumDaysInFirstWeek];
+ if (minDaysInWeek == 1)
+ {
+ value = WeekRule_FirstDay;
+ }
+ else if (minDaysInWeek == 7)
+ {
+ value = WeekRule_FirstFullWeek;
+ }
+ else if (minDaysInWeek >= 4)
+ {
+ value = WeekRule_FirstFourDayWeek;
+ }
+ else
+ {
+ value = -1;
+ isSuccess = false;
+ }
+ break;
+ }
+ case LocaleNumber_ReadingLayout:
+ {
+ NSLocaleLanguageDirection langDir = [NSLocale characterDirectionForLanguage:[[NSLocale currentLocale] objectForKey:NSLocaleLanguageCode]];
+ // 0 - Left to right (such as en-US)
+ // 1 - Right to left (such as arabic locales)
+ value = NSLocaleLanguageDirectionRightToLeft == langDir ? 1 : 0;
+ break;
+ }
+ case LocaleNumber_FirstDayofWeek:
+ {
+ NSCalendar *calendar = [currentLocale objectForKey:NSLocaleCalendar];
+ value = [calendar firstWeekday] - 1; // .NET is 0-based and in Apple is 1-based;
+ break;
+ }
+ default:
+ value = -1;
+ isSuccess = false;
+ break;
+ }
+
+ assert(isSuccess);
+
+ return value;
+}
+
+/*
+PAL Function:
+GlobalizationNative_GetLocaleInfoPrimaryGroupingSizeNative
+
+Returns primary grouping size for decimal and currency
+*/
+int32_t GlobalizationNative_GetLocaleInfoPrimaryGroupingSizeNative(const char* localeName, LocaleNumberData localeGroupingData)
+{
+ bool isSuccess = true;
+ NSString *locName = [NSString stringWithFormat:@"%s", localeName];
+ NSLocale *currentLocale = [[NSLocale alloc] initWithLocaleIdentifier:locName];
+ NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init];
+ numberFormatter.locale = currentLocale;
+
+ switch (localeGroupingData)
+ {
+ case LocaleNumber_Digit:
+ numberFormatter.numberStyle = NSNumberFormatterDecimalStyle;
+ break;
+ case LocaleNumber_Monetary:
+ numberFormatter.numberStyle = NSNumberFormatterCurrencyStyle;
+ break;
+ default:
+ isSuccess = false;
+ assert(isSuccess);
+ break;
+ }
+ return [numberFormatter groupingSize];
+}
+
+/*
+PAL Function:
+GlobalizationNative_GetLocaleInfoSecondaryGroupingSizeNative
+
+Returns secondary grouping size for decimal and currency
+*/
+int32_t GlobalizationNative_GetLocaleInfoSecondaryGroupingSizeNative(const char* localeName, LocaleNumberData localeGroupingData)
+{
+ bool isSuccess = true;
+ NSString *locName = [NSString stringWithFormat:@"%s", localeName];
+ NSLocale *currentLocale = [[NSLocale alloc] initWithLocaleIdentifier:locName];
+ NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init];
+ numberFormatter.locale = currentLocale;
+
+ switch (localeGroupingData)
+ {
+ case LocaleNumber_Digit:
+ numberFormatter.numberStyle = NSNumberFormatterDecimalStyle;
+ break;
+ case LocaleNumber_Monetary:
+ numberFormatter.numberStyle = NSNumberFormatterCurrencyStyle;
+ break;
+ default:
+ isSuccess = false;
+ assert(isSuccess);
+ break;
+ }
+
+ return [numberFormatter secondaryGroupingSize];
+}
+
+/*
+PAL Function:
+GlobalizationNative_GetLocaleTimeFormatNative
+
+Returns time format information (in native format, it needs to be converted to .NET's format).
+*/
+const char* GlobalizationNative_GetLocaleTimeFormatNative(const char* localeName, int shortFormat)
+{
+ NSString *locName = [NSString stringWithFormat:@"%s", localeName];
+ NSLocale *currentLocale = [[NSLocale alloc] initWithLocaleIdentifier:locName];
+ NSDateFormatter* dateFormatter = [[NSDateFormatter alloc] init];
+ [dateFormatter setLocale:currentLocale];
+
+ if (shortFormat != 0)
+ {
+ [dateFormatter setTimeStyle:NSDateFormatterShortStyle];
+ }
+ else
+ {
+ [dateFormatter setTimeStyle:NSDateFormatterMediumStyle];
+ }
+
+ return strdup([[dateFormatter dateFormat] UTF8String]);
+}
+
#endif
#if defined(TARGET_MACCATALYST) || defined(TARGET_IOS) || defined(TARGET_TVOS)
diff --git a/src/native/libs/System.Globalization.Native/pal_localeNumberData.h b/src/native/libs/System.Globalization.Native/pal_localeNumberData.h
index db5c401b8b7b60..a68f1e32e334ab 100644
--- a/src/native/libs/System.Globalization.Native/pal_localeNumberData.h
+++ b/src/native/libs/System.Globalization.Native/pal_localeNumberData.h
@@ -43,3 +43,14 @@ PALEXPORT int32_t GlobalizationNative_GetLocaleInfoGroupingSizes(const UChar* lo
LocaleNumberData localeGroupingData,
int32_t* primaryGroupSize,
int32_t* secondaryGroupSize);
+
+#ifdef __APPLE__
+PALEXPORT int32_t GlobalizationNative_GetLocaleInfoIntNative(const char* localeName,
+ LocaleNumberData localeNumberData);
+
+PALEXPORT int32_t GlobalizationNative_GetLocaleInfoPrimaryGroupingSizeNative(const char* localeName,
+ LocaleNumberData localeGroupingData);
+
+PALEXPORT int32_t GlobalizationNative_GetLocaleInfoSecondaryGroupingSizeNative(const char* localeName,
+ LocaleNumberData localeGroupingData);
+#endif
diff --git a/src/native/libs/System.Globalization.Native/pal_localeStringData.c b/src/native/libs/System.Globalization.Native/pal_localeStringData.c
index 9120b89bd329bd..488cbc8cb35f2a 100644
--- a/src/native/libs/System.Globalization.Native/pal_localeStringData.c
+++ b/src/native/libs/System.Globalization.Native/pal_localeStringData.c
@@ -401,24 +401,3 @@ int32_t GlobalizationNative_GetLocaleInfoString(const UChar* localeName,
return UErrorCodeToBool(status);
}
-/*
-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);
-}
diff --git a/src/native/libs/System.Globalization.Native/pal_localeStringData.h b/src/native/libs/System.Globalization.Native/pal_localeStringData.h
index e6030d8a4cf86e..a6961c39761bfd 100644
--- a/src/native/libs/System.Globalization.Native/pal_localeStringData.h
+++ b/src/native/libs/System.Globalization.Native/pal_localeStringData.h
@@ -52,9 +52,5 @@ PALEXPORT int32_t GlobalizationNative_GetLocaleInfoString(const UChar* localeNam
#ifdef __APPLE__
PALEXPORT const char* GlobalizationNative_GetLocaleInfoStringNative(const char* localeName, LocaleStringData localeStringData);
-#endif
-
-PALEXPORT int32_t GlobalizationNative_GetLocaleTimeFormat(const UChar* localeName,
- int shortFormat, UChar* value,
- int32_t valueLength);
+#endif
diff --git a/src/native/libs/System.Globalization.Native/pal_locale_internal.h b/src/native/libs/System.Globalization.Native/pal_locale_internal.h
index 1f9edb7640cbfb..c754554bbfdd55 100644
--- a/src/native/libs/System.Globalization.Native/pal_locale_internal.h
+++ b/src/native/libs/System.Globalization.Native/pal_locale_internal.h
@@ -4,7 +4,6 @@
#pragma once
#include "pal_icushim_internal.h"
-#include "pal_localeStringData.h"
/*
Function:
@@ -61,21 +60,4 @@ DetectDefaultSystemLocaleName
Detects the default locale string for Apple platforms
*/
char* DetectDefaultAppleLocaleName(void);
-
-/*
-Function:
-GlobalizationNative_GetLocaleNameNative
-
-Returns native locale name for Apple platforms
-*/
-const char* GlobalizationNative_GetLocaleNameNative(const char* localeName);
-
-/*
-Function:
-GlobalizationNative_GetLocaleInfoStringNative
-
-Returns string locale information if found for the specified locale name for Apple platforms.
-Returns empty string if not found.
-*/
-const char* GlobalizationNative_GetLocaleInfoStringNative(const char* localeName, LocaleStringData localeStringData);
#endif