Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 31d5215

Browse files
committedMay 31, 2024·
fix(skia): ResourceLoader culture resolution
1 parent 7e39a57 commit 31d5215

File tree

11 files changed

+564
-420
lines changed

11 files changed

+564
-420
lines changed
 

‎src/Uno.UI.RuntimeTests/Tests/Windows_ApplicationModel_Resources/Given_ResourceLoader.cs

+16-9
Original file line numberDiff line numberDiff line change
@@ -6,27 +6,32 @@
66
using System.Threading;
77
using Windows.ApplicationModel.Resources;
88
using Windows.ApplicationModel.Resources.Core;
9+
using Windows.Globalization;
910

1011
namespace Uno.UI.RuntimeTests.Tests
1112
{
1213
[TestClass]
1314
public class Given_ResourceLoader
1415
{
16+
private const string DefaultLanguage = "en-US";
17+
1518
[TestInitialize]
1619
public void Init()
1720
{
18-
CultureInfo.CurrentUICulture = new CultureInfo("en-US");
21+
CultureInfo.CurrentUICulture = new CultureInfo(DefaultLanguage);
22+
ApplicationLanguages.PrimaryLanguageOverride = DefaultLanguage;
1923
#if __XAMARIN__ || __WASM__
20-
ResourceLoader.DefaultLanguage = "en-US";
24+
ResourceLoader.DefaultLanguage = DefaultLanguage;
2125
#endif
2226
}
2327

2428
[TestCleanup]
2529
public void Cleanup()
2630
{
27-
CultureInfo.CurrentUICulture = new CultureInfo("en-US");
31+
CultureInfo.CurrentUICulture = new CultureInfo(DefaultLanguage);
32+
ApplicationLanguages.PrimaryLanguageOverride = DefaultLanguage;
2833
#if __XAMARIN__ || __WASM__
29-
ResourceLoader.DefaultLanguage = "en-US";
34+
ResourceLoader.DefaultLanguage = DefaultLanguage;
3035
#endif
3136
}
3237

@@ -130,7 +135,7 @@ public void When_LocalizedResource()
130135

131136
foreach (var language in languages)
132137
{
133-
CultureInfo.CurrentUICulture = new CultureInfo(language);
138+
ApplicationLanguages.PrimaryLanguageOverride = language;
134139
Assert.AreEqual($@"Text in '{language}'", SUT.GetString("Given_ResourceLoader/When_LocalizedResource"));
135140
}
136141
}
@@ -140,7 +145,7 @@ public void When_MissingLocalizedResource_FallbackOnParent()
140145
{
141146
var SUT = ResourceLoader.GetForViewIndependentUse();
142147

143-
CultureInfo.CurrentUICulture = new CultureInfo("fr-FR");
148+
ApplicationLanguages.PrimaryLanguageOverride = "fr-FR";
144149
Assert.AreEqual(@"Text in 'fr'", SUT.GetString("Given_ResourceLoader/When_LocalizedResource"));
145150
}
146151

@@ -149,16 +154,19 @@ public void When_MissingLocalizedResource_FallbackOnRegional()
149154
{
150155
var SUT = ResourceLoader.GetForViewIndependentUse();
151156

152-
CultureInfo.CurrentUICulture = new CultureInfo("es");
157+
ApplicationLanguages.PrimaryLanguageOverride = "es";
153158
Assert.AreEqual(@"Text in 'es-MX'", SUT.GetString("Given_ResourceLoader/When_LocalizedResource"));
154159
}
155160

156161
[TestMethod]
162+
#if __IOS__
163+
[Ignore("Unstable test due to device language settings: NSLocale.PreferredLanguages can be 'en' or 'en-US'.")]
164+
#endif
157165
public void When_MissingLocalizedResource_FallbackOnDefault()
158166
{
159167
var SUT = ResourceLoader.GetForViewIndependentUse();
160168

161-
CultureInfo.CurrentUICulture = new CultureInfo("de-DE");
169+
ApplicationLanguages.PrimaryLanguageOverride = "de-DE";
162170
Assert.AreEqual(@"Text in 'en-US'", SUT.GetString("Given_ResourceLoader/When_LocalizedResource"));
163171
}
164172

@@ -174,6 +182,5 @@ public void When_FileAndStringNameFormat()
174182
Assert.AreEqual(@"", SUT.GetString("/this-does-not-exist"));
175183
Assert.AreEqual(@"", SUT.GetString("//this-does-not-exist"));
176184
}
177-
178185
}
179186
}

