From f7ebec093e09cab8c7debfc8723baaf7db816bef Mon Sep 17 00:00:00 2001 From: Dong Bin Date: Fri, 17 Jan 2025 16:48:58 +0800 Subject: [PATCH 1/3] feat: add language switch. remove lazying loading dictionary. format code. Remove useless fields. --- src/Ursa.Themes.Semi/Index.axaml.cs | 72 ++++++++++++++--------------- 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/src/Ursa.Themes.Semi/Index.axaml.cs b/src/Ursa.Themes.Semi/Index.axaml.cs index 2112116..a478b5a 100644 --- a/src/Ursa.Themes.Semi/Index.axaml.cs +++ b/src/Ursa.Themes.Semi/Index.axaml.cs @@ -1,4 +1,5 @@ using System.Globalization; +using Avalonia; using Avalonia.Controls; using Avalonia.Markup.Xaml; using Avalonia.Styling; @@ -7,32 +8,30 @@ using Ursa.Themes.Semi.Locale; namespace Ursa.Themes.Semi; /// -/// Notice: Don't set Locale if your app is in InvariantGlobalization mode. +/// Notice: Don't set Locale if your app is in InvariantGlobalization mode. /// -public class SemiTheme: Styles +public class SemiTheme : Styles { - public static ThemeVariant Aquatic => new ThemeVariant(nameof(Aquatic), ThemeVariant.Dark); - public static ThemeVariant Desert => new ThemeVariant(nameof(Desert), ThemeVariant.Light); - public static ThemeVariant Dusk => new ThemeVariant(nameof(Dusk), ThemeVariant.Dark); - public static ThemeVariant NightSky => new ThemeVariant(nameof(NightSky), ThemeVariant.Dark); - - private static readonly Lazy> _localeToResource = new Lazy>( - () => new Dictionary - { - { new CultureInfo("zh-CN"), new zh_cn() }, - { new CultureInfo("en-US"), new en_us() }, - }); - + private static readonly Dictionary _localeToResource = new() + { + { new CultureInfo("zh-CN"), new zh_cn() }, + { new CultureInfo("en-US"), new en_us() } + }; + private static readonly ResourceDictionary _defaultResource = new zh_cn(); - private readonly IServiceProvider? _sp; + private CultureInfo? _locale; + public SemiTheme(IServiceProvider? provider = null) { - _sp = provider; AvaloniaXamlLoader.Load(provider, this); } - private CultureInfo? _locale; + public static ThemeVariant Aquatic => new(nameof(Aquatic), ThemeVariant.Dark); + public static ThemeVariant Desert => new(nameof(Desert), ThemeVariant.Light); + public static ThemeVariant Dusk => new(nameof(Dusk), ThemeVariant.Dark); + public static ThemeVariant NightSky => new(nameof(NightSky), ThemeVariant.Dark); + public CultureInfo? Locale { get => _locale; @@ -43,33 +42,34 @@ public class SemiTheme: Styles _locale = value; var resource = TryGetLocaleResource(value); if (resource is null) return; - foreach (var kv in resource) - { - this.Resources.Add(kv); - } + foreach (var kv in resource) Resources.Add(kv); } catch { _locale = CultureInfo.InvariantCulture; } - } } - + private static ResourceDictionary? TryGetLocaleResource(CultureInfo? locale) { - if (Equals(locale, CultureInfo.InvariantCulture)) - { - return _defaultResource; - } - if (locale is null) - { - return _localeToResource.Value[new CultureInfo("zh-CN")]; - } - if (_localeToResource.Value.TryGetValue(locale, out var resource)) - { - return resource; - } - return _localeToResource.Value[new CultureInfo("zh-CN")]; + if (Equals(locale, CultureInfo.InvariantCulture)) return _defaultResource; + if (locale is null) return _localeToResource[new CultureInfo("zh-CN")]; + if (_localeToResource.TryGetValue(locale, out var resource)) return resource; + return _localeToResource[new CultureInfo("zh-CN")]; + } + + public static void OverrideLocaleResources(Application application, CultureInfo? culture) + { + if (culture is null) return; + if (!_localeToResource.TryGetValue(culture, out var resources)) return; + foreach (var kv in resources) application.Resources[kv.Key] = kv.Value; + } + + public static void OverrideLocaleResources(StyledElement element, CultureInfo? culture) + { + if (culture is null) return; + if (!_localeToResource.TryGetValue(culture, out var resources)) return; + foreach (var kv in resources) element.Resources[kv.Key] = kv.Value; } } \ No newline at end of file From 58f51e15f391bd7890845cbefe119856fe56906c Mon Sep 17 00:00:00 2001 From: Dong Bin Date: Fri, 17 Jan 2025 18:05:01 +0800 Subject: [PATCH 2/3] feat: add unit test. fix various issues discovered in test. --- src/Ursa.Themes.Semi/Index.axaml.cs | 42 ++++++-- .../Semi/LocalizationTest.cs | 101 ++++++++++++++++++ 2 files changed, 133 insertions(+), 10 deletions(-) create mode 100644 tests/HeadlessTest.Ursa/Semi/LocalizationTest.cs diff --git a/src/Ursa.Themes.Semi/Index.axaml.cs b/src/Ursa.Themes.Semi/Index.axaml.cs index a478b5a..efa2038 100644 --- a/src/Ursa.Themes.Semi/Index.axaml.cs +++ b/src/Ursa.Themes.Semi/Index.axaml.cs @@ -19,7 +19,7 @@ public class SemiTheme : Styles }; private static readonly ResourceDictionary _defaultResource = new zh_cn(); - + private CultureInfo? _locale; public SemiTheme(IServiceProvider? provider = null) @@ -39,10 +39,16 @@ public class SemiTheme : Styles { try { - _locale = value; - var resource = TryGetLocaleResource(value); - if (resource is null) return; - foreach (var kv in resource) Resources.Add(kv); + if (TryGetLocaleResource(value, out var resource) && resource is not null) + { + _locale = value; + foreach (var kv in resource) Resources[kv.Key] = kv.Value; + } + else + { + _locale = new CultureInfo("zh-CN"); + foreach (var kv in _defaultResource) Resources[kv.Key] = kv.Value; + } } catch { @@ -51,12 +57,28 @@ public class SemiTheme : Styles } } - private static ResourceDictionary? TryGetLocaleResource(CultureInfo? locale) + private static bool TryGetLocaleResource(CultureInfo? locale, out ResourceDictionary? resourceDictionary) { - if (Equals(locale, CultureInfo.InvariantCulture)) return _defaultResource; - if (locale is null) return _localeToResource[new CultureInfo("zh-CN")]; - if (_localeToResource.TryGetValue(locale, out var resource)) return resource; - return _localeToResource[new CultureInfo("zh-CN")]; + if (Equals(locale, CultureInfo.InvariantCulture)) + { + resourceDictionary = _defaultResource; + return true; + } + + if (locale is null) + { + resourceDictionary = _defaultResource; + return false; + } + + if (_localeToResource.TryGetValue(locale, out var resource)) + { + resourceDictionary = resource; + return true; + } + + resourceDictionary = _defaultResource; + return false; } public static void OverrideLocaleResources(Application application, CultureInfo? culture) diff --git a/tests/HeadlessTest.Ursa/Semi/LocalizationTest.cs b/tests/HeadlessTest.Ursa/Semi/LocalizationTest.cs new file mode 100644 index 0000000..fd6c0df --- /dev/null +++ b/tests/HeadlessTest.Ursa/Semi/LocalizationTest.cs @@ -0,0 +1,101 @@ +using System.Globalization; +using Avalonia; +using Avalonia.Controls; +using Avalonia.Headless.XUnit; +using Avalonia.Threading; +using Avalonia.VisualTree; +using Semi.Avalonia.Locale; +using Ursa.Controls; +using Ursa.Themes.Semi; + +namespace HeadlessTest.Ursa.Semi; + +public class LocalizationTest +{ + [AvaloniaFact] + public void Default_Locale_Is_Chinese() + { + var window = new UrsaWindow(); + window.Show(); + MessageBox.ShowOverlayAsync("Hello World", button: MessageBoxButton.YesNo, toplevelHashCode: window.GetHashCode()); + Task.Delay(100).Wait(); + Dispatcher.UIThread.RunJobs(); + var dialog = window.GetVisualDescendants().OfType().SingleOrDefault(); + var yesButton = dialog?.GetVisualDescendants().OfType