WPF 国际化(全球化)管理

resx资源文件

单个resx扩展

复制代码
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Data;
using System.Windows.Markup;


    /// <summary>
    /// XAML 用法:
    /// xmlns:ext="clr-namespace:自己的命名空间.Resources"
    /// {ext:Localize MainPage_Menu_Start} 
    /// Model用法:
    /// LocalizeExtension.Instance["MainPage_Menu_DataPacking"];
    /// </summary>
    internal sealed class LocalizeExtension : MarkupExtension, INotifyPropertyChanged
    {
        public static LocalizeExtension Instance { get; } = new();

        public static void ChangeLanguage(CultureInfo ci)
        {
            Resources.Langs.Lang.Culture = ci;
            OnLanguageChanged?.Invoke();
        }

        public static event Action? OnLanguageChanged;

        private string Key { get; }

        public string Result => GetString(Key);
        public string this[string key] => GetString(key);

        public static string GetString(string key)
        {
            return Resources.Langs.Lang.ResourceManager.GetString(key, Resources.Langs.Lang.Culture)?.Replace("\\n", "\n") ?? $"#{key}#";
        }

        private LocalizeExtension()
        {
            Key = string.Empty;
        }

        public LocalizeExtension(string key)
        {
            Key = key;

            OnLanguageChanged += () => {
                OnPropertyChanged(nameof(Result));
            };
        }

        public override object ProvideValue(IServiceProvider serviceProvider)
        {
            var binding = new Binding(nameof(Result))
            {
                Source = this,
                Mode = BindingMode.OneWay
            };
            return binding.ProvideValue(serviceProvider);
        }

        public event PropertyChangedEventHandler? PropertyChanged;

        private void OnPropertyChanged([CallerMemberName] string? propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }

多个resx资源扩展

复制代码
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.Runtime.CompilerServices;
using System.Windows.Data;
using System.Windows.Markup;


{
   /// <summary>
/// XAML 用法:
/// xmlns:ext="clr-namespace:自己的命名空间.Resources"
/// 1:{ext:Localize Key=Main_Title, Source=GUI}
/// 2:{ext:Localize MainPage_Menu_Start}    --默认资源组(Lang)
/// Model用法:
/// 1:LocalizeExtension.Instance["MainPage_Menu_DataPacking"];
/// 2:LocalizeExtension.Instance["GUI", nameof(Layout.Main_Nav_Title)];
/// </summary>
public sealed class LocalizeExtension : MarkupExtension, INotifyPropertyChanged
{
    public static LocalizeExtension Instance { get; } = new LocalizeExtension();

    public static event Action? OnLanguageChanged;
  
    private const string DefaultSource = "Lang";
  
    private static readonly Dictionary<string, System.Resources.ResourceManager> ResourceManagers =
        new(StringComparer.OrdinalIgnoreCase)
        {
            ["Lang"] = Lang.ResourceManager,
            ["GUI"] = Layout.ResourceManager,
        };
   
    public LocalizeExtension(string key)
    {
        Key = key;
        Source = DefaultSource;
        OnLanguageChanged += RaiseResultChanged;
    }
    
    public LocalizeExtension()
    {
        Key = string.Empty;
        Source = DefaultSource;
        OnLanguageChanged += RaiseResultChanged;
    }
    public string Key { get; set; } = string.Empty;

    public string Source { get; set; } = DefaultSource;

    public string this[string key] => GetString(key, DefaultSource);
    
    public string this[string source, string key] => GetString(key, source);

    public string Result => GetString(Key, Source);

    public override object ProvideValue(IServiceProvider serviceProvider)
    {
        var pvt = serviceProvider.GetService(typeof(IProvideValueTarget)) as IProvideValueTarget;
        var targetObject = pvt?.TargetObject;
        var targetProperty = pvt?.TargetProperty;
       
        // targetObject 是 Binding,必须返回"对象",不能返回 BindingExpression
        if (targetObject is Binding)
        {
            return this;
        }

        // 正常给 DependencyProperty 赋值,返回内部绑定实现动态刷新
        if (targetObject is DependencyObject && targetProperty is DependencyProperty)
        {
            var binding = new Binding(nameof(Result))
            {
                Source = this,
                Mode = BindingMode.OneWay
            };
            return binding.ProvideValue(serviceProvider);
        }
        return Result;
    }
   
    public static void ChangeLanguage(CultureInfo culture)
    {
        Lang.Culture = culture;
        Layout.Culture = culture;
        OnLanguageChanged?.Invoke();
    }
 
    public static string GetString(string key, string? source = null)
    {
        if (string.IsNullOrWhiteSpace(key))
            return string.Empty;
        var src = string.IsNullOrWhiteSpace(source) ? DefaultSource : source;
        if (!ResourceManagers.TryGetValue(src!, out var rm))
            return $"#InvalidSource:{src}#{key}#";
      
        var value = rm.GetString(key, Lang.Culture);
        return value?.Replace("\\n", "\n") ?? $"#{src}:{key}#";
    }
   
    public static void RegisterSource(string source, System.Resources.ResourceManager manager)
    {
        if (string.IsNullOrWhiteSpace(source))
            throw new ArgumentException("source is required.", nameof(source));
        ResourceManagers[source] = manager ?? throw new ArgumentNullException(nameof(manager));
    }

    public event PropertyChangedEventHandler? PropertyChanged;

    private void RaiseResultChanged()
    {
        OnPropertyChanged(nameof(Result));
    }

    private void OnPropertyChanged([CallerMemberName] string? propName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));
    }
}
相关推荐
nashane1 天前
HarmonyOS 6.0 分布式数据库进阶:设备协同与高效数据同步实战(API 11 Stage 模型)
wpf·harmonyos 5
极客智造1 天前
WPF InputBindings MVVM详解
wpf
nashane1 天前
HarmonyOS 6.0 分布式数据实战:KVStore跨设备同步与高性能查询指南(API 11 Stage模型)
wpf·harmonyos 5
SEO-狼术2 天前
Easily Reorder Rows in WPF Grids
wpf
烟话66 天前
MVVM核心机制:属性通知与命令绑定解析
wpf
不知名君6 天前
WPF 的原生窗体标题栏主题自适应系统深浅主题
wpf
碎碎念的安静6 天前
WPF 与 Qt 进程间通信(IPC)
开发语言·qt·wpf
军训猫猫头7 天前
7.带输入参数的线程启动 C# + WPF 完整示例
开发语言·前端·c#·.net·wpf
周杰伦fans8 天前
WPF Prism 框架完全入门指南:从环境搭建到弹窗导航实战
wpf