‎src/Uno.UI.Tests/ResourceLoader/Given_ResourceLoader.cs

+12-7
Original file line numberDiff line numberDiff line change
@@ -7,28 +7,33 @@
77
using System.Threading.Tasks;
88
using Microsoft.VisualStudio.TestTools.UnitTesting;
99
using Uno.UI.Tests.ResourceLoader.Controls;
10+
using Windows.Globalization;
1011
using _ResourceLoader = Windows.ApplicationModel.Resources.ResourceLoader;
1112

1213
namespace Uno.UI.Tests.ResourceLoaderTests
1314
{
1415
[TestClass]
1516
public class Given_ResourceLoader
1617
{
18+
private const string DefaultLanguage = "en-US";
1719
private const string UITestResources = "Uno.UI.Tests/Resources";
1820

1921
[TestInitialize]
2022
public void Init()
2123
{
22-
CultureInfo.CurrentUICulture = new CultureInfo("en-US");
23-
_ResourceLoader.DefaultLanguage = "en-US";
24+
CultureInfo.CurrentUICulture = new CultureInfo(DefaultLanguage);
25+
ApplicationLanguages.PrimaryLanguageOverride = DefaultLanguage;
26+
_ResourceLoader.DefaultLanguage = DefaultLanguage;
27+
2428
_ResourceLoader.AddLookupAssembly(GetType().Assembly);
2529
}
2630

2731
[TestCleanup]
2832
public void Cleanup()
2933
{
30-
CultureInfo.CurrentUICulture = new CultureInfo("en-US");
31-
_ResourceLoader.DefaultLanguage = "en-US";
34+
CultureInfo.CurrentUICulture = new CultureInfo(DefaultLanguage);
35+
ApplicationLanguages.PrimaryLanguageOverride = DefaultLanguage;
36+
_ResourceLoader.DefaultLanguage = DefaultLanguage;
3237
}
3338

3439
[TestMethod]
@@ -52,7 +57,7 @@ public void When_ResourceFile_Neutral_Both()
5257
{
5358
void setResources(string language)
5459
{
55-
CultureInfo.CurrentUICulture = new CultureInfo(language);
60+
ApplicationLanguages.PrimaryLanguageOverride = language;
5661
_ResourceLoader.DefaultLanguage = language;
5762
}
5863

@@ -71,7 +76,7 @@ public void When_MissingLocalizedResource_FallbackOnParent()
7176
{
7277
var SUT = _ResourceLoader.GetForCurrentView(UITestResources);
7378

74-
CultureInfo.CurrentUICulture = new CultureInfo("fr-FR");
79+
ApplicationLanguages.PrimaryLanguageOverride = "fr-FR";
7580
Assert.AreEqual(@"Text in 'fr'", SUT.GetString("Given_ResourceLoader/When_LocalizedResource"));
7681
}
7782

@@ -80,7 +85,7 @@ public void When_MissingLocalizedResource_FallbackOnDefault()
8085
{
8186
var SUT = _ResourceLoader.GetForCurrentView(UITestResources);
8287

83-
CultureInfo.CurrentUICulture = new CultureInfo("de-DE");
88+
ApplicationLanguages.PrimaryLanguageOverride = "de-DE";
8489
Assert.AreEqual(@"Text in 'en'", SUT.GetString("Given_ResourceLoader/When_LocalizedResource"));
8590
}
8691

‎src/Uno.UWP/ApplicationModel/Resources/ResourceLoader.cs

+269-225
Large diffs are not rendered by default.

‎src/Uno.UWP/FeatureConfiguration/WinRTFeatureConfiguration.cs

+17-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
namespace Uno;
1+
using System.Globalization;
2+
3+
namespace Uno;
24

35
public static partial class WinRTFeatureConfiguration
46
{
@@ -19,4 +21,18 @@ public static class ApplicationLanguages
1921
/// </summary>
2022
public static bool UseLegacyPrimaryLanguageOverride { get; set; } = true;
2123
}
24+
25+
public static class ResourceLoader
26+
{
27+
/// <summary>
28+
/// Use <see cref="Windows.Globalization.ApplicationLanguages.PrimaryLanguageOverride"/> as the override for resource resolution.
29+
/// Alternatively if set to false, use <see cref="CultureInfo.CurrentUICulture"/> as the override.
30+
/// </summary>
31+
public static bool UsePrimaryLanguageOverride { get; set; } = true;
32+
33+
/// <summary>
34+
/// Determines if parsed string resources are preserved between languages change.
35+
/// </summary>
36+
public static bool PreserveParsedResources { get; set; }
37+
}
2238
}

‎src/Uno.UWP/Generated/3.0.0.0/Windows.System.UserProfile/GlobalizationPreferences.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,8 @@ public static string HomeGeographicRegion
4848
}
4949
}
5050
#endif
51-
#if false || false || IS_UNIT_TESTS || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__
52-
[global::Uno.NotImplemented("IS_UNIT_TESTS", "__WASM__", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")]
51+
#if false || false || IS_UNIT_TESTS || __WASM__ || false || __NETSTD_REFERENCE__ || __MACOS__
52+
[global::Uno.NotImplemented("IS_UNIT_TESTS", "__WASM__", "__NETSTD_REFERENCE__", "__MACOS__")]
5353
public static global::System.Collections.Generic.IReadOnlyList<string> Languages
5454
{
5555
get
+158-136
Original file line numberDiff line numberDiff line change
@@ -1,194 +1,216 @@
1-
using System;
1+
#nullable enable
2+
3+
using System;
24
using System.Globalization;
35
using System.Linq;
46
using System.Text.RegularExpressions;
57
using Windows.Storage;
8+
using Uno;
69
using Uno.Foundation.Logging;
710
using Uno.UI;
8-
using Uno;
11+
using System.Collections.Generic;
12+
using Windows.System.UserProfile;
13+
using System.Diagnostics.CodeAnalysis;
14+
15+
namespace Windows.Globalization;
916

10-
namespace Windows.Globalization
17+
public static partial class ApplicationLanguages
1118
{
12-
public static partial class ApplicationLanguages
13-
{
14-
private static string _primaryLanguageOverride = string.Empty;
19+
private static string? _primaryLanguageOverride = string.Empty;
1520

1621
#if !IS_UNIT_TESTS
17-
private const string PrimaryLanguageOverrideSettingKey = "__Uno.PrimaryLanguageOverride";
22+
private const string PrimaryLanguageOverrideSettingKey = "__Uno.PrimaryLanguageOverride";
1823
#endif
1924

20-
static ApplicationLanguages()
25+
public static string? PrimaryLanguageOverride
26+
{
27+
get => _primaryLanguageOverride;
28+
set
2129
{
22-
#if !IS_UNIT_TESTS
23-
if (ApplicationData.Current.LocalSettings.Values.TryGetValue(PrimaryLanguageOverrideSettingKey, out var savedValue)
24-
&& savedValue is string stringSavedValue)
30+
value ??= string.Empty;
31+
if (_primaryLanguageOverride != value)
2532
{
26-
_primaryLanguageOverride = stringSavedValue;
27-
}
28-
#endif
29-
30-
ApplyLanguages();
31-
}
33+
typeof(ApplicationLanguages).Log().LogDebug($"PLO: {_primaryLanguageOverride} -> {value}");
34+
_primaryLanguageOverride = value;
3235

33-
internal static void ApplyCulture()
34-
{
35-
var primaryLanguageOverride = PrimaryLanguageOverride;
36-
if (primaryLanguageOverride.Length > 0)
37-
{
38-
if (typeof(ApplicationLanguages).Log().IsEnabled(LogLevel.Debug))
36+
ApplyLanguages();
37+
if (WinRTFeatureConfiguration.ApplicationLanguages.UseLegacyPrimaryLanguageOverride)
3938
{
40-
typeof(ApplicationLanguages).Log().Debug($"Using {primaryLanguageOverride} (from PrimaryLanguageOverride) as primary language");
39+
ApplyCulture();
4140
}
4241

43-
setCulture(primaryLanguageOverride);
42+
#if !IS_UNIT_TESTS
43+
ApplicationData.Current.LocalSettings.Values[PrimaryLanguageOverrideSettingKey] = _primaryLanguageOverride;
44+
#endif
4445
}
45-
else if (Languages.Count > 0)
46-
{
47-
var language = Languages[0];
48-
if (typeof(ApplicationLanguages).Log().IsEnabled(LogLevel.Debug))
49-
{
50-
typeof(ApplicationLanguages).Log().Debug($"Using {language} (from Languages) as primary language");
51-
}
46+
}
47+
}
5248

53-
setCulture(language);
54-
}
55-
else
56-
{
57-
if (typeof(ApplicationLanguages).Log().IsEnabled(LogLevel.Warning))
58-
{
59-
typeof(ApplicationLanguages).Log().Warn($"Unable to determine the default culture, using invariant culture");
60-
}
61-
}
49+
public static IReadOnlyList<string> Languages { get; private set; }
50+
public static IReadOnlyList<string> ManifestLanguages { get; }
6251

63-
static void setCulture(string cultureId)
64-
{
65-
var culture = CreateCulture(cultureId);
66-
CultureInfo.CurrentCulture = culture;
67-
CultureInfo.DefaultThreadCurrentCulture = culture;
68-
CultureInfo.CurrentUICulture = culture;
69-
CultureInfo.DefaultThreadCurrentUICulture = culture;
70-
}
52+
static ApplicationLanguages()
53+
{
54+
#if !IS_UNIT_TESTS
55+
if (ApplicationData.Current.LocalSettings.Values.TryGetValue(PrimaryLanguageOverrideSettingKey, out var savedValue) &&
56+
savedValue is string stringSavedValue)
57+
{
58+
_primaryLanguageOverride = stringSavedValue;
7159
}
60+
#endif
7261

73-
public static string PrimaryLanguageOverride
62+
ManifestLanguages = GetManifestLanguages();
63+
ApplyLanguages();
64+
}
65+
66+
internal static void ApplyCulture()
67+
{
68+
var primaryLanguageOverride = PrimaryLanguageOverride;
69+
if (!string.IsNullOrEmpty(primaryLanguageOverride))
7470
{
75-
get
71+
if (typeof(ApplicationLanguages).Log().IsEnabled(LogLevel.Debug))
7672
{
77-
return _primaryLanguageOverride;
73+
typeof(ApplicationLanguages).Log().Debug($"Using {primaryLanguageOverride} (from PrimaryLanguageOverride) as primary language");
7874
}
79-
set
80-
{
81-
value ??= string.Empty;
8275

83-
_primaryLanguageOverride = value;
84-
ApplyLanguages();
85-
if (WinRTFeatureConfiguration.ApplicationLanguages.UseLegacyPrimaryLanguageOverride)
86-
{
87-
ApplyCulture();
88-
}
76+
setCulture(primaryLanguageOverride);
77+
}
78+
else if (Languages.Count > 0)
79+
{
80+
var language = Languages[0];
81+
if (typeof(ApplicationLanguages).Log().IsEnabled(LogLevel.Debug))
82+
{
83+
typeof(ApplicationLanguages).Log().Debug($"Using {language} (from Languages) as primary language");
84+
}
8985

90-
#if !IS_UNIT_TESTS
91-
ApplicationData.Current.LocalSettings.Values[PrimaryLanguageOverrideSettingKey] = _primaryLanguageOverride;
92-
#endif
86+
setCulture(language);
87+
}
88+
else
89+
{
90+
if (typeof(ApplicationLanguages).Log().IsEnabled(LogLevel.Warning))
91+
{
92+
typeof(ApplicationLanguages).Log().Warn($"Unable to determine the default culture, using invariant culture");
9393
}
9494
}
9595

96-
public static global::System.Collections.Generic.IReadOnlyList<string> Languages
96+
static void setCulture(string cultureId)
9797
{
98-
get;
99-
private set;
98+
var culture = CreateCulture(cultureId);
99+
CultureInfo.CurrentCulture = culture;
100+
CultureInfo.DefaultThreadCurrentCulture = culture;
101+
CultureInfo.CurrentUICulture = culture;
102+
CultureInfo.DefaultThreadCurrentUICulture = culture;
100103
}
104+
}
101105

102-
#if !(__IOS__)
103-
public static global::System.Collections.Generic.IReadOnlyList<string> ManifestLanguages
106+
#if !__IOS__
107+
private static string[] GetManifestLanguages()
108+
{
109+
string AdjustCultureName(string? name)
110+
=> string.IsNullOrEmpty(name) ? "en-US" : name;
111+
112+
var languages = new[]
104113
{
105-
get;
106-
} = GetManifestLanguages();
114+
#if __ANDROID__
115+
ContextHelper.Current?.Resources?.Configuration?.Locales?.Get(0)?.ToLanguageTag(),
116+
#endif
117+
AdjustCultureName(CultureInfo.InstalledUICulture?.Name),
118+
AdjustCultureName(CultureInfo.CurrentUICulture?.Name),
119+
AdjustCultureName(CultureInfo.CurrentCulture?.Name)
120+
};
121+
122+
return languages
123+
.Where(l => !string.IsNullOrWhiteSpace(l))
124+
.OfType<string>()
125+
.Distinct()
126+
.ToArray();
127+
}
128+
#else
129+
private static string[] GetManifestLanguages()
130+
{
131+
var manifestLanguages = global::Foundation.NSLocale.PreferredLanguages
132+
.Concat(global::Foundation.NSBundle.MainBundle.PreferredLocalizations)
133+
.Concat(global::Foundation.NSBundle.MainBundle.Localizations)
134+
.Distinct()
135+
.ToArray();
107136

137+
return manifestLanguages;
138+
}
139+
#endif
108140

109-
private static string[] GetManifestLanguages()
141+
[MemberNotNull(nameof(Languages))]
142+
private static void ApplyLanguages()
143+
{
144+
#if false
145+
Languages = GlobalizationPreferences.Languages
146+
.Cast<string?>()
147+
.Prepend(PrimaryLanguageOverride)
148+
.Where(x => !string.IsNullOrEmpty(x))
149+
.OfType<string>()
150+
#if false // On windows, we would filter against ManifestLanguages, but it does not apply to other targets.
151+
.Intersect(ManifestLanguages, FastBaseCultureComparer.Instance)
152+
#endif
153+
.ToArray();
154+
#else
155+
var languages = ManifestLanguages;
156+
#if __SKIA__
157+
if (OperatingSystem.IsWindows() && GlobalizationPreferences.Languages is { Count: > 0 } preferences)
110158
{
111-
string AdjustCultureName(string name)
112-
=> string.IsNullOrEmpty(name) ? "en-US" : name;
113-
114-
var languages = new[]
115-
{
116-
#if __ANDROID__
117-
ContextHelper.Current?.Resources?.Configuration?.Locales?.Get(0)?.ToLanguageTag(),
159+
languages = preferences;
160+
}
118161
#endif
119-
AdjustCultureName(CultureInfo.InstalledUICulture?.Name),
120-
AdjustCultureName(CultureInfo.CurrentUICulture?.Name),
121-
AdjustCultureName(CultureInfo.CurrentCulture?.Name)
122-
};
123162

124-
return languages
125-
.Where(l => !string.IsNullOrWhiteSpace(l))
163+
var overriddenLanguage = PrimaryLanguageOverride;
164+
if (!string.IsNullOrWhiteSpace(overriddenLanguage))
165+
{
166+
languages = languages
167+
.Prepend(overriddenLanguage)
126168
.Distinct()
127169
.ToArray();
128170
}
171+
172+
Languages = languages;
129173
#endif
174+
}
130175

131-
private static void ApplyLanguages()
132-
{
133-
var overridenLanguage = PrimaryLanguageOverride;
176+
private static Regex? _cultureFormatRegex;
134177

135-
if (string.IsNullOrWhiteSpace(overridenLanguage))
136-
{
137-
Languages = ManifestLanguages;
138-
}
139-
else
140-
{
141-
var manifestLanguages = ManifestLanguages.ToArray();
142-
var languages = new string[ManifestLanguages.Count + 1];
143-
languages[0] = overridenLanguage;
144-
if (manifestLanguages.Length > 0)
145-
{
146-
Array.Copy(manifestLanguages, 0, languages, 1, manifestLanguages.Length);
147-
}
148-
149-
Languages = languages.Distinct().ToArray();
150-
}
178+
private static CultureInfo CreateCulture(string cultureId)
179+
{
180+
try
181+
{
182+
return new CultureInfo(cultureId);
151183
}
152-
153-
private static Regex _cultureFormatRegex;
154-
155-
private static CultureInfo CreateCulture(string cultureId)
184+
catch (CultureNotFoundException)
156185
{
186+
_cultureFormatRegex ??= CultureRegex();
187+
188+
var match = _cultureFormatRegex.Match(cultureId);
157189
try
158190
{
159-
return new CultureInfo(cultureId);
191+
// If the script subtag is specified, we'll try to just remove it.
192+
// Mono is not supporting it.
193+
if (match.Groups["script"].Success && match.Groups["reg"].Success)
194+
{
195+
cultureId = $"{match.Groups["lang"].Value}-{match.Groups["reg"].Value}";
196+
return new CultureInfo(cultureId);
197+
}
160198
}
161199
catch (CultureNotFoundException)
162200
{
163-
_cultureFormatRegex ??= CultureRegex();
164-
165-
var match = _cultureFormatRegex.Match(cultureId);
166-
try
167-
{
168-
// If the script subtag is specified, we'll try to just remove it.
169-
// Mono is not supporting it.
170-
if (match.Groups["script"].Success && match.Groups["reg"].Success)
171-
{
172-
cultureId = $"{match.Groups["lang"].Value}-{match.Groups["reg"].Value}";
173-
return new CultureInfo(cultureId);
174-
}
175-
}
176-
catch (CultureNotFoundException)
177-
{
178-
}
179-
180-
// If the runtime is not able to match the language + region, we'll fallback to just the language.
181-
if (match.Groups["lang"].Success)
182-
{
183-
return new CultureInfo(match.Groups["lang"].Value);
184-
}
201+
}
185202

186-
// It seems not possible to resolve this culture.
187-
throw;
203+
// If the runtime is not able to match the language + region, we'll fallback to just the language.
204+
if (match.Groups["lang"].Success)
205+
{
206+
return new CultureInfo(match.Groups["lang"].Value);
188207
}
189-
}
190208

191-
[GeneratedRegex(@"(?<lang>[a-z]{2,8})(?:(?:\-(?<script>[a-zA-Z]+))?\-(?<reg>[A-Z]+))?", RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.Singleline | RegexOptions.CultureInvariant)]
192-
private static partial Regex CultureRegex();
209+
// It seems not possible to resolve this culture.
210+
throw;
211+
}
193212
}
213+
214+
[GeneratedRegex(@"(?<lang>[a-z]{2,8})(?:(?:\-(?<script>[a-zA-Z]+))?\-(?<reg>[A-Z]+))?", RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.Singleline | RegexOptions.CultureInvariant)]
215+
private static partial Regex CultureRegex();
194216
}

‎src/Uno.UWP/Globalization/ApplicationLanguages.iOS.cs

-21
This file was deleted.

‎src/Uno.UWP/Storage/StorageProvider.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,6 @@ internal StorageProvider(string id, string displayNameResourceKey)
2121

2222
public string Id { get; }
2323

24-
public string DisplayName => _resourceLoader.Value.GetString(_displayNameResourceKey);
24+
public string? DisplayName => _resourceLoader.Value.GetString(_displayNameResourceKey);
2525
}
2626
}
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,57 @@
1-
#if __ANDROID__
2-
using System;
1+
using System;
32
using System.Collections.Generic;
3+
using System.Runtime.InteropServices;
44
using System.Text;
5+
6+
#if __ANDROID__
57
using Java.Util;
8+
#elif __IOS__
9+
using Foundation;
10+
#elif __SKIA__
11+
using Windows.WinRT;
12+
#endif
613

7-
namespace Windows.System.UserProfile
14+
namespace Windows.System.UserProfile;
15+
16+
public static partial class GlobalizationPreferences
817
{
9-
public static partial class GlobalizationPreferences
18+
19+
#if __ANDROID__ || __IOS__ || __SKIA__
20+
public static IReadOnlyList<string> Languages =>
21+
#if __ANDROID__
22+
new[] { Locale.Default.ToLanguageTag() };
23+
#elif __IOS__
24+
NSLocale.PreferredLanguages;
25+
#elif __SKIA__
26+
OperatingSystem.IsWindows() ? GetWinUserLanguageList() : throw new PlatformNotSupportedException();
27+
#endif
28+
#endif
29+
30+
#if __SKIA__
31+
private static string[] GetWinUserLanguageList()
1032
{
11-
public static IReadOnlyList<string> Languages => new[] { Locale.Default.ToLanguageTag() };
33+
if (NativeMethods.EnsureLanguageProfileExists() >= 0)
34+
{
35+
const char Delimiter = ';';
36+
if (NativeMethods.GetUserLanguages(Delimiter, out var handle) >= 0)
37+
{
38+
var languages = MarshalString.FromAbi(handle).Split(Delimiter);
39+
MarshalString.DisposeAbi(handle);
40+
41+
return languages;
42+
}
43+
}
44+
45+
return Array.Empty<string>();
46+
}
47+
48+
private static class NativeMethods
49+
{
50+
[DllImport("winlangdb.dll", CharSet = CharSet.Unicode, SetLastError = true)]
51+
public static extern int EnsureLanguageProfileExists();
52+
53+
[DllImport("bcp47langs.dll", CharSet = CharSet.Unicode, SetLastError = true)]
54+
public static extern int GetUserLanguages(char Delimiter, out IntPtr UserLanguages);
1255
}
13-
}
1456
#endif
57+
}

‎src/Uno.UWP/System/UserProfile/GlobalizationPreferences.iOS.cs

-12
This file was deleted.
+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Runtime.InteropServices;
5+
using System.Text;
6+
using System.Threading.Tasks;
7+
using Windows.Foundation.Metadata;
8+
9+
namespace Windows.WinRT;
10+
11+
internal class MarshalString
12+
{
13+
public unsafe static string FromAbi(IntPtr value)
14+
{
15+
if (value == IntPtr.Zero)
16+
{
17+
return "";
18+
}
19+
uint length = default(uint);
20+
char* value2 = Platform.WindowsGetStringRawBuffer(value, &length);
21+
return new string(value2, 0, (int)length);
22+
}
23+
24+
public static void DisposeAbi(IntPtr hstring)
25+
{
26+
if (hstring != IntPtr.Zero)
27+
{
28+
_ = Platform.WindowsDeleteString(hstring);
29+
}
30+
}
31+
32+
private static class Platform
33+
{
34+
[DllImport("api-ms-win-core-winrt-string-l1-1-0.dll", CallingConvention = CallingConvention.StdCall)]
35+
internal unsafe static extern char* WindowsGetStringRawBuffer(IntPtr hstring, uint* length);
36+
37+
[DllImport("api-ms-win-core-winrt-string-l1-1-0.dll", CallingConvention = CallingConvention.StdCall)]
38+
internal static extern int WindowsDeleteString(IntPtr hstring);
39+
}
40+
}

0 commit comments

Comments
 (0)
Please sign in to comment.