SeatFlow 基于 .NET .resx 的国际化系统架构、资源文件结构、XAML/C# 使用规范和 i18n.py 脚本工具
本页目录
全部文档
国际化系统
架构概述
SeatFlow 使用标准 .NET .resx 资源文件实现国际化。资源文件位于 SeatFlow.Presentation.Avalonia/Lang/ 目录,由一个中性语言文件、一个英文卫星文件和手动维护的强类型访问器类组成。
SeatFlow.Presentation.Avalonia/Lang/
├── Resources.resx # 中性语言(zh-CN),约 700 个键
├── Resources.en-US.resx # 英文卫星程序集
├── Resources.Designer.cs # 强类型访问器(手动维护)
└── .backup/ # 自动备份目录(已 gitignore)
设计决策
.resx格式而非.json或.po,因为 .NET 原生支持且与{x:Static}在 Avalonia XAML 中完美集成- 不使用 Visual Studio 的
PublicResXFileCodeGenerator(与dotnet build不兼容),改用Designer.cs手动维护 - 使用
python3 scripts/i18n.py脚本统一管理三文件的增删改查和同步
资源键命名规范
格式:{Category}_{MeaningfulName}(PascalCase,下划线分隔)
已知分类前缀
| 前缀 | 用途 |
|---|---|
About_ |
关于对话框 |
App_ |
应用级消息 |
Common_ |
共享 UI 标签(如 Common_OK、Common_Cancel) |
ConfigBlock_ |
配置块 UI |
Data_ |
数据加载/保存 |
Freeform_ |
自由布局管理 |
Gender_ |
性别标签 |
Guide_ |
引导系统 |
Home_ |
首页 |
Lang_ |
语言名称 |
Member_ |
成员管理 |
Nav_ |
导航栏 |
Plugin_ |
插件管理 |
Seating_ |
座位安排 |
Settings_ |
设置页 |
Snapshot_ |
快照历史 |
Startup_ |
启动守卫 |
Strategy_ |
策略配置 |
Theme_ |
主题名称 |
Venue_ |
会场配置 |
Watchdog_ |
看门狗服务 |
Zoom_ |
缩放级别 |
格式字符串
资源值中使用 {0}、{1} 等占位符,键名建议以 Fmt 结尾标识。
<data name="Snapshot_VenuesLoadedFmt" xml:space="preserve">
<value>已加载 {0} 个会场</value>
</data>
当前支持语言
| 语言 | 文件 | 状态 |
|---|---|---|
| 中文(简体) | Resources.resx(中性语言) |
完整,~700 键 |
| 英语(美国) | Resources.en-US.resx |
持续维护 |
添加新语言
- 在
Lang/目录下创建Resources.xx-XX.resx文件(例如Resources.ja-JP.resx) - 将所有键翻译为目标语言
- 运行
python3 scripts/i18n.py check验证一致性 - 无需修改 C# 代码——.NET 运行时自动按
CultureInfo.CurrentUICulture加载对应卫星程序集 - 在
AppSettings.Language中添加对应语言代码
XAML 中使用
属性语法(推荐,编译绑定支持)
<TextBlock Text="{x:Static lang:Resources.Settings_Title}" />
<Button Content="{x:Static lang:Resources.Common_OK}" />
命名空间声明
xmlns:lang="using:SeatFlow.Presentation.Avalonia.Lang"
重要限制
- 仅支持属性语法:
<TextBlock Text="{x:Static ...}" /> - 不支持元素内容语法:以下写法不会正确解析
<!-- 错误:元素内容语法不适用于 {x:Static} -->
<Button>
<x:Static xmlns:lang="..." Member="lang:Resources.Common_OK" />
</Button>
C# 中使用
标准用法
using SeatFlow.Presentation.Avalonia.Lang;
// 直接引用
StatusMessage = Resources.Settings_Saved;
// 带格式参数
StatusMessage = string.Format(Resources.Snapshot_VenuesLoadedFmt, count);
// 条件判断
if (Resources.Culture.TwoLetterISOLanguageName == "zh")
{
// 中文逻辑
}
Window 子类中的特殊处理
在继承自 Window 的类(DialogWindow、InputWindow)中,Resources 解析为 Window.Resources(IResourceDictionary)。必须使用完全限定名:
// 正确:在 Window 子类中使用完全限定名
string title = Lang.Resources.Settings_Title;
// 错误:会产生编译错误
string title = Resources.Settings_Title; // 解析为 Window.Resources
ViewModel 中的使用
ViewModel 继承自 ViewModelBase(继承 ObservableObject),不存在 Window.Resources 冲突,直接使用 Resources.xxx 即可。
语言切换流程
语言切换在应用启动时通过 App.ApplyLanguageFromSettings() 完成:
App.Initialize()
├── ApplyLanguageFromSettings() ← 必须在 XAML 加载前调用
│ ├── 从 AppSettings 读取 Language 配置
│ ├── 设置 CultureInfo.CurrentUICulture
│ └── 设置 Resources.Culture
└── AvaloniaXamlLoader.Load(this) ← XAML 加载时 {x:Static} 已正确解析
语言设置在 AppSettings.Language 中持久化,用户通过设置页面修改,重启后生效。
策略/插件内部 i18n
策略 manifest 中的用户可见文字使用内联 i18n 词典格式,而非 .resx 键:
{
"label": { "zh-CN": "历史窗口大小", "en-US": "History Window Size" },
"messages": {
"DeskMate_Split": {
"zh-CN": "同桌组({0})中的 {1} 已被前排策略分配",
"en-US": "Desk-mate group ({0}) member(s) {1} already assigned"
}
}
}
LocalizeHelper.Resolve
Presentation 层通过 LocalizeHelper.Resolve(dict) 方法解析:
public static string Resolve(Dictionary<string, string> localizedDict)
{
if (localizedDict.TryGetValue(CultureInfo.CurrentUICulture.Name, out var value))
return value;
if (localizedDict.TryGetValue("zh-CN", out var fallback))
return fallback;
return localizedDict.Values.FirstOrDefault() ?? "";
}
解析顺序:
- 精确匹配
CultureInfo.CurrentUICulture.Name(如en-US) - 回退到
zh-CN - 回退到第一个可用值
内置策略和插件策略共用同一机制——消息模板在 Manifests/{Id}.json 中声明,插件策略的在各插件包策略子目录下的 manifest.json 中声明。
i18n.py 脚本工具
scripts/i18n.py 统一管理三文件(zh-CN .resx、en-US .resx、Designer.cs)的增删改查和同步。所有命令从 scripts/ 目录执行。
常用命令
cd scripts
# 列出所有键
python3 i18n.py list
# 查找未翻译的键(zh-CN == en-US)
python3 i18n.py list --missing-en
# 搜索匹配特定模式的键
python3 i18n.py list --pattern "Export"
# 列出含格式占位符的键
python3 i18n.py list --format-strings
# 按分类过滤
python3 i18n.py list --category Settings
# 导出为 JSON 或 CSV
python3 i18n.py list --output json
python3 i18n.py list --output csv > translations.csv
校验
# 一致性校验
python3 i18n.py check
# 自动修复排序问题
python3 i18n.py check --fix
校验项包括:
- 三文件 key 集合一致性
- key 命名规范(Category_MeaningfulName PascalCase)
- zh-CN 和 en-US 格式字符串参数数量匹配
- 空值检测
- XML 重复 key 检测
增删改查
# 添加新键
python3 i18n.py add Settings_NewOption --zh "新选项" --en "New Option"
python3 i18n.py add Common_NewAction --zh "执行操作" --en "Execute" --comment "工具栏按钮"
# 修改已有键
python3 i18n.py modify Settings_Title --zh "系统设置"
python3 i18n.py modify Settings_Title --en "System Settings"
# 重命名键(三文件同步)
python3 i18n.py rename Seating_Title Seating_WindowTitle
# 删除键
python3 i18n.py delete Obsolete_Key
python3 i18n.py delete Temp_DebugKey --force
Designer.cs 同步
# 预览变更
python3 i18n.py sync --dry-run
# 执行同步
python3 i18n.py sync
从 Resources.resx 的 key 列表重新生成 Resources.Designer.cs 中的所有属性,保留原有注释分隔符和代码结构。
批量翻译工作流
# 1. 导出为 CSV
python3 i18n.py export -o translations.csv
# 2. 在 Excel 中编辑 translations.csv
# 3. 预览导入
python3 i18n.py import translations.csv --dry-run
# 4. 执行导入
python3 i18n.py import translations.csv --force
安全机制
| 机制 | 说明 |
|---|---|
| 自动备份 | 所有写入操作自动备份三文件到 Lang/.backup/ |
| Dry-run | --dry-run 展示变更摘要但不实际写入 |
| 确认提示 | 默认要求输入 y 确认;--force 跳过 |
| 原子性 | 先完成全部校验,再一次性写入所有文件 |
| XML 合法性 | 写入后立即重新解析验证 XML 格式 |
常见工作流
添加新的 UI 文案:
python3 i18n.py add Settings_AutoSave --zh "自动保存" --en "Auto Save"
# 在 C# 中使用:StatusMessage = Resources.Settings_AutoSave;
# 在 AXAML 中使用:<TextBlock Text="{x:Static lang:Resources.Settings_AutoSave}" />
python3 i18n.py check
dotnet build
批量修改翻译:
python3 i18n.py export -o translations.csv
# 在 Excel 中编辑 CSV
python3 i18n.py import translations.csv --dry-run
python3 i18n.py import translations.csv
python3 i18n.py check
dotnet build
用户自定义语言配置
语言通过 AppSettings.Language 存储,值为区域代码(如 zh-CN、en-US)。用户在设置页面选择语言后:
AppSettings.Language持久化到 JSON- 重启后
ApplyLanguageFromSettings()读取并应用 - 所有
{x:Static}绑定和Resources.xxx调用自动切换到目标语言
资源文件结构参考
完整脚本参考文档见 scripts/ToolsCollection.md。脚本单元测试在 scripts/tests/test_i18n.py(45 个测试用例)。