Files
XplorePlane/XP.Common/Documents/Localization.README.md

8.1 KiB
Raw Permalink Blame History

多语言支持快速开始 | Localization Quick Start

概述 | Overview

XplorePlane 多语言支持系统基于 .NET 原生 Resx 资源文件实现,与 Prism MVVM 架构无缝集成。系统支持简体中文(zh-CN)、繁体中文(zh-TW)和美式英语(en-US)三种语言。

核心特性 | Key Features

  • 基于 .NET Resx 资源文件,编译时类型安全
  • 简洁的 XAML 标记扩展语法
  • 完整的 ViewModel 集成支持
  • 语言设置持久化到 App.config
  • 跨模块事件通知机制
  • 健壮的错误处理和回退机制
  • 多资源源 Fallback Chain 机制,支持模块级资源注册

快速开始 | Quick Start

1. 在 XAML 中使用本地化资源 | Using Localization in XAML

基础用法 | Basic Usage

<Window x:Class="XplorePlane.App.Views.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:loc="clr-namespace:XplorePlane.Common.Localization.Extensions;assembly=XP.Common"
        Title="{loc:Localization Key=App_Title}">

    <Grid>
        <!-- 按钮文本本地化 | Localized button text -->
        <Button Content="{loc:Localization Key=Button_OK}" />

        <!-- 标签文本本地化 | Localized label text -->
        <Label Content="{loc:Localization Key=Settings_Language}" />

        <!-- 菜单项本地化 | Localized menu item -->
        <MenuItem Header="{loc:Localization Key=Menu_File}" />
    </Grid>
</Window>

命名空间声明 | Namespace Declaration

在 XAML 文件顶部添加命名空间引用:

xmlns:loc="clr-namespace:XplorePlane.Common.Localization.Extensions;assembly=XP.Common"

语法说明 | Syntax Explanation

  • {loc:Localization Key=ResourceKey} - 完整语法
  • {loc:Localization App_Title} - 简化语法(Key 可省略)
  • 资源键不存在时,显示键名本身(便于调试)

2. 在 C# 代码中使用静态帮助类 | Using Static Helper in C# Code

适用于不方便依赖注入的场景(静态方法、工具类等)。

using XP.Common.Localization;

// 基本用法 | Basic usage
var title = LocalizationHelper.Get("App_Title");

