feat(app-iced,i18n): M106 —— 界面跟随系统语言 + 设置页真实语言行
用户原话:「我系统语言是中文 为什么打开软件是英文 并且设置页里面显示的是 English(system)」。
两处根源:①M92 的探测链一直是对的,但 `pages::Model.lang` 在 `app/view.rs` 里写死成
`Lang::En`(`pages::render` 还把下载页写死成英文),探测结果没人用;②设置页语言行是硬编码
字面量 `("Language", "English (System)")`。
新增/改动(按层):
- i18n:新域 `catalog/general.rs`(24 条中英双份 key,装配链尾追加)+ `catalog/golden.rs`
只追加不重排;`choice.rs`(`LangChoice`:跟随系统/手选 + D28 四级优先级真值表 + 字体优先级真值表)。
- app-iced 语言入口 `src/ui.rs` + `src/ui/lang.rs`:`LangState` = 用户档位 + 探测结论;
启动判定 `ULL_LANG` > 配置 `ui.language` > 系统 locale > 英文;两行日志(探测/生效 + 中文策略)。
- `app/state.rs`:`App` 持有 `LangState`,切语言 = 换目录 + 落盘 + 重建视图模型;
`app/view.rs` 的 `model()` 收 `lang`,不再写死英文。
- 设置页:语言行换真控件(跟随系统 / 简体中文 / 英文,回显「界面当前使用:简体中文」);
页头、左列分组名、逐实例空态、导航轨标签全部走 catalog。
- presenter:`settings_bridge_lang.rs`(`load_ui_language` / `persist_ui_language`)。
「跟随系统」= **删键**(schema 允许值只有 zh_cn/en_us),读的是 `doc().get()` 而不是
`effective_str`(后者会把默认值 `zh_cn` 当成用户手选,英文机器一启动就被切成中文)。
验证:408 条 app-iced lib 测试 + 28 条 i18n 测试 + 全仓 `cargo test --workspace --exclude
unlucky-app` 全绿;fmt / clippy `-D warnings` 干净;出图 6 张(en/zh × 浅/深 + 下载页对照)
在 `DevDocs/artifacts/m106/`(该目录被 .gitignore 忽略,按仓内惯例作为本地验收产物)。
This commit is contained in:
1 parent
777d02e7cb
commit
89dcb6bdf3
25 files changed
+1456
-92
No files matched your search
@@ -41,6 +41,10 @@ impl App {
|
||||
// M101:设置页的四条填页线共用一个投递口;组内变体在 `update/settings.rs` 里穷尽分发
|
||||
// (那一层没有 `_ =>`,见本文件末尾与 `update/settings/tests.rs` 的两条守门)
|
||||
pages::Message::Settings(group) => return self.on_settings(group),
|
||||
// M106:换界面语言(落盘 + 重建视图模型;档位不是业务命令,不派 `AppCommand`)。
|
||||
// 这是**唯一**一处绕开 M101 投递口的页面消息:语言档位不是「某一组的状态」,
|
||||
// 它要落盘到 `ui.language` 并重建整棵视图模型(设置页的语言行只是它的第一个出口)。
|
||||
pages::Message::Language(choice) => self.set_language(choice),
|
||||
// M95:摘要落剪贴板(文本由页面造,这里只搬)
|
||||
pages::Message::CopySummary(text) => return iced::clipboard::write(text),
|
||||
// 下面四支已由 `on_download` 提前消化;显式列出而不是 `_ => {}`,见模块文档
|
||||
@@ -70,6 +74,76 @@ impl App {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use unlucky_design::color::SchemeFlavor;
|
||||
use unlucky_i18n::LangChoice;
|
||||
use unlucky_presenter::mock::MockBackend;
|
||||
use unlucky_presenter::settings_bridge::SettingsBridge;
|
||||
|
||||
use crate::app::state::App;
|
||||
use crate::cli::Options;
|
||||
use crate::pages;
|
||||
use crate::theme::Families;
|
||||
|
||||
const FAMILIES: Families = Families {
|
||||
text: "Segoe UI",
|
||||
display: "Segoe UI",
|
||||
mono: "Consolas",
|
||||
};
|
||||
|
||||
/// 不碰磁盘的 App(mock 后端 + 内存设置桥),与 `app/tests.rs` 的 `headless` 同型。
|
||||
fn headless() -> App {
|
||||
App::with_presenter(
|
||||
Options::default(),
|
||||
FAMILIES,
|
||||
SchemeFlavor::Light,
|
||||
Box::new(MockBackend::new()),
|
||||
SettingsBridge::memory_only(),
|
||||
)
|
||||
}
|
||||
|
||||
/// M106:设置页语言行点一下 → 生效语言与视图模型都跟着换。
|
||||
///
|
||||
/// 抓得住:①`pages::Message::Language` 没接进 `on_page`(点了没反应,编译器不会报——它接得住,
|
||||
/// 但接错了分支就静默);②`set_language` 只改字段不重建视图(界面要等下一次刷新才变);
|
||||
/// ③档位与生效语言各说各话(设置页会画错选中态)。
|
||||
/// 抓不住:界面真的画成中文了没有(那要截图,见 `DevDocs/artifacts/m106/`)。
|
||||
#[test]
|
||||
fn the_language_message_switches_the_catalog_and_rebuilds_the_view() {
|
||||
let mut app = headless();
|
||||
assert_eq!(
|
||||
app.view.lang,
|
||||
app.lang(),
|
||||
"首帧的视图模型要已经按生效语言建好"
|
||||
);
|
||||
|
||||
let _ = app.update(crate::app::message::Message::Page(
|
||||
pages::Message::Language(LangChoice::Fixed(unlucky_i18n::Lang::ZhCn)),
|
||||
));
|
||||
assert_eq!(app.lang(), unlucky_i18n::Lang::ZhCn);
|
||||
assert_eq!(app.view.lang, unlucky_i18n::Lang::ZhCn, "目录要跟着换");
|
||||
assert_eq!(
|
||||
app.language(),
|
||||
pages::Language {
|
||||
choice: LangChoice::Fixed(unlucky_i18n::Lang::ZhCn),
|
||||
effective: unlucky_i18n::Lang::ZhCn,
|
||||
}
|
||||
);
|
||||
|
||||
let refreshes = app.refreshes;
|
||||
let _ = app.update(crate::app::message::Message::Page(
|
||||
pages::Message::Language(LangChoice::Fixed(unlucky_i18n::Lang::En)),
|
||||
));
|
||||
assert_eq!(app.view.lang, unlucky_i18n::Lang::En);
|
||||
assert_eq!(app.refreshes, refreshes + 1, "换语言要重建一次视图模型");
|
||||
|
||||
// 同一档再点一次:什么都不该发生(不白重建)
|
||||
let refreshes = app.refreshes;
|
||||
let _ = app.update(crate::app::message::Message::Page(
|
||||
pages::Message::Language(LangChoice::Fixed(unlucky_i18n::Lang::En)),
|
||||
));
|
||||
assert_eq!(app.refreshes, refreshes, "同一档不该再重建");
|
||||
}
|
||||
|
||||
/// 抓得住:有人给 `on_page` 的 match 重新加 `_ => {}` 兜底 —— 那正是 M94 复审的 p3:
|
||||
/// 兜底让「加了变体忘了接线」从**编译错误**退化成「点了没反应」,这条测试把它拽回来。
|
||||
/// 抓不住:别的兜底写法(把 match 拆成 if-else 链、给子枚举加通配之后再漏变体),
|
||||
|
||||
Reference in new issue
Block a user