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));
    }
}
相关推荐
Chris _data11 天前
WPF 学习第三天 — Modbus RTU 串口通信
hadoop·学习·wpf
布吉岛的石头12 天前
Java 程序员第 43 阶段05:微服务整合大模型,跨服务调用架构设计实战,Seata分布式事务实战
wpf
步步为营DotNet12 天前
基于.NET Aspire 实现云原生应用的高效监控与可观测性
云原生·.net·wpf
芒鸽12 天前
HarmonyOS 分布式开发实战:设备协同、数据共享与跨设备迁移
分布式·wpf·harmonyos
Volunteer Technology12 天前
Flink状态管理与容错(二)
大数据·flink·wpf
happyprince13 天前
07_verl-Trainer模块详解
人工智能·架构·wpf·强化学习
bugcome_com13 天前
WPF + Prism 技术指南与实战项目(二、模板搭建)
wpf
小满Autumn13 天前
log4net 日志框架 — 从配置到实战速查手册
笔记·c#·.net·wpf·上位机·log4net
政沅同学14 天前
基于 C# WPF + HALCON 的工业视觉算法工具框架(开源)
开发语言·c#·wpf
happyprince14 天前
03_verl-设计理念与核心原理
wpf