// 带格式化参数 | With format arguments
var errorMsg = LocalizationHelper.Get("Settings_Language_SwitchFailed", ex.Message);
  • 在 ViewModel / Service 中优先使用 ILocalizationService(可测试、可 Mock
  • 在静态方法、工具类、或不方便注入的地方使用 LocalizationHelper
  • 两者读取同一套 Resx 资源文件,结果一致

V1.4.1.1 变更: LocalizationHelper 新增 Initialize(ILocalizationService) 方法。初始化后,Get() 会优先通过 ILocalizationService 获取字符串(支持 Fallback Chain);未初始化时仍兼容回退到原始 ResourceManager。建议在 CommonModule 或 App 启动时调用初始化。

// 在 CommonModule 或 App 启动时调用 | Call at CommonModule or App startup
LocalizationHelper.Initialize(localizationService);

3. 在 ViewModel 中使用本地化服务 | Using Localization Service in ViewModel

依赖注入 | Dependency Injection

using XplorePlane.Common.Localization.Interfaces;
using XplorePlane.Common.Localization.Enums;
using Prism.Mvvm;

namespace XplorePlane.App.ViewModels
{
    public class MyViewModel : BindableBase
    {
        private readonly ILocalizationService _localizationService;
        private readonly ILoggerService _logger;

        public MyViewModel(
            ILocalizationService localizationService,
            ILoggerService logger)
        {
            _localizationService = localizationService;
            _logger = logger;
        }

        // 获取本地化字符串 | Get localized string
        public string GetWelcomeMessage()
        {
            return _localizationService.GetString("Welcome_Message");
        }

        // 获取当前语言 | Get current language
        public SupportedLanguage CurrentLanguage => _localizationService.CurrentLanguage;
    }
}

动态文本绑定 | Dynamic Text Binding

public class StatusViewModel : BindableBase
{
    private readonly ILocalizationService _localizationService;
    private string _statusMessage;

    public string StatusMessage
    {
        get => _statusMessage;
        set => SetProperty(ref _statusMessage, value);
    }

    public StatusViewModel(ILocalizationService localizationService)
    {
        _localizationService = localizationService;

        // 订阅语言切换事件 | Subscribe to language changed event
        _localizationService.LanguageChanged += OnLanguageChanged;

        // 初始化状态消息 | Initialize status message
        UpdateStatusMessage();
    }

    private void OnLanguageChanged(object sender, LanguageChangedEventArgs e)
    {
        // 语言切换时更新文本 | Update text when language changes
        UpdateStatusMessage();
    }

    private void UpdateStatusMessage()
    {
        StatusMessage = _localizationService.GetString("Status_Ready");
    }
}

数据验证消息本地化 | Localized Validation Messages

public class FormViewModel : BindableBase, IDataErrorInfo
{
    private readonly ILocalizationService _localizationService;
    private string _username;

    public string Username
    {
        get => _username;
        set => SetProperty(ref _username, value);
    }

    public string this[string columnName]
    {
        get
        {
            if (columnName == nameof(Username))
            {
                if (string.IsNullOrWhiteSpace(Username))
                {
                    return _localizationService.GetString("Validation_UsernameRequired");
                }
                if (Username.Length < 3)
                {
                    return _localizationService.GetString("Validation_UsernameTooShort");
                }
            }
            return null;
        }
    }

    public string Error => null;
}

4. 多资源源 Fallback Chain | Multi-Source Fallback Chain

V1.1 版本引入了多资源源 Fallback Chain 机制,允许各模块注册自己的 Resx 资源文件。查找资源键时,从最后注册的资源源开始向前遍历,第一个返回非 null 值的即为结果。

架构说明 | Architecture

Fallback Chain(查找顺序从右到左):
[XP.Common (默认)] → [XP.Scan (模块注册)] → [XP.Hardware (模块注册)]
                                                ↑ 最高优先级
  • XP.Common 为默认资源源,始终位于 Chain[0],不可注销
  • 后注册的模块优先级更高
  • 单个资源源查找异常时自动跳过,继续遍历下一个
  • 全部未找到时返回 key 本身并记录警告日志

注册模块资源源 | Register Module Resource Source

在 Prism 模块的 OnInitialized 中注册:

using System.Resources;
using XP.Common.Localization.Interfaces;

public class ScanModule : IModule
{
    private readonly ILocalizationService _localizationService;

    public ScanModule(ILocalizationService localizationService)
    {
        _localizationService = localizationService;
    }

    public void OnInitialized(IContainerProvider containerProvider)
    {
        // 注册模块资源源到 Fallback Chain
        var resourceManager = new ResourceManager(
            "XP.Scan.Resources.Resources",
            typeof(ScanModule).Assembly);
        _localizationService.RegisterResourceSource("XP.Scan", resourceManager);
    }

    public void RegisterTypes(IContainerRegistry containerRegistry) { }
}

注销模块资源源 | Unregister Module Resource Source

// 注销指定资源源(不可注销默认的 "XP.Common"
_localizationService.UnregisterResourceSource("XP.Scan");

注意事项 | Notes

  • 资源源名称不可重复,重复注册会抛出 InvalidOperationException
  • 注销 "XP.Common" 会抛出 InvalidOperationException
  • 注销不存在的名称会静默忽略并记录警告日志
  • 线程安全:内部使用 ReaderWriterLockSlim 保护读写操作

版本 | Version: 1.1
最后更新 | Last Updated: 2026-04-01