Files
Unlucky-Launcher/crates/presenter/src/mock/instances.rs
T
AskaEth 1a4e1fee12 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(有实例 / 空态 / 运行中 × 浅深 × 中英)。
2026-11-05 13:36:44 +08:00

123 lines
4.9 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//! 截图/演示用的**实例场景**(只在 mock 后端里被调用)。
//!
//! D30 要求实例页出状态矩阵:空态 / 有实例 / 运行中(带 pid)/ 启动中(带百分比)。
//! 截图通道强制 mock 后端(`AppOptions::mock`),所以这四态的**数据**必须由 mock 造:
//! 走 `ULL_INSTANCE_STATE` 选场景,与 `ULL_ACCOUNT_STATE`(M114)/ `ULL_CONTENT_STATE`(M116)
//! 同一套取证开关。
//!
//! ## 三个约束(塔台 M120 批的)
//!
//! 1. **不设变量 = 今天那 4 条**(Idle×3 + Failed×1)——[`crate::mock_data::mock_instances`]
//! 原样返回,产品/单测的行为一字未变;
//! 2. **这个开关只影响 mock 通道**:真 host 的实例来自 `core::game` 的快照,读不到这里;
//! 3. `running` 的 pid 是**写死的 mock 值**(`1234`),不是真进程 pid——它证明的是
//! 「`运行中 · pid {pid}` 这条文案的数字与分隔符不随语言变形」,不是「我能读到真 pid」。
//!
//! ```text
//! ULL_INSTANCE_STATE=empty ULL_START_ROUTE=instances ULL_LANG=zh_cn \
//! unlucky-launcher-iced --screenshot=instances-empty-zh.png --software-renderer
//! ```
use crate::dto::{InstanceRuntime, InstanceSummary, LaunchStage, Progress};
use crate::mock_data::mock_instances;
/// 挑演示场景的环境变量。
pub(crate) const STATE_ENV: &str = "ULL_INSTANCE_STATE";
/// 场景名(未知值按 `demo` 处理,与 `ULL_ACCOUNT_STATE` 同一条兜底口径)。
pub(super) fn scenario() -> String {
std::env::var(STATE_ENV)
.unwrap_or_else(|_| "demo".to_string())
.trim()
.to_lowercase()
}
/// 场景对应的实例列表。
///
/// | 场景 | 列表 | 页面看起来 |
/// |---|---|---|
/// | `empty` | 空表 | 空列表 + 「未选择实例」空态(`detail: None`) |
/// | `demo`(缺省 / 未知值) | 4 条 Idle×3 + Failed×1 | 今天的样子(M90 起的基线) |
/// | `running` | 4 条,其中 1 条 `Running{pid}`、1 条 `Busy{42%}` | 列表 overline 与详情状态行都带运行态 |
pub(super) fn instances(scenario: &str) -> Vec<InstanceSummary> {
match scenario {
"empty" => Vec::new(),
"running" => running(),
_ => mock_instances(),
}
}
/// 运行态矩阵:同一批实例换 runtime(名字/版本/加载器都还是 `mock_data` 那一份)。
///
/// 三种态各留一条,是为了**一屏看完**状态文案的三种形状:正在跑(`pid`)、启动中(百分比)、
/// 启动失败(原因)。少一条就得再出一张图,而图多一张就多一处可以撒谎的地方。
fn running() -> Vec<InstanceSummary> {
let mut list = mock_instances();
for instance in &mut list {
match instance.id.as_str() {
"survival" => instance.runtime = InstanceRuntime::Running { pid: 1234 },
"vanilla-latest" => {
instance.runtime = InstanceRuntime::Busy {
stage: LaunchStage::Download,
progress: Progress {
done: 630,
total: 1_500,
},
};
}
_ => {}
}
}
list
}
#[cfg(test)]
mod tests {
use super::*;
/// **默认行为不变**(塔台三条约束的第 1 条):不设变量就是 `mock_data` 那一份。
///
/// 抓得住:有人把 `demo` 分支改成了「顺手多造几条」——那会让所有既有截图与断言漂。
#[test]
fn the_demo_scenario_is_exactly_the_old_mock_list() {
for unknown in ["demo", "", "bogus"] {
let got = instances(unknown);
let want = mock_instances();
assert_eq!(got.len(), want.len(), "{unknown:?}");
assert_eq!(got, want, "{unknown:?} 必须与 mock_data 逐字段相同");
}
}
#[test]
fn the_empty_scenario_has_no_instances() {
assert!(instances("empty").is_empty());
}
/// 运行态矩阵:`Running` / `Busy` 各一条,且**名字与版本没被动手脚**(它们不是场景的一部分)。
#[test]
fn the_running_scenario_covers_running_and_busy() {
let list = instances("running");
assert_eq!(list.len(), mock_instances().len());
assert!(
list.iter().any(
|item| matches!(item.runtime, InstanceRuntime::Running { pid } if pid == 1234)
),
"要有一条带固定 pid 的运行中实例"
);
assert!(list.iter().any(|item| matches!(
item.runtime,
InstanceRuntime::Busy {
stage: LaunchStage::Download,
..
}
)));
assert_eq!(list[0].name, mock_instances()[0].name);
}
/// 环境变量名是**契约**(出图脚本与报告都写它,改名等于让文档说谎)。
#[test]
fn the_scenario_variable_is_named() {
assert_eq!(STATE_ENV, "ULL_INSTANCE_STATE");
}
}