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));
    }
}
相关推荐
FuckPatience2 天前
WPF 窗体唤醒,判断是否已存在,存在则弹出
wpf
程序员清风2 天前
从单体应用到微服务:后端系统拆分的基本方法
微服务·架构·wpf
FuckPatience3 天前
WPF 改变根据某一列条件改变行样式
wpf
dalong103 天前
WPF:DataGrid样式
wpf
samble5 天前
从零搭建工业控制系统(二十二):线程安全与并发控制
c#·wpf·并发·mvvm·线程安全·工业控制
姜穆澜5 天前
OneID 从 0 到 1 完整生产案例(三)
大数据·wpf
dalong105 天前
WPF:MVVM 示例
大数据·wpf
samble6 天前
从零搭建工业控制系统(二十一):状态管理系统——中心状态对象设计
c#·wpf·mvvm·状态管理·工业控制
mengge.cloud6 天前
存储技术基础小白教程
linux·运维·服务器·wpf·存储
DreamLife☼7 天前
复杂Agent系统架构设计
系统架构·wpf·agent·组件·设计·构架·说明