Skip to content

Commit bb3597a

Browse files
authored
[iOS][non-icu] HybridGlobalization Get available locales (#93594)
Implements `GlobalizationNative_GetLocalesNative` for iOS hybrid globalization in order to return all of the available locales on the device. Fixes #93514 Contributes to #80689
1 parent 4dc17af commit bb3597a

File tree

7 files changed

+138
-38
lines changed

7 files changed

+138
-38
lines changed

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,5 +24,8 @@ internal static partial class Globalization
2424

2525
[LibraryImport(Libraries.GlobalizationNative, EntryPoint = "GlobalizationNative_GetLocaleTimeFormatNative", StringMarshalling = StringMarshalling.Utf8)]
2626
internal static partial string GetLocaleTimeFormatNative(string localeName, [MarshalAs(UnmanagedType.Bool)] bool shortFormat);
27+
28+
[LibraryImport(Libraries.GlobalizationNative, EntryPoint = "GlobalizationNative_GetLocalesNative", StringMarshalling = StringMarshalling.Utf16)]
29+
internal static partial int GetLocalesNative([Out] char[]? value, int valueLength);
2730
}
2831
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using Xunit;
5+
6+
namespace System.Globalization.Tests
7+
{
8+
public class CultureInfoGetCultures
9+
{
10+
[Fact]
11+
public void GetSpecificCultures()
12+
{
13+
var specificCultures = CultureInfo.GetCultures(CultureTypes.SpecificCultures);
14+
Assert.True(specificCultures.Length > 0);
15+
Assert.All(specificCultures, c => Assert.True(c.IsNeutralCulture == false));
16+
}
17+
18+
[Fact]
19+
public void GetAllCultures()
20+
{
21+
var allCultures = CultureInfo.GetCultures(CultureTypes.AllCultures);
22+
Assert.True(allCultures.Length > 0);
23+
}
24+
}
25+
}

src/libraries/System.Globalization/tests/Hybrid/System.Globalization.IOS.Tests.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
</PropertyGroup>
77
<ItemGroup>
88
<Compile Include="..\CultureInfo\CultureInfoEnglishName.cs" />
9+
<Compile Include="..\CultureInfo\CultureInfoGetCultures.cs" />
910
<Compile Include="..\CultureInfo\CultureInfoNumberFormat.cs" />
1011
<Compile Include="..\CultureInfo\CultureInfoNames.cs" />
1112
<Compile Include="..\CultureInfo\CultureInfoNativeName.cs" />

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

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -420,15 +420,38 @@ private static CultureInfo[] IcuEnumCultures(CultureTypes types)
420420
return Array.Empty<CultureInfo>();
421421
}
422422

423-
int bufferLength = Interop.Globalization.GetLocales(null, 0);
423+
int bufferLength;
424+
#if TARGET_MACCATALYST || TARGET_IOS || TARGET_TVOS
425+
if (GlobalizationMode.Hybrid)
426+
{
427+
bufferLength = Interop.Globalization.GetLocalesNative(null, 0);
428+
}
429+
else
430+
{
431+
bufferLength = Interop.Globalization.GetLocales(null, 0);
432+
}
433+
#else
434+
bufferLength = Interop.Globalization.GetLocales(null, 0);
435+
#endif
424436
if (bufferLength <= 0)
425437
{
426438
return Array.Empty<CultureInfo>();
427439
}
428440

429441
char [] chars = new char[bufferLength];
430442

