国际化
GPUI Component 的组件文案位于 crates/component/locales/ui.yml,其中有 en、zh-CN、zh-HK、zh-TW、it 等语言的条目;不同 key 的覆盖程度不一定相同。应用负责自己的文案、语言选择策略和数据格式化,也可以通过 rust-i18n 新增或覆盖组件翻译,无需复制整个内置文件。
此功能需要 rust-i18n 4.2 或更高版本。
#添加依赖
在持有 locale 文件的应用 crate 中添加 rust-i18n:
[dependencies]
gpui-kit = "0.6"
rust-i18n = "4.2"
#创建应用 locale 文件
在应用 crate 中创建 locales/ui.yml,并将组件翻译放在 gpui_component namespace 下:
_version: 2
app:
save:
en: Save
fr: Enregistrer
gpui_component:
Calendar:
week.0:
fr: Di
month.January:
fr: Janvier
DatePicker:
placeholder:
fr: Sélectionner une date
应用自己的 key 放在 gpui_component 之外。组件翻译的 namespace 必须是 gpui_component,它对应 Rust crate 名称 gpui-component,其中连字符转换为下划线。请从 GPUI Component 内置 locale 文件逐字核对 key;例如 month.January 是 Calendar 下的一个完整 key。
#注册扩展
首先在应用的 crate root 初始化 locales:
rust_i18n::i18n!("locales", fallback = "en");
然后在初始化 GPUI Component 之前注册扩展:
app.run(move |cx| {
use gpui_kit::component as gpui_component;
rust_i18n::extend!(gpui_component);
gpui_kit::init(cx);
// Open a window and initialize the rest of the application.
});
应用启动期间只需调用一次 extend!。crates/story/src/lib.rs使用了同样的别名和调用顺序。
#查找优先级
应用提供的翻译优先于组件的内置翻译:
Application locales (locales/ui.yml)
│
│ key not found
▼
GPUI Component built-in locales
这种 deep merge 行为意味着:
- 新语言(例如
fr)只需提供应用需要的 key。 - locale 和 key 都相同时,应用提供的翻译会覆盖内置值。
- 应用未提供的 key 会继续使用组件内置值。
- GPUI Component 后续新增的翻译无需复制,也会自动可用。
例如,只定义 gpui_component.Calendar.month.January.en 会修改一月份的英文标签,其他英文日历标签仍然来自 GPUI Component。
#命名空间只对组件内部的查找生效
extend! 改变的是 GPUI Component 查找自身 key 的方式,它不会让应用自己的 t! 调用能访问到内置翻译:
// Inside GPUI Component: check the application first, then built-in locales.
t!("Calendar.month.February") // -> "February"
// In application code: only the application's locale files are searched.
t!("gpui_component.Calendar.month.February") // -> returns the key if undefined
组件文案应当通过渲染组件来呈现,不要自己去查这些 key。
应用文案可在应用 crate 中调用 rust_i18n::t!("app.save")。将整句放在同一个翻译 key 中,让每种语言自行决定语序和标点。组件占位符的翻译不会顺带翻译周围的应用标题、标签或菜单项。
#切换语言
gpui-component 已经再导出 locale 相关方法,应用无需直接依赖 rust-i18n 即可切换语言:
use gpui_kit::component::{locale, set_locale};
set_locale("fr");
let current = locale();
组件随后会按照上述优先级,使用当前 locale 查找翻译文本。
当前 locale 是 GPUI 变更跟踪之外的全局状态,单独修改它不会触发重绘。如果语言选择器由某个 view 处理,切换后通知这个 view:
use gpui_kit::component::set_locale;
set_locale("fr");
cx.notify();
cx.notify() 如何安排 View 更新,见 Context。
如果应用级 action 在 &mut App 中切换语言,则刷新展示翻译内容的窗口:
use gpui_kit::{App, component::set_locale};
fn select_locale(language: &str, cx: &mut App) {
set_locale(language);
cx.refresh_windows();
}
其他已缓存的界面内容还需显式重建。例如 story 应用的 SelectLocale 处理函数切换语言后会重建原生菜单和窗口内菜单,使选中状态更新;这些状态并非实时读取翻译。Story 的菜单标签目前也是固定字符串,因此这个选择器展示的是组件翻译和选中状态,并不表示整个画廊都已翻译。
#不同文字系统的文本与布局
翻译改变字符串,文本系统随后解析字体、塑形字形并测量结果。中文等 CJK 文本或中英混排需要字体包含相应字形。字体族可加载,也可能缺少特定字形;回退字体还可能改变换行和控件高度。详见字体和文本系统。在每个目标平台上,以长译文、窄按钮、输入框占位符和应用缩放检查界面。自定义文本的宽度应以塑形后的布局测量,不能按字符数估算。
GPUI Kit 的这个 locale API 没有提供应用级 RTL 布局切换。选择阿拉伯语或希伯来语 locale 可以选出对应文案,但不会自动镜像导航、图标方向、对齐、焦点顺序,以及指针和键盘行为。应按目标语言逐个检查页面,并在目标平台测试双向文本和编辑操作。文字塑形与整个界面的镜像是两项不同的检查。
#在应用中格式化数据
这里的 rust-i18n 用于选择文案;set_locale 不会替应用数据格式化日期、数字、货币、单位或复数。根据应用需求选择支持 locale 的格式化工具,将完成的文本传给 view。将变量放进完整的翻译消息,使译者能够调整位置。不要把分别翻译的片段拼成句子,也不要假定 format!("{value}") 会采用当地的数字分隔符。日期选择器可以翻译自己的月份名称,而应用其他地方显示的日期仍需遵循应用的格式化策略。
#验证集成
仓库中的 story crate 提供可运行的扩展和法语覆盖。从仓库根目录运行 cargo run -p gpui-component-story,在 Language 菜单中选择 Français,再打开日历或日期选择器 story;月份名称和日期占位符会使用 crates/story/locales/ui.yml 中的 fr 值。切回英语并检查相同控件;菜单选中标记应跟随选择。还可运行 cargo test -p gpui-component-story --features test-support extends_component_translations_with_story_locales,验证 story 的翻译叠加测试。
#常见问题
| 现象 | 检查项 |
|---|---|
| 组件仍显示旧语言 | 调用 set_locale 后通知所属 view 或刷新窗口;重建缓存的菜单或标签。 |
| 覆盖没有生效 | key 应放在 gpui_component 下,拼写和 locale 代码须与内置文件一致,并在 gpui_kit::init 前注册 extend!。 |
应用中的 t! 返回 key | 在应用 locale 文件中定义该 key;组件内置文案供组件查找,不供应用直接查找。 |
| CJK 出现方框或布局变化 | 检查目标平台的字体字形覆盖和回退;见字体。 |
| RTL 文案已出现但界面仍不顺手 | 逐项检查布局和交互;切换 locale 不会镜像整个界面。 |