refactor(pages,i18n): M99 纯搬移 —— 导航表抽 pages/nav.rs、设置页拆目录、catalog 按域拆开

- pages/mod.rs 400 -> 356 行:NAV 表与组件版 rail 搬进 pages/nav.rs(NAV 可见性变 pub(crate),
  只为让 pages/tests.rs 的三条断言原样保留)
- pages/settings.rs -> pages/settings/{mod,frame,general,appearance,java,download,ai,tests}.rs:
  mod.rs 只留「分组注册表 + 选中分组 + 内容分发」,一组一个文件,为四条 section 线各留位置
- i18n catalog.rs 310 -> 273 行:条目表按域拆到 catalog/<域>.rs,catalog.rs 留 catalog! 宏、
  装配链、Catalog 与对外 API;新增 catalog/golden.rs(拆分前 key->译文 全量快照)守门
- 行为/文案/样式零变化:pages_demo 的 instances/settings × 浅/深 四张图逐字节相同
This commit is contained in:
AskaEth committed 2026-11-03 13:19:17 +08:00
1 parent 4b1471a60d
commit 330f7216fd
29 files changed
+1173 -541

No files matched your search

+85 -122
View File
@@ -23,6 +23,44 @@
//! 表格是手写的,缺条目的风险回来了;
//! 2. **回归守门**:万一有人把这张表改回「两套各写一遍」,[`Catalog::missing`] 的守卫测试立刻红。
//!
//! ## 按域拆开(M99 `SETTINGSFRAME`)
//!
//! 条目表原先整张写在本文件里,而**设置页正文接下来由四条 mission 并行接**(java / download /
//! language+appearance / ai+about),每条都要往这张表里加一批 key——同一张表就是四条线的冲突点。
//! 现在一个域一个文件(`catalog/<域>.rs`),本文件只留:`catalog!` 宏、装配链、[`Catalog`] 与对外 API。
//!
//! | 文件 | 域 | 谁会长它 |
//! |---|---|---|
//! | [`nav`] / [`instances`] / [`loader`] / [`launch`] / [`font`] / [`core_text`] | 页面与运行时文案 | 各自页面 |
//! | [`download`] / [`logs`] | 下载页 / 日志页(M94 / M95) | 那两页 |
//! | `settings/`(`general` / `appearance` / `java` / `download` / `ai`) | 设置页,**一个分组一个文件** | 设置页四条线 |
//!
//! ### 域文件的形状(加 key 时照抄)
//!
//! ```ignore
//! macro_rules! <域>_entries {
//! ($next:ident, $($rest:ident,)* { $($acc:tt)* }) => {
//! $next! { $($rest,)* {
//! $($acc)*
//! NavInstances => ("Instances", "实例"),
//! } }
//! };
//! }
//! pub(super) use <域>_entries;
//! ```
//!
//! 它把本域条目**追加**到累积表 `$acc` 之后,再把「还没装配的域」原样交给 `$next`——最后交给
//! [`finish`],由它把整张表喂给 [`catalog!`]。**为什么非得这么绕**:`macro_rules!` 不能在
//! 宏参数里展开另一个宏(`catalog! { nav_entries!() }` 只会把 `nav_entries!()` 当成普通 token),
//! 也不能用元变量当宏名(`macro_rules! $name` 非法),所以只能「回调链」式累积。代价是每个域文件
//! 都要重抄上面那 6 行签名——这是 `macro_rules!` 的固有成本,不是可以省掉的样板。
//!
//! ### 拆开之后仍然只有一个真相源
//!
//! 装配顺序写在下面那条调用里,**它等于 `Key` 的声明顺序**;[`golden`] 把拆分前那份
//! `key -> (en, zh-CN)` 全量逐字节钉住(连顺序一起钉),所以「搬丢一条 / 两条互抄 / 顺序被调换」
//! 都会红。
//!
//! ## 缺译时给什么
//!
//! **不回落**(绝不拿另一种语言顶替,用户会看到「一半英文一半中文」的界面而不自知):
@@ -66,132 +104,59 @@ macro_rules! catalog {
};
}
catalog! {
// ── 导航轨(`pages::Route` 的四项)
NavInstances => ("Instances", "实例"),
NavDownload => ("Downloads", "下载"),
NavLogs => ("Logs", "日志"),
NavSettings => ("Settings", "设置"),
// --------------------------------------------------------------- 域装配
// ── 实例页
InstancesNew => ("New instance", "新建实例"),
InstancesImport => ("Import", "导入"),
InstancesBack => ("Back to instances", "返回实例列表"),
InstancesPlay => ("Play", "启动"),
InstancesLastPlayed => ("Last played {when}", "上次游玩 {when}"),
InstancesEmptyTitle => ("No instance selected", "未选择实例"),
InstancesEmptyBody => ("Pick an instance to see its details.", "选择一个实例查看详情。"),
/// 链的终点:把累积起来的所有条目交给 [`catalog!`],生成 `Key` 与两张表。
macro_rules! finish {
({ $($acc:tt)* }) => { catalog! { $($acc)* } };
}
// ── 加载器(品牌名不翻译:Fabric / Forge / Quilt 一律原样,只有「原版」是词)
LoaderVanilla => ("Vanilla", "原版"),
mod core_text;
mod download;
mod font;
mod instances;
mod launch;
mod loader;
mod logs;
mod nav;
mod settings;
// ── 启动结局(原来 UI 把 "Launch failed" 与 core 的「静默失败」拼成一句话,现在一句话 = 一个 key)
LaunchOutcomeNormal => ("Finished normally", "正常结束"),
LaunchOutcomeCrash => ("The game crashed", "游戏崩溃"),
LaunchOutcomeSilent => (
"Suspected silent failure: exit code 0, but the game never really started",
"疑似静默失败:退出码 0,但游戏没真正跑起来"
),
LaunchFailedWithCode => ("Launch failed (exit {code}): {reason}", "启动失败(退出码 {code}):{reason}"),
#[cfg(test)]
mod golden;
// ── 中文字体(D28:中文环境缺字体时的下载提示 / 失败回退)
FontCjkNeedsDownload => (
"No Chinese font found on this system. Download an open-source (OFL) Chinese font?",
"系统里没找到中文字体。要下载一套开源(OFL)中文字体吗?"
),
FontCjkDownloading => ("Downloading Chinese font… {percent}%", "正在下载中文字体… {percent}%"),
FontCjkDownloadFailed => (
"Chinese font download failed; the interface stays in English.",
"中文字体下载失败,界面暂时保持英文。"
),
FontCjkReady => ("Chinese font ready: {family}", "中文字体已就绪:{family}"),
#[cfg(test)]
mod tests;
// ── 设置
SettingsLanguage => ("Language", "界面语言"),
SettingsLanguageHint => (
"Switches the interface language. Chinese text needs a Chinese font.",
"切换界面语言。显示中文需要中文字体。"
),
use core_text::core_text_entries;
use download::download_entries;
use font::font_entries;
use instances::instances_entries;
use launch::launch_entries;
use loader::loader_entries;
use logs::logs_entries;
use nav::nav_entries;
use settings::{
settings_ai_entries, settings_appearance_entries, settings_download_entries,
settings_general_entries, settings_java_entries,
};
// ── core / presenter 只给了中文串、UI 又不许画原文时的兜底(见 `bridge`)
CoreTextUntranslated => ("[system text not translated]", "[未翻译的系统文案]"),
// --- download page (M94) ---
// 下载页(`crates/app-iced/src/pages/download`)。一句话一个 key:带变量的句子用 `{name}`,
// 页面只做 `Catalog::render`,不拼片段(`bridge` 的规矩 1)。
//
// 文案口径(M94 r1 / P2-2):**只说这个页面真能做到的动作**(切分类、刷新、看日志),
// 不写「稍后会自动重试」这类 UI 没有的能力,也不拿文案去补功能。
DownloadTitle => ("Downloads", "下载"),
DownloadSubtitle => ("Versions you can install", "可以安装的版本"),
DownloadRefresh => ("Refresh", "刷新"),
DownloadInstall => ("Install", "安装"),
DownloadCancel => ("Cancel install", "取消安装"),
DownloadInstalled => ("Installed", "已安装"),
DownloadCount => ("{count} versions", "{count} 个版本"),
DownloadRowSupporting => ("{kind} · released {date}", "{kind} · 发布于 {date}"),
DownloadSelectHint => ("Pick a version, then install", "选一个版本再安装"),
DownloadLoading => ("Loading versions…", "正在加载版本列表…"),
DownloadLoadingHint => (
"If this stays here, press Refresh in the top right.",
"如果一直停在这里,点右上角的刷新。"
),
DownloadEmptyTitle => ("No versions to show", "没有可显示的版本"),
DownloadEmptyBody => (
"Pick another category, or refresh the list.",
"换一个分类,或刷新一次列表。"
),
DownloadCategoryAll => ("All", "全部"),
DownloadCategoryRelease => ("Release", "正式版"),
DownloadCategorySnapshot => ("Snapshot", "预览版"),
DownloadCategoryAncient => ("Ancient", "远古版"),
DownloadStagePlanning => ("Planning", "解析版本"),
DownloadStageDownloading => ("Downloading files", "下载文件"),
DownloadStageInstalling => ("Building the version", "生成版本"),
DownloadStageRegistering => ("Registering the instance", "登记实例"),
DownloadStageDone => ("Done", "已完成"),
DownloadStageFailed => ("Install failed", "安装失败"),
DownloadStageCancelled => ("Install cancelled", "安装已取消"),
DownloadProgress => (
"{done}/{total} files",
"{done}/{total} 文件"
),
DownloadProgressUnknown => ("Waiting for the first file…", "等第一个文件…"),
// 速率与剩余时间是两个独立短语,各占一个 key(`DownloadRate` 只负责把它们排成一行)
DownloadRate => ("{speed} · {eta}", "{speed},{eta}"),
DownloadEtaSecs => ("{secs}s left", "剩 {secs}s"),
DownloadEtaMins => ("{mins}m{secs}s left", "剩 {mins}m{secs}s"),
DownloadFailedHint => (
"The Logs page has the full reason.",
"完整原因在日志页。"
),
DownloadOpenLogs => ("Open logs", "打开日志"),
DownloadDismiss => ("Dismiss", "关闭"),
// --- logs page (M95) ---
// 日志页的**界面文案**(标题 / 按钮 / 分组名 / 空态)。诊断结论的正文不在这里:
// 那是规则表 `rules.d/*.yaml` 的输出(数据),见 `pages/logs/diagnostic.rs` 的模块文档。
// `LogsStatus` 是**模板**:中英差别落在分隔符上(中文用全角竖线——`·` 是外来符号,
// 中文正文里偏挤;这也让「中文目录必须与英文不同」这条守卫成立,不是为过测试硬造差异)。
LogsSubtitleIdle => ("Nothing is running right now", "当前没有运行中的实例"),
LogsStatus => ("{instance} · {when} · {outcome}", "{instance}|{when}|{outcome}"),
LogsOpenFolder => ("Open folder", "打开目录"),
LogsClear => ("Clear", "清空"),
LogsOutputTitle => ("OUTPUT", "输出"),
LogsDiagnosisTitle => ("DIAGNOSIS", "诊断"),
LogsConfidenceHigh => ("HIGH CONFIDENCE", "高置信度"),
LogsConfidenceMedium => ("MEDIUM CONFIDENCE", "中置信度"),
LogsConfidenceLow => ("LOW CONFIDENCE", "低置信度"),
LogsSubtitleLive => ("Running now — logs stream as they arrive", "正在运行 — 日志实时滚动"),
LogsConfidence => ("Confidence {percent}%", "置信度 {percent}%"),
LogsSuggestedFix => ("Suggested fix", "建议动作"),
LogsSummaryLabel => ("One-line summary", "一行摘要"),
LogsCopySummary => ("Copy summary", "复制摘要"),
LogsEmptyTitle => ("No output yet", "还没有输出"),
LogsEmptyBody => (
"Logs stream here while the launcher or the game is running. Pick an instance and start it to see the first lines.",
"启动器或游戏运行时,日志会实时出现在这里。选择一个实例并启动,就能看到第一批日志。"
),
LogsEmptyAction => ("Choose an instance", "选择实例"),
// 装配顺序 = `Key` 的声明顺序。**与拆分前逐条一致**,动它等于动 `Key` 的 `Ord` 与导出表顺序,
// 金表(`catalog/golden.rs`)会红。设置页那五个域按页面上的分组顺序排,中间三个现在是空的。
nav_entries! {
instances_entries,
loader_entries,
launch_entries,
font_entries,
settings_general_entries,
settings_appearance_entries,
settings_java_entries,
settings_download_entries,
settings_ai_entries,
core_text_entries,
download_entries,
logs_entries,
finish,
{}
}
/// 一套语言的目录(不可变;`Copy`,随手传值)。
@@ -306,5 +271,3 @@ pub fn placeholders(text: &str) -> Vec<&str> {
}
found
}
#[cfg(test)]
mod tests;