443+
#if TARGET_MACCATALYST || TARGET_IOS || TARGET_TVOS
444+
if (GlobalizationMode.Hybrid)
445+
{
446+
bufferLength = Interop.Globalization.GetLocalesNative(chars, bufferLength);
447+
}
448+
else
449+
{
450+
bufferLength = Interop.Globalization.GetLocales(chars, bufferLength);
451+
}
452+
#else
431453
bufferLength = Interop.Globalization.GetLocales(chars, bufferLength);
454+
#endif
432455
if (bufferLength <= 0)
433456
{
434457
return Array.Empty<CultureInfo>();

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ static const Entry s_globalizationNative[] =
7272
DllImportEntry(GlobalizationNative_GetLocaleInfoSecondaryGroupingSizeNative)
7373
DllImportEntry(GlobalizationNative_GetLocaleInfoStringNative)
7474
DllImportEntry(GlobalizationNative_GetLocaleNameNative)
75+
DllImportEntry(GlobalizationNative_GetLocalesNative)
7576
DllImportEntry(GlobalizationNative_GetLocaleTimeFormatNative)
7677
DllImportEntry(GlobalizationNative_GetTimeZoneDisplayNameNative)
7778
DllImportEntry(GlobalizationNative_IndexOfNative)

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,6 @@ PALEXPORT int32_t GlobalizationNative_GetLocaleTimeFormat(const UChar* localeNam
2121
PALEXPORT const char* GlobalizationNative_GetLocaleNameNative(const char* localeName);
2222

2323
PALEXPORT const char* GlobalizationNative_GetLocaleTimeFormatNative(const char* localeName, int shortFormat);
24+
25+
PALEXPORT int32_t GlobalizationNative_GetLocalesNative(UChar* locales, int32_t length);
2426
#endif

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

Lines changed: 82 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ static void GetParent(const char* localeID, char* parent, int32_t parentCapacity
9797
{
9898
@autoreleasepool
9999
{
100-
const char* value;
100+
NSString *value;
101101
NSString *locName = [NSString stringWithFormat:@"%s", localeName];
102102
NSLocale *currentLocale = [[NSLocale alloc] initWithLocaleIdentifier:locName];
103103
NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init];
@@ -112,35 +112,35 @@ static void GetParent(const char* localeID, char* parent, int32_t parentCapacity
112112
case LocaleString_LocalizedDisplayName:
113113
/// <summary>Display name (language + country usually) in English, eg "German (Germany)" (corresponds to LOCALE_SENGLISHDISPLAYNAME)</summary>
114114
case LocaleString_EnglishDisplayName:
115-
value = [[gbLocale displayNameForKey:NSLocaleIdentifier value:currentLocale.localeIdentifier] UTF8String];
116-
break;
115+
value = [gbLocale displayNameForKey:NSLocaleIdentifier value:currentLocale.localeIdentifier];
116+
break;
117117
/// <summary>Display name in native locale language, eg "Deutsch (Deutschland) (corresponds to LOCALE_SNATIVEDISPLAYNAME)</summary>
118118
case LocaleString_NativeDisplayName:
119-
value = [[currentLocale displayNameForKey:NSLocaleIdentifier value:currentLocale.localeIdentifier] UTF8String];
119+
value = [currentLocale displayNameForKey:NSLocaleIdentifier value:currentLocale.localeIdentifier];
120120
break;
121121
/// <summary>Language Display Name for a language, eg "German" in UI language (corresponds to LOCALE_SLOCALIZEDLANGUAGENAME)</summary>
122122
case LocaleString_LocalizedLanguageName:
123123
/// <summary>English name of language, eg "German" (corresponds to LOCALE_SENGLISHLANGUAGENAME)</summary>
124124
case LocaleString_EnglishLanguageName:
125-
value = [[gbLocale localizedStringForLanguageCode:currentLocale.languageCode] UTF8String];
125+
value = [gbLocale localizedStringForLanguageCode:currentLocale.languageCode];
126126
break;
127127
/// <summary>native name of language, eg "Deutsch" (corresponds to LOCALE_SNATIVELANGUAGENAME)</summary>
128128
case LocaleString_NativeLanguageName:
129-
value = [[currentLocale localizedStringForLanguageCode:currentLocale.languageCode] UTF8String];
129+
value = [currentLocale localizedStringForLanguageCode:currentLocale.languageCode];
130130
break;
131131
/// <summary>English name of country, eg "Germany" (corresponds to LOCALE_SENGLISHCOUNTRYNAME)</summary>
132132
case LocaleString_EnglishCountryName:
133-
value = [[gbLocale localizedStringForCountryCode:currentLocale.countryCode] UTF8String];
133+
value = [gbLocale localizedStringForCountryCode:currentLocale.countryCode];
134134
break;
135135
/// <summary>native name of country, eg "Deutschland" (corresponds to LOCALE_SNATIVECOUNTRYNAME)</summary>
136136
case LocaleString_NativeCountryName:
137-
value = [[currentLocale localizedStringForCountryCode:currentLocale.countryCode] UTF8String];
137+
value = [currentLocale localizedStringForCountryCode:currentLocale.countryCode];
138138
break;
139139
case LocaleString_ThousandSeparator:
140-
value = [currentLocale.groupingSeparator UTF8String];
140+
value = currentLocale.groupingSeparator;
141141
break;
142142
case LocaleString_DecimalSeparator:
143-
value = [currentLocale.decimalSeparator UTF8String];
143+
value = currentLocale.decimalSeparator;
144144
// or value = [[currentLocale objectForKey:NSLocaleDecimalSeparator] UTF8String];
145145
break;
146146
case LocaleString_Digits:
@@ -150,87 +150,84 @@ static void GetParent(const char* localeID, char* parent, int32_t parentCapacity
150150
[nf1 setLocale:currentLocale];
151151

152152
NSNumber *newNum = [nf1 numberFromString:digitsString];
153-
value = [[newNum stringValue] UTF8String];
153+
value = [newNum stringValue];
154154
break;
155155
}
156156
case LocaleString_MonetarySymbol:
157-
value = [currentLocale.currencySymbol UTF8String];
157+
value = currentLocale.currencySymbol;
158158
break;
159159
case LocaleString_Iso4217MonetarySymbol:
160160
// check if this is correct, check currencyISOCode
161-
value = [currentLocale.currencySymbol UTF8String];
161+
value = currentLocale.currencyCode;
162162
break;
163163
case LocaleString_CurrencyEnglishName:
164-
value = [[gbLocale localizedStringForCurrencyCode:currentLocale.currencyCode] UTF8String];
164+
value = [gbLocale localizedStringForCurrencyCode:currentLocale.currencyCode];
165165
break;
166166
case LocaleString_CurrencyNativeName:
167-
value = [[currentLocale localizedStringForCurrencyCode:currentLocale.currencyCode] UTF8String];
167+
value = [currentLocale localizedStringForCurrencyCode:currentLocale.currencyCode];
168168
break;
169169
case LocaleString_MonetaryDecimalSeparator:
170-
value = [numberFormatter.currencyDecimalSeparator UTF8String];
170+
value = numberFormatter.currencyDecimalSeparator;
171171
break;
172172
case LocaleString_MonetaryThousandSeparator:
173-
value = [numberFormatter.currencyGroupingSeparator UTF8String];
173+
value = numberFormatter.currencyGroupingSeparator;
174174
break;
175175
case LocaleString_AMDesignator:
176-
value = [dateFormatter.AMSymbol UTF8String];
176+
value = dateFormatter.AMSymbol;
177177
break;
178178
case LocaleString_PMDesignator:
179-
value = [dateFormatter.PMSymbol UTF8String];
179+
value = dateFormatter.PMSymbol;
180180
break;
181181
case LocaleString_PositiveSign:
182-
value = [numberFormatter.plusSign UTF8String];
182+
value = numberFormatter.plusSign;
183183
break;
184184
case LocaleString_NegativeSign:
185-
value = [numberFormatter.minusSign UTF8String];
185+
value = numberFormatter.minusSign;
186186
break;
187187
case LocaleString_Iso639LanguageTwoLetterName:
188-
value = [[currentLocale objectForKey:NSLocaleLanguageCode] UTF8String];
188+
value = [currentLocale objectForKey:NSLocaleLanguageCode];
189189
break;
190190
case LocaleString_Iso639LanguageThreeLetterName:
191191
{
192192
NSString *iso639_2 = [currentLocale objectForKey:NSLocaleLanguageCode];
193-
value = uloc_getISO3LanguageByLangCode([iso639_2 UTF8String]);
194-
break;
193+
return iso639_2 == nil ? strdup("") : strdup(uloc_getISO3LanguageByLangCode([iso639_2 UTF8String]));
195194
}
196195
case LocaleString_Iso3166CountryName:
197-
value = [[currentLocale objectForKey:NSLocaleCountryCode] UTF8String];
196+
value = [currentLocale objectForKey:NSLocaleCountryCode];
198197
break;
199198
case LocaleString_Iso3166CountryName2:
200199
{
201-
const char *countryCode = strdup([[currentLocale objectForKey:NSLocaleCountryCode] UTF8String]);
202-
value = uloc_getISO3CountryByCountryCode(countryCode);
203-
break;
200+
NSString* countryCode = [currentLocale objectForKey:NSLocaleCountryCode];
201+
return countryCode == nil ? strdup("") : strdup(uloc_getISO3CountryByCountryCode([countryCode UTF8String]));
204202
}
205203
case LocaleString_NaNSymbol:
206-
value = [numberFormatter.notANumberSymbol UTF8String];
204+
value = numberFormatter.notANumberSymbol;
207205
break;
208206
case LocaleString_PositiveInfinitySymbol:
209-
value = [numberFormatter.positiveInfinitySymbol UTF8String];
207+
value = numberFormatter.positiveInfinitySymbol;
210208
break;
211209
case LocaleString_NegativeInfinitySymbol:
212-
value = [numberFormatter.negativeInfinitySymbol UTF8String];
210+
value = numberFormatter.negativeInfinitySymbol;
213211
break;
214212
case LocaleString_PercentSymbol:
215-
value = [numberFormatter.percentSymbol UTF8String];
213+
value = numberFormatter.percentSymbol;
216214
break;
217215
case LocaleString_PerMilleSymbol:
218-
value = [numberFormatter.perMillSymbol UTF8String];
216+
value = numberFormatter.perMillSymbol;
219217
break;
220218
case LocaleString_ParentName:
221219
{
222220
char localeNameTemp[FULLNAME_CAPACITY];
223221
const char* lName = [currentLocale.localeIdentifier UTF8String];
224222
GetParent(lName, localeNameTemp, FULLNAME_CAPACITY);
225-
value = strdup(localeNameTemp);
226-
break;
223+
return strdup(localeNameTemp);
227224
}
228225
default:
229-
value = "";
226+
value = nil;
230227
break;
231228
}
232229

233-
return value ? strdup(value) : "";
230+
return value == nil ? strdup("") : strdup([value UTF8String]);
234231
}
235232
}
236233

@@ -667,6 +664,54 @@ Returns time format information (in native format, it needs to be converted to .
667664
}
668665
}
669666

667+
// GlobalizationNative_GetLocalesNative gets all locale names and store it in the value buffer
668+
// in case of success, it returns the count of the characters stored in value buffer
669+
// in case of failure, it returns negative number.
670+
// if the input value buffer is null, it returns the length needed to store the
671+
// locale names list.
672+
// if the value is not null, it fills the value with locale names separated by the length
673+
// of each name.
674+
int32_t GlobalizationNative_GetLocalesNative(UChar* value, int32_t length)
675+
{
676+
@autoreleasepool
677+
{
678+
NSArray<NSString*>* availableLocaleIdentifiers = [NSLocale availableLocaleIdentifiers];
679+
int32_t index = 0;
680+
int32_t totalLength = 0;
681+
int32_t availableLength = (int32_t)[availableLocaleIdentifiers count];
682+
683+
if (availableLength <= 0)
684+
return -1; // failed
685+
686+
for (NSInteger i = 0; i < availableLength; i++)
687+
{
688+
NSString *localeIdentifier = availableLocaleIdentifiers[i];
689+
int32_t localeNameLength = localeIdentifier.length;
690+
totalLength += localeNameLength + 1; // add 1 for the name length
691+
if (value != NULL)
692+
{
693+
if (totalLength > length)
694+
return -3;
695+
696+
value[index++] = (UChar) localeNameLength;
697+
698+
for (int j = 0; j < localeNameLength; j++)
699+
{
700+
if ((UChar)[localeIdentifier characterAtIndex:j] == '_')
701+
{
702+
value[index++] = (UChar) '-';
703+
}
704+
else
705+
{
706+
value[index++] = (UChar) [localeIdentifier characterAtIndex:j];
707+
}
708+
}
709+
}
710+
}
711+
return totalLength;
712+
}
713+
}
714+
670715
#endif
671716

672717
#if defined(TARGET_MACCATALYST) || defined(TARGET_IOS) || defined(TARGET_TVOS)

0 commit comments

Comments
 (0)