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));
    }
}
相关推荐
c#上位机1 天前
wpf之RadialGradientBrush径向渐变画刷
wpf
不懂的浪漫2 天前
OpenTelemetry 和 SkyWalking Agent 怎么选?一次讲清 OTel、SkyWalking Agent 的相同点与区别
wpf·skywalking·链路追踪·opentelemetry·otel
c#上位机2 天前
wpf之LinearGradientBrush线性渐变
wpf
暖馒3 天前
WPF绑定由简到繁深入笔记
笔记·wpf
东方.既白3 天前
WPF炫酷界面DEMO
wpf
海盗12343 天前
WPF中OxyPlot不同图表的使用
wpf
czhc11400756634 天前
wpf 511 封装通信类 半导体协议:SECS
wpf
lingxiao168884 天前
WPF数据采集和监控(Industrial)
wpf
雨浓YN4 天前
GKTGD 工业监控系统-02MySQL 数据库技术文档(类库:NET8_SQLData)
数据库·wpf
雨浓YN4 天前
GKTGD 工业监控系统-03SQLite 数据库技术文档(类库:NET8_SQLData)
数据库·wpf