将Feature/XP.Common和Feature/XP.Hardware分支合并至Develop/XP.forHardwareAndCommon,完善XPapp注册和相关硬件类库通用类库功能。

This commit is contained in:
QI Mingxuan
2026-04-16 17:31:13 +08:00
parent 6ec4c3ddaa
commit 2bd6e566c3
581 changed files with 74600 additions and 222 deletions
+265
View File
@@ -0,0 +1,265 @@
# 多语言支持快速开始 | 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
```xml
<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 文件顶部添加命名空间引用:
```xml
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
适用于不方便依赖注入的场景(静态方法、工具类等)。
```csharp
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 启动时调用初始化。
```csharp
// 在 CommonModule 或 App 启动时调用 | Call at CommonModule or App startup
LocalizationHelper.Initialize(localizationService);
```
---
### 3. 在 ViewModel 中使用本地化服务 | Using Localization Service in ViewModel
#### 依赖注入 | Dependency Injection
```csharp
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
```csharp
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
```csharp
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` 中注册:
```csharp
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
```csharp
// 注销指定资源源(不可注销默认的 "XP.Common"
_localizationService.UnregisterResourceSource("XP.Scan");
```
#### 注意事项 | Notes
- 资源源名称不可重复,重复注册会抛出 `InvalidOperationException`
- 注销 `"XP.Common"` 会抛出 `InvalidOperationException`
- 注销不存在的名称会静默忽略并记录警告日志
- 线程安全:内部使用 `ReaderWriterLockSlim` 保护读写操作
---
**版本 | Version:** 1.1
**最后更新 | Last Updated:** 2026-04-01