feat(app-iced): M120 实例页与启动阶段标签跟着界面语言走

来源:M106 r1 复审点名——中文机器上首屏(实例页)仍是英中混排(页头 / New instance /
Import / ALL INSTANCES / VERSION / Present on disk / Running · pid / Launch failed)。
用户的原话是「我系统语言是中文 为什么打开软件是英文」。

改了什么
- i18n:catalog/instances.rs 从 6 条死 key 补到 34 条(页头 / 列表列头 / 详情动作 /
  窄窗说明 / 元信息标签与值 / 隔离档位 / 运行状态 / 四个启动阶段)。族在装配链第 2 位,
  新 key 追加在本族末尾,golden 对应位置插入(不是文件末尾追加)。
- 页面:新增 pages/instances/text.rs(Texts)——页面唯一的取词入口,实例页里不再有
  任何可见英文字面量;detail / demo 也改从它取。
- 阶段标签:view/instances.rs 的 Running / Launch failed / 四阶段全部走目录;
  **数字与分隔符不随语言变形**(运行中 · pid 1234 与 Running · pid 1234 只换前半句)。
- 顺手修掉两处「语言反向泄漏」:原版徽标此前直接画 presenter 的中文「原版」(英文界面
  会漏中文),现在走 LoaderVanilla;隔离档位 isolated/shared 也从 core 的稳定 id 换成人话。
- 删掉 InstancesLastPlayed:M117 加的整句 key,从未接线,形状与「标签 + 值」的元信息卡
  不符,由 InstancesMetaLastPlayed + InstancesNeverPlayed 取代。

守门(pages/instances/tests/i18n.rs + app/view/tests.rs)
- 中文态无 ASCII 英文单词(品牌 Java / 技术标识 pid 白名单);
- 每一条 Instances* 的两套译文必须不同(切语言时真的变);
- Running 的数字与 · pid 分隔符在两套语言里逐字保留;
- 页面源码里 29 条旧英文基线不许回来;每条 key 都必须被页面引用(死 key)。

出图:DevDocs/artifacts/m120/01~12(有实例 / 空态 / 运行中 × 浅深 × 中英)。
This commit is contained in:
AskaEth committed 2026-11-05 13:36:44 +08:00
1 parent 2f491c31f9
commit 1a4e1fee12
17 files changed
+924 -119

No files matched your search

+17 -10
View File
@@ -48,6 +48,7 @@
mod data;
mod demo;
mod detail;
mod text;
#[cfg(test)]
mod tests;
@@ -55,6 +56,7 @@ mod tests;
#[cfg(test)]
pub(crate) use data::{DEMO, NOTE, SELECTED};
pub(super) use demo::demo;
pub(super) use text::Texts;
use iced::widget::{column, container, row, scrollable};
use iced::{Alignment, Background, Border, Element, Length, Padding};
@@ -92,22 +94,26 @@ pub(super) const ROW_SPACING: Option<Space> = None;
pub(super) const LIST_BOTTOM_INSET: Space = Space::X2;
/// 整页(`block.detail = None` 时详情区显示空态)。
///
/// 文案全部走 [`Texts`](`model.lang` 的语言):这个函数是实例页唯一的取词入口,
/// 页面里不留任何可见英文字面量(守门见 `instances/tests/i18n.rs`)。
pub fn view<'a>(ui: &'a Ui, metrics: FrameMetrics, model: &'a Model) -> Element<'a, Message> {
let block = &model.instances;
let forced = model.forced;
let header = Header::new("Instances")
.subtitle("Local versions and their mod loaders")
let texts = Texts::of(model.lang.catalog());
let header = Header::new(texts.title())
.subtitle(texts.subtitle())
.action(buttons::text(
ui,
forced_state(
Button::new("New instance", Message::Action(Action::NewInstance)),
Button::new(texts.new_instance(), Message::Action(Action::NewInstance)),
forced,
),
))
.action(buttons::text(
ui,
forced_state(
Button::new("Import", Message::Action(Action::Import)),
Button::new(texts.import(), Message::Action(Action::Import)),
forced,
),
));
@@ -115,15 +121,15 @@ pub fn view<'a>(ui: &'a Ui, metrics: FrameMetrics, model: &'a Model) -> Element<
let body = match metrics.layout {
PageLayout::ListDetail => frame::panes(
metrics,
list_column(ui, block, forced),
list_column(ui, block, forced, texts),
match &block.detail {
Some(detail) => detail::detail_column(ui, metrics, detail, forced),
None => detail::detail_empty(ui),
Some(detail) => detail::detail_column(ui, metrics, detail, forced, texts),
None => detail::detail_empty(ui, texts),
},
),
_ => match &block.detail {
Some(detail) => detail::narrow_detail(ui, metrics, detail, forced),
None => frame::body(metrics, detail::detail_empty(ui)),
Some(detail) => detail::narrow_detail(ui, metrics, detail, forced, texts),
None => frame::body(metrics, detail::detail_empty(ui, texts)),
},
};
@@ -136,6 +142,7 @@ fn list_column<'a>(
ui: &'a Ui,
block: &'a Instances,
forced: Option<Interaction>,
texts: Texts,
) -> Element<'a, Message> {
let head = container(
row![
@@ -143,7 +150,7 @@ fn list_column<'a>(
ui,
TypeRole::LabelMedium,
false,
"ALL INSTANCES",
texts.all_title(),
ui.color(Role::OnSurfaceVariant),
))
.width(Length::Fill)