feat(m179): 外观分组接真语言并拆成五段文件(只接管子,不补文案)
M172 审计:`pages/appearance/**` 中文界面下真正本地化 0/27,两个根因各断一刀。
① 拆文件(纯搬移):`section.rs` 395 行 → `section/` 五个文件,行数守卫只剩 5 行余量,
不拆就加不了任何东西。
- `section/mod.rs`(壳:section() + card() + 五个行控件 + 取词口 t)
- `section/window.rs`(① 整窗 α + ② 圆角,含 notice/说明段)
- `section/panel.rs`(③ 面板 α)、`section/bg_image.rs`(④⑤⑥⑦⑧)、
`section/colour_source.rs`(⑨)
取证:每段与原文件做「去空白/去尾逗号」diff,4/4 相同;只有五处机械改写
(`rows = rows.push(…)`→`rows.push(…)`、`t(Key::X)`→`t(Key::X, lang)`、
`super::notice`→`super::super::notice`、一处 `.into()`、rustfmt 换行)。
② 语言接到渲染层:`settings::Body` 从 `fn(&Ui, States)` 变成 `fn(&Ui, States, Lang)`,
`form` 传 `language.effective`,`settings/appearance.rs` 收 `lang` 转给
`pages::appearance::section(ui, state, lang)`,`section::t(key, lang)` 取词。
选「接进唯一那个槽位」而不是再给 `form` 加一条按下标分发的 `else if`:`settings/mod.rs`
贴着 400 行硬顶(M195 还要在此落地),这条路净增 0 行且不可能与分发表漂开。
验收点:`pages/appearance/**` 里已经搜不到 `Lang::En`。
③ 能变红的守卫:`the_appearance_group_speaks_chinese_when_the_interface_does`
—— 走 `settings::view` 真分发渲染一帧,断言取词记录里含汉字、英文那帧不含汉字且同一 key
中英译文不同。取证口是 `appearance::probe`(`#[cfg(test)]` thread_local):iced 0.14
没有可遍历控件树的公开接口(`Widget::children()` 返回状态 Tree;`operate` 要 Renderer),
所以取证点退到「字进树之前」的唯一取词口。变异证明:把 `t()` 改回 `Lang::En.catalog()`
⇒ 该用例红(失败信息逐条列出四条英文);还原 ⇒ 绿。
只接语言管子,不补文案:本组其余英文仍是字面量(行号见 M179 报告)。真实窗口截图、
macOS/Linux 字体差异未验。
This commit is contained in:
1 parent
e386cfbeee
commit
b457f0acc8
14 files changed
+745
-422
No files matched your search
@@ -25,7 +25,7 @@
|
||||
//! | 分组 | 文件(正文 / 状态 / 消息) | 谁会动它 |
|
||||
//! |---|---|---|
|
||||
//! | General | [`general`] | language+appearance 线(语言下拉 + 主题 / 启动页) |
|
||||
//! | Appearance | [`appearance`] | language+appearance 线(真控件已在 `pages/appearance/section.rs`) |
|
||||
//! | Appearance | [`appearance`] | language+appearance 线(真控件已在 `pages/appearance/section/`) |
|
||||
//! | Java | [`java`] | java 线 |
|
||||
//! | Downloads | [`download`] | download 线 |
|
||||
//! | Updates | [`update`] | update 线(M159,按下标分发) |
|
||||
@@ -117,7 +117,7 @@
|
||||
//! 与 M106 同形状),要么读 `Groups` 里本线的字段。两种形状都是「谁先撞谁付一次钱」的一次性代价,
|
||||
//! 不是永久性四路撞车。
|
||||
//! 5. **首帧取数 / 异步发起**(同上,r2 复审 P1 补)—— [`Body`] 是
|
||||
//! `fn(&Ui, States) -> Option<Element>`,返回不了 `Task`;而且 `settings::view` → `pages::render`
|
||||
//! `fn(&Ui, States, Lang) -> Option<Element>`,返回不了 `Task`;而且 `settings::view` → `pages::render`
|
||||
//! → `App::view` **一路都返回 `Element`**(iced 0.14 的 `view` 也只返回 `Element`,`view(&self)`
|
||||
//! 又改不了状态),**view 期间产出的 `Task` 没有出口**。所以只有一条路:在 `app/update.rs` 的
|
||||
//! `Message::Boot` 分支补一次首帧派发(**1 个共享文件**——`on_boot` 本来就在派
|
||||
@@ -193,13 +193,16 @@ impl Default for Language {
|
||||
}
|
||||
}
|
||||
|
||||
/// 一个分组正文的**真控件渲染器**:入参是「画一帧时那份只读状态」(组状态 + 外观状态),
|
||||
/// 返回 `None` = 这一组现在没东西可画(例如外观分组在 harness 路径下没有外观状态),
|
||||
/// 由 [`form`] 退回演示行。
|
||||
/// 一个分组正文的**真控件渲染器**:入参是「画一帧时那份只读状态」(组状态 + 外观状态)
|
||||
/// 与**这一次生效的语言**,返回 `None` = 这一组现在没东西可画(例如外观分组在 harness 路径下
|
||||
/// 没有外观状态),由 [`form`] 退回演示行。
|
||||
///
|
||||
/// 各组自己的渲染器**定义在自己文件里**,签名是这一条:于是「这一组要读哪些状态」由它自己决定,
|
||||
/// 本文件不需要认识任何一组的字段(r1 复审 P1 要的正是这一点)。
|
||||
type Body = for<'a> fn(&'a Ui, States<'a>) -> Option<Element<'a, Message>>;
|
||||
///
|
||||
/// `lang` 是 M179 加的:槽位此前拿不到语言,唯一挂了真控件的外观组只能写死英文;接进**唯一的那个
|
||||
/// 槽位**比再给 [`form`] 加一条按下标分发的 `else if` 更省行数(本文件贴着 400 行硬顶)。
|
||||
type Body = for<'a> fn(&'a Ui, States<'a>, Lang) -> Option<Element<'a, Message>>;
|
||||
|
||||
/// 一个设置分组:名字、图标、演示行、(可选)把这一组正文画成真控件的渲染器。
|
||||
///
|
||||
@@ -236,9 +239,9 @@ const SELECTED: usize = 0;
|
||||
|
||||
/// General 分组在 [`GROUPS`] 里的下标(0)。它与 [`SELECTED`] 同值**是巧合,不是依赖**。
|
||||
///
|
||||
/// 为什么需要它:语言行要的是「当前档位 + 生效语言」([`Language`]),而 `Group.body` 的签名只带
|
||||
/// [`States`](外观 + 组状态)——给 `States` 加字段要动 `settings/state.rs` 与 4 处 struct literal
|
||||
/// (见本文件文档「仍然共享」第 4 条),给 `Body` 加形参会令另外三组各自的 `body` 函数编译不过。
|
||||
/// 为什么需要它:语言行要的是「当前档位 + 生效语言」([`Language`]),而 `Group.body` 只带
|
||||
/// [`States`] 与**生效语言**(`Lang`,M179)——档位 `LangChoice` 装不进去(`States` 也装不下:
|
||||
/// 加字段要动 `settings/state.rs` 与 4 处 struct literal,见本文件文档「仍然共享」第 4 条)。
|
||||
/// 所以这里做一次**按下标的分发**:代价是一行 `if`,换来 `general::GROUP.body` 仍是 `None`
|
||||
/// (`tests/mod.rs` 的「只有外观组带真控件 body」照样成立)。
|
||||
const GENERAL_GROUP: usize = 0;
|
||||
@@ -315,8 +318,8 @@ pub fn view<'a>(
|
||||
///
|
||||
/// **分发点(两条路,M106 起就并存)**:
|
||||
///
|
||||
/// 1. `GROUP.body`——`Some(f)` 让 `f` 自己去 [`States`] 里取它要的数据(`None` 返回值 ⇒ 退回
|
||||
/// 演示行),`None` 直接画演示行([`frame::demo_card`]);
|
||||
/// 1. `GROUP.body`——`Some(f)` 让 `f` 自己去 [`States`] 里取它要的数据、并从形参拿**生效语言**
|
||||
/// (`None` 返回值 ⇒ 退回演示行),`None` 直接画演示行([`frame::demo_card`]);
|
||||
/// 2. **按下标分发**——General(要语言档位)、Update(要 presenter 快照)、Assistant(要 `Lang`
|
||||
/// 与测试结论)、Java(要 `Lang` 与扫描槽)四组在下面各有 `else if`,它们的 `GROUP.body`
|
||||
/// 仍是 `None`(理由见各自的 `<组>_GROUP` 常量)。
|
||||
@@ -357,7 +360,7 @@ fn form<'a>(
|
||||
} else {
|
||||
group
|
||||
.body
|
||||
.and_then(|body| body(ui, states))
|
||||
.and_then(|body| body(ui, states, language.effective))
|
||||
.unwrap_or_else(|| frame::demo_card(ui, group.rows))
|
||||
};
|
||||
|
||||
|
||||
Reference in new issue
Block a user