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

[iOS][non-icu] HybridGlobalization Get available locales #93594

Merged
merged 5 commits into from
Oct 19, 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
3 changes: 3 additions & 0 deletions src/libraries/Common/src/Interop/Interop.Locale.iOS.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,8 @@ internal static partial class Globalization

[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_GetLocalesNative", StringMarshalling = StringMarshalling.Utf16)]
internal static partial int GetLocalesNative([Out] char[]? value, int valueLength);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using Xunit;

namespace System.Globalization.Tests
{
public class CultureInfoGetCultures
{
[Fact]
public void GetSpecificCultures()
{
var specificCultures = CultureInfo.GetCultures(CultureTypes.SpecificCultures);
Assert.True(specificCultures.Length > 0);
Assert.All(specificCultures, c => Assert.True(c.IsNeutralCulture == false));
}

[Fact]
public void GetAllCultures()
{
var allCultures = CultureInfo.GetCultures(CultureTypes.AllCultures);
Assert.True(allCultures.Length > 0);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
</PropertyGroup>
<ItemGroup>
<Compile Include="..\CultureInfo\CultureInfoEnglishName.cs" />
<Compile Include="..\CultureInfo\CultureInfoGetCultures.cs" />
<Compile Include="..\CultureInfo\CultureInfoNumberFormat.cs" />
<Compile Include="..\CultureInfo\CultureInfoNames.cs" />
<Compile Include="..\CultureInfo\CultureInfoNativeName.cs" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -420,15 +420,38 @@ private static CultureInfo[] IcuEnumCultures(CultureTypes types)
return Array.Empty<CultureInfo>();
}

int bufferLength = Interop.Globalization.GetLocales(null, 0);
int bufferLength;
#if TARGET_MACCATALYST || TARGET_IOS || TARGET_TVOS
if (GlobalizationMode.Hybrid)
{
bufferLength = Interop.Globalization.GetLocalesNative(null, 0);
}
else
{
bufferLength = Interop.Globalization.GetLocales(null, 0);
}
#else
bufferLength = Interop.Globalization.GetLocales(null, 0);
#endif
if (bufferLength <= 0)
{
return Array.Empty<CultureInfo>();
}

char [] chars = new char[bufferLength];

#if TARGET_MACCATALYST || TARGET_IOS || TARGET_TVOS
if (GlobalizationMode.Hybrid)
{
bufferLength = Interop.Globalization.GetLocalesNative(chars, bufferLength);
}
else
{
bufferLength = Interop.Globalization.GetLocales(chars, bufferLength);
}
#else
bufferLength = Interop.Globalization.GetLocales(chars, bufferLength);
#endif
if (bufferLength <= 0)
{
return Array.Empty<CultureInfo>();
Expand Down
1 change: 1 addition & 0 deletions src/native/libs/System.Globalization.Native/entrypoints.c
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ static const Entry s_globalizationNative[] =
DllImportEntry(GlobalizationNative_GetLocaleInfoSecondaryGroupingSizeNative)
DllImportEntry(GlobalizationNative_GetLocaleInfoStringNative)
DllImportEntry(GlobalizationNative_GetLocaleNameNative)
DllImportEntry(GlobalizationNative_GetLocalesNative)
DllImportEntry(GlobalizationNative_GetLocaleTimeFormatNative)
DllImportEntry(GlobalizationNative_GetTimeZoneDisplayNameNative)
DllImportEntry(GlobalizationNative_IndexOfNative)
Expand Down
2 changes: 2 additions & 0 deletions src/native/libs/System.Globalization.Native/pal_locale.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,6 @@ PALEXPORT int32_t GlobalizationNative_GetLocaleTimeFormat(const UChar* localeNam
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);
#endif
119 changes: 82 additions & 37 deletions src/native/libs/System.Globalization.Native/pal_locale.m
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ static void GetParent(const char* localeID, char* parent, int32_t parentCapacity
{
@autoreleasepool
{
const char* value;
NSString *value;
NSString *locName = [NSString stringWithFormat:@"%s", localeName];
NSLocale *currentLocale = [[NSLocale alloc] initWithLocaleIdentifier:locName];
NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init];
Expand All @@ -112,35 +112,35 @@ static void GetParent(const char* localeID, char* parent, int32_t parentCapacity
case LocaleString_LocalizedDisplayName:
/// <summary>Display name (language + country usually) in English, eg "German (Germany)" (corresponds to LOCALE_SENGLISHDISPLAYNAME)</summary>
case LocaleString_EnglishDisplayName:
value = [[gbLocale displayNameForKey:NSLocaleIdentifier value:currentLocale.localeIdentifier] UTF8String];
break;
value = [gbLocale displayNameForKey:NSLocaleIdentifier value:currentLocale.localeIdentifier];
break;
/// <summary>Display name in native locale language, eg "Deutsch (Deutschland) (corresponds to LOCALE_SNATIVEDISPLAYNAME)</summary>
case LocaleString_NativeDisplayName:
value = [[currentLocale displayNameForKey:NSLocaleIdentifier value:currentLocale.localeIdentifier] UTF8String];
value = [currentLocale displayNameForKey:NSLocaleIdentifier value:currentLocale.localeIdentifier];
break;
/// <summary>Language Display Name for a language, eg "German" in UI language (corresponds to LOCALE_SLOCALIZEDLANGUAGENAME)</summary>
case LocaleString_LocalizedLanguageName:
/// <summary>English name of language, eg "German" (corresponds to LOCALE_SENGLISHLANGUAGENAME)</summary>
case LocaleString_EnglishLanguageName:
value = [[gbLocale localizedStringForLanguageCode:currentLocale.languageCode] UTF8String];
value = [gbLocale localizedStringForLanguageCode:currentLocale.languageCode];
break;
/// <summary>native name of language, eg "Deutsch" (corresponds to LOCALE_SNATIVELANGUAGENAME)</summary>
case LocaleString_NativeLanguageName:
value = [[currentLocale localizedStringForLanguageCode:currentLocale.languageCode] UTF8String];
value = [currentLocale localizedStringForLanguageCode:currentLocale.languageCode];
break;
/// <summary>English name of country, eg "Germany" (corresponds to LOCALE_SENGLISHCOUNTRYNAME)</summary>
case LocaleString_EnglishCountryName:
value = [[gbLocale localizedStringForCountryCode:currentLocale.countryCode] UTF8String];
value = [gbLocale localizedStringForCountryCode:currentLocale.countryCode];
break;
/// <summary>native name of country, eg "Deutschland" (corresponds to LOCALE_SNATIVECOUNTRYNAME)</summary>
case LocaleString_NativeCountryName:
value = [[currentLocale localizedStringForCountryCode:currentLocale.countryCode] UTF8String];
value = [currentLocale localizedStringForCountryCode:currentLocale.countryCode];
break;
case LocaleString_ThousandSeparator:
value = [currentLocale.groupingSeparator UTF8String];
value = currentLocale.groupingSeparator;
break;
case LocaleString_DecimalSeparator:
value = [currentLocale.decimalSeparator UTF8String];
value = currentLocale.decimalSeparator;
// or value = [[currentLocale objectForKey:NSLocaleDecimalSeparator] UTF8String];
break;
case LocaleString_Digits:
Expand All @@ -150,87 +150,84 @@ static void GetParent(const char* localeID, char* parent, int32_t parentCapacity
[nf1 setLocale:currentLocale];

NSNumber *newNum = [nf1 numberFromString:digitsString];
value = [[newNum stringValue] UTF8String];
value = [newNum stringValue];
break;
}
case LocaleString_MonetarySymbol:
value = [currentLocale.currencySymbol UTF8String];
value = currentLocale.currencySymbol;
break;
case LocaleString_Iso4217MonetarySymbol:
// check if this is correct, check currencyISOCode
value = [currentLocale.currencySymbol UTF8String];
value = currentLocale.currencyCode;
break;
case LocaleString_CurrencyEnglishName:
value = [[gbLocale localizedStringForCurrencyCode:currentLocale.currencyCode] UTF8String];
value = [gbLocale localizedStringForCurrencyCode:currentLocale.currencyCode];
break;
case LocaleString_CurrencyNativeName:
value = [[currentLocale localizedStringForCurrencyCode:currentLocale.currencyCode] UTF8String];
value = [currentLocale localizedStringForCurrencyCode:currentLocale.currencyCode];
break;
case LocaleString_MonetaryDecimalSeparator:
value = [numberFormatter.currencyDecimalSeparator UTF8String];
value = numberFormatter.currencyDecimalSeparator;
break;
case LocaleString_MonetaryThousandSeparator:
value = [numberFormatter.currencyGroupingSeparator UTF8String];
value = numberFormatter.currencyGroupingSeparator;
break;
case LocaleString_AMDesignator:
value = [dateFormatter.AMSymbol UTF8String];
value = dateFormatter.AMSymbol;
break;
case LocaleString_PMDesignator:
value = [dateFormatter.PMSymbol UTF8String];
value = dateFormatter.PMSymbol;
break;
case LocaleString_PositiveSign:
value = [numberFormatter.plusSign UTF8String];
value = numberFormatter.plusSign;
break;
case LocaleString_NegativeSign:
value = [numberFormatter.minusSign UTF8String];
value = numberFormatter.minusSign;
break;
case LocaleString_Iso639LanguageTwoLetterName:
value = [[currentLocale objectForKey:NSLocaleLanguageCode] UTF8String];
value = [currentLocale objectForKey:NSLocaleLanguageCode];
break;
case LocaleString_Iso639LanguageThreeLetterName:
{
NSString *iso639_2 = [currentLocale objectForKey:NSLocaleLanguageCode];
value = uloc_getISO3LanguageByLangCode([iso639_2 UTF8String]);
break;
return iso639_2 == nil ? strdup("") : strdup(uloc_getISO3LanguageByLangCode([iso639_2 UTF8String]));
}
case LocaleString_Iso3166CountryName:
value = [[currentLocale objectForKey:NSLocaleCountryCode] UTF8String];
value = [currentLocale objectForKey:NSLocaleCountryCode];
break;
case LocaleString_Iso3166CountryName2:
{
const char *countryCode = strdup([[currentLocale objectForKey:NSLocaleCountryCode] UTF8String]);
value = uloc_getISO3CountryByCountryCode(countryCode);
break;
NSString* countryCode = [currentLocale objectForKey:NSLocaleCountryCode];
return countryCode == nil ? strdup("") : strdup(uloc_getISO3CountryByCountryCode([countryCode UTF8String]));
}
case LocaleString_NaNSymbol:
value = [numberFormatter.notANumberSymbol UTF8String];
value = numberFormatter.notANumberSymbol;
break;
case LocaleString_PositiveInfinitySymbol:
value = [numberFormatter.positiveInfinitySymbol UTF8String];
value = numberFormatter.positiveInfinitySymbol;
break;
case LocaleString_NegativeInfinitySymbol:
value = [numberFormatter.negativeInfinitySymbol UTF8String];
value = numberFormatter.negativeInfinitySymbol;
break;
case LocaleString_PercentSymbol:
value = [numberFormatter.percentSymbol UTF8String];
value = numberFormatter.percentSymbol;
break;
case LocaleString_PerMilleSymbol:
value = [numberFormatter.perMillSymbol UTF8String];
value = numberFormatter.perMillSymbol;
break;
case LocaleString_ParentName:
{
char localeNameTemp[FULLNAME_CAPACITY];
const char* lName = [currentLocale.localeIdentifier UTF8String];
GetParent(lName, localeNameTemp, FULLNAME_CAPACITY);
value = strdup(localeNameTemp);
break;
return strdup(localeNameTemp);
}
default:
value = "";
value = nil;
break;
}

return value ? strdup(value) : "";
return value == nil ? strdup("") : strdup([value UTF8String]);
}
}

Expand Down Expand Up @@ -667,6 +664,54 @@ Returns time format information (in native format, it needs to be converted to .
}
}

// GlobalizationNative_GetLocalesNative gets all locale names and store it in the value buffer
// in case of success, it returns the count of the characters stored in value buffer
// in case of failure, it returns negative number.
// if the input value buffer is null, it returns the length needed to store the
// locale names list.
// if the value is not null, it fills the value with locale names separated by the length
// of each name.
int32_t GlobalizationNative_GetLocalesNative(UChar* value, int32_t length)
{
@autoreleasepool
{
NSArray<NSString*>* availableLocaleIdentifiers = [NSLocale availableLocaleIdentifiers];
int32_t index = 0;
int32_t totalLength = 0;
int32_t availableLength = (int32_t)[availableLocaleIdentifiers count];

if (availableLength <= 0)
return -1; // failed

for (NSInteger i = 0; i < availableLength; i++)
{
NSString *localeIdentifier = availableLocaleIdentifiers[i];
int32_t localeNameLength = localeIdentifier.length;
totalLength += localeNameLength + 1; // add 1 for the name length
if (value != NULL)
{
if (totalLength > length)
return -3;
mdh1418 marked this conversation as resolved.
Show resolved Hide resolved

value[index++] = (UChar) localeNameLength;

for (int j = 0; j < localeNameLength; j++)
{
if ((UChar)[localeIdentifier characterAtIndex:j] == '_')
{
value[index++] = (UChar) '-';
}
else
{
value[index++] = (UChar) [localeIdentifier characterAtIndex:j];
}
}
}
}
return totalLength;
}
}

#endif

#if defined(TARGET_MACCATALYST) || defined(TARGET_IOS) || defined(TARGET_TVOS)
Expand Down
Loading