PhotoCraft 简体中文本地化指南:zh-hans 目录、区域判定与三种切换方式
【免费下载链接】photocraftPhotoCraft: An open-source, photo editor written by scratch in pure Rust项目地址: https://gitcode.com/gh_mirrors/pho/photocraft
你在 PhotoCraft 里把系统区域设为 zh-CN,界面却显示成繁体,甚至整版英文?这是 zh-hans 本地化里最容易被低估的一层:Rust 国际化不只是"找到翻译文件替换文案",还要把系统吐出来的 locale 标签精确分流到正确的目录。下面这套机制——zh-hans 目录如何注册、zh-CN 为什么一定落到简体、改一个词后怎样确认没改坏——就是 PhotoCraft 简体中文方案的全部。
三种方式切换简体中文
简体中文支持在 v0.2.0 发布之后才合入,那个版本里没有语言选择器,需要使用包含本地化改动的构建。对当前版本来说,切换路径有三条。
- 🖱️ 界面操作
- 🛠️
prefs.set命令 - 🌐 Auto 自动检测
界面操作
走Preferences > Interface > Language > 简体中文。下拉列表按各语言的本族语名称排序,简体中文就在其中;首选项对话框会实时预览所选语言的标题、控件与按钮,点取消不会提交任何更改。
prefs.set命令
CLI、MCP 与桌面控制通道共用同一个入口,这条 JSON 把interface.language指向 zh-hans:
{"method":"engine.execute","params":{"command":"prefs.set","params":{"path":"interface.language","value":"zh-hans"}}}发送后菜单文案会在同一帧内换成中文,测试a_control_request_changes_the_drawing_language_in_the_same_frame就锁死了这一点。引擎侧对取值有校验:只接受auto或语言代码,传 JSON 数组这类非法值会被拒绝并回退英语,界面不会崩。
Auto 自动检测
Auto 下应用按顺序读取LANG/LC_*、macOS 首选语言列表、Windows 用户语言(经由固定的sys-locale依赖安全调用平台 API,见crates/ui-egui/src/i18n/system.rs),协商到第一个受支持的代码为止。zh、zh-CN、zh-SG及zh-Hans的各种变体都会命中 zh-hans;另外可以设置PHOTOCRAFT_LOCALE环境变量覆盖成单个标签,方便可复现地启动调试。
Auto 模式下 zh 系标签如何分流到简繁
判定核心是candidates()(crates/ui-egui/src/i18n/mod.rs#L335-L355)。它把一个标签按最具体到最一般的顺序给出候选,再拿每个候选去语言注册表里试。比如zh_TW.UTF-8会展开成zh-tw、zh-hant、zh——有点像查地址时逐级放宽:先按"某区某路 12 号"找,找不到放宽到整条路,再放宽到整个区。
特殊规则专门照顾中文:主标签是zh、且整串里没有显式出现hans或hant时,按地区推断书写系统——
if primary == "zh" && !parts.iter().any(|p| matches!(*p, "hans" | "hant")) { // Chinese by region when no script is given. let script = if parts.iter().any(|p| matches!(*p, "tw" | "hk" | "mo")) { "zh-hant" } else { "zh-hans" }; out.insert(out.len() - 1, script.to_string()); }也就是说tw/hk/mo注入繁体候选,其余情况注入简体候选,而且插入位置保证它优先于裸zh被尝试。对照下来是这样一张分流表:
| 系统 locale 标签 | 解析结果 |
|---|---|
zh、zh-CN、zh-SG、zh-Hans、zh-Hans-CN、zh_CN.UTF-8 | zh-hans(简体中文) |
zh-TW、zh-HK、zh-MO、zh-Hant、zh-Hant-HK、zh_TW.UTF-8 | zh-hant(繁体中文,独立目录) |
单元测试tags_map_to_languages逐条断言:zh-CN、zh_CN.UTF-8、zh_SG、zh-Hans、zh-Hans-CN乃至裸zh全部解析到zh-hans,且绝不错入繁体目录。多语言偏好列表则按顺序取第一个受支持项,system_preferences_are_negotiated_in_order验证了["zh-Hans-CN", "zh-TW"]得到zh-hans。首启行为也被锁定:first_launch_renders_the_system_language_without_overwriting_auto确认首次渲染跟随系统语言、但不会把检测结果写回偏好文件;显式选过的语言在系统语言变化后重启依然保留。
zh-hans 目录格式:TSV 三列长什么样
翻译数据在crates/ui-egui/src/i18n/zh-hans.tsv,UTF-8、TAB 分隔三列:context、英文源文本、简体中文译文,头部注释(文件第 1–12 行)写明了全部约定。第一列的四种取值决定行的语义:
- 空:普通 UI 文本,英文原文直对译文;
@id:以稳定的命令 ID 作键,同一段英文在不同命令下可以译成不同文本;@plural:复数消息,源列写成one|other,中文只有一种形式;- 其他字符串(如
cameraRaw):上下文消歧,同一英文词在不同面板含义不同时分开翻译。
文件里真实的复数行长这样:
@plural Group · {n} layer|Group · {n} layers 组 · {n} 个图层 @plural {n} item|{n} items {n} 项 @plural {n} layer|{n} layers {n} 个图层列内支持\n、\t、\\三种转义。解析端逐行检查:格式错误的行会被报告并跳过,不会让 UI 崩掉——坏翻译至多让那一条回退英文,而目录级测试会强制要求捆绑目录零错误。
语言注册表与六条硬约定
zh-hans 在LANGUAGES注册表(crates/ui-egui/src/i18n/mod.rs)中登记,关键字段就四个含义:
code: "zh-hans":小写 BCP 47 代码,也是interface.language的取值;Lang::from_pref按忽略大小写比较,所以写成ZH-Hans同样命中;name: "简体中文":首选项下拉里显示的本族语名称;source:目录内容经include_str!在编译期嵌入二进制,运行时拿不到文件,也改不了文件;plural: plural_none:任何计数都返回索引 0,对应"中文只有一种复数形式";complete_menus: true则声明该目录覆盖全部强制菜单键,自动进入后文的覆盖门禁测试。
维护这份目录时,以下约定是硬性的:
- 英文源键、上下文、命令 ID、占位符与转义符原样保留——你翻译的是含义,键不许动;
- 会打开对话框的命令,译文结尾必须保留
…,与源文逐行对应; - 缺失翻译自动回退英语,
tr()查不到就原样返回英文; - 用户提供的名称与文档数据(图层名、文档名、预设名)不翻译;
- 产品名与技术名保留原拼写:PhotoCraft、ArtCraft、OpenType、RGB、CMYK、Lab 等;
- 状态消息、错误与自动化输出保持英语,目录全覆盖不代表这些字符串会被翻译。
这六条里大部分都有测试在直接执行,漏一条门禁就红。
术语速查表
| 英文 | 简体中文 |
|---|---|
| Layer / Layer Comp | 图层 / 图层复合 |
| Mask / Clipping Mask | 蒙版 / 剪贴蒙版 |
| Selection / Feather | 选区 / 羽化 |
| Blend Mode / Opacity | 混合模式 / 不透明度 |
| Adjustment Layer | 调整图层 |
| Smart Object / Smart Filter | 智能对象 / 智能滤镜 |
| Canvas / Artboard | 画布 / 画板 |
| Brush / Stroke | 画笔 / 描边 |
| Fill / Gradient | 填充 / 渐变 |
| Path / Rasterize | 路径 / 栅格化 |
| Preset / Swatch | 预设 / 色板 |
| Export / Preferences | 导出 / 首选项 |
防腐化机制:门禁测试与验证命令
共享目录测试对所有声明了完整覆盖的目录(zh-hans 在内)生效,逐个看它们拦什么:
bundled_catalogs_are_consistent:格式零错误、无重复键、占位符集合与源文一致、省略号一致、@id键必须对应真实存在的命令 ID;complete_languages_translate_every_menu_string:遍历菜单目录、命令表与引擎command_specs()里的全部菜单路径段和标签,逐条确认目录有译文;every_tl_literal_is_translated:静态扫描crates/ui-egui/src下所有tl!字面量(要求至少 300 个,防止扫描失效),声明完整的目录必须全部覆盖;brush_section_names_are_translated/blend_mode_names_are_translated:画笔面板分区名与混合模式名逐一检查,因为它们是动态标签,字面量扫描看不到。
zh-hans 还有一份专属回归,simplified_chinese_covers_dynamic_shortcuts_and_layer_counts把动态行为钉死——大小写不敏感的代码解析、动态快捷键占位符填充、图层计数与英语回退:
assert!(zh.complete_menus(), "Simplified Chinese must participate in the coverage gates"); assert_eq!(Lang::from_pref("ZH-Hans"), zh); assert_eq!(tr(zh, "Pixel Layer"), "像素图层"); for n in [0, 1, 3] { assert_eq!(trn(zh, n, "{n} layer", "{n} layers"), format!("{n} 个图层")); } assert_eq!(tr(zh, "no such label"), "no such label"); // 英语回退这些断言意味着:改错一个动态标签或复数形式,测试当场红。行为级测试dynamic_brush_sections_draw_in_the_selected_language与cjk_font_order_follows_the_selected_language(均在crates/ui-egui/src/i18n/live_tests.rs)则进一步离屏渲染,确认画笔面板分区行真的以中文画出、CJK 字体回退链首位是简体字形脚本。
提交前跑下面这一组命令,它们覆盖目录解析、locale 判定、回退与门禁:
cargo test -p photocraft-ui-egui cargo clippy -p photocraft-ui-egui --all-targets -- -D warnings cargo xtask layers cargo xtask wasm另外可以用snapshot示例(crates/ui-egui/examples/snapshot.rs)以 zh-hans 离屏渲染,目检菜单、首选项和代表性对话框,输出里会带"resolved": "zh-hans"之类的字段,方便确认最终生效语言。
四个容易踩的坑
- 🌐Web 端不会自动检测浏览器语言,需要手动选择,浏览器 locale 检测属于未完成工作;
- 改 TSV 必须重新编译:目录经
include_str!浇铸进二进制,改完不重编,界面纹丝不动; - 用户数据永不翻译:
Camera Raw Filter ({layer})里{layer}的值原样保留;状态消息与错误也保持英语; - 字体不随目录交付:目录不带任何字体资源,原生构建由 shell 的 CJK 回退加载器单独处理(切换语言时重建字体定义、重置加载器),所以要在各目标平台目检缺字与布局,Web 端字形覆盖单独验证。
动手之前:给贡献者的一段路线
先读docs/localization-zh-hans.md了解规范,改动时把源键与译文同步更新、保留上下文和占位符,跑完上一节的验证命令,再用snapshot示例以 zh-hans 渲染检查菜单与对话框,最后对照当前英文 UI 逐条复核改动条目的语义是否仍然对应——这一串做完,你的翻译才算真正合进了防腐化体系。
【免费下载链接】photocraftPhotoCraft: An open-source, photo editor written by scratch in pure Rust项目地址: https://gitcode.com/gh_mirrors/pho/photocraft
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考