P1-1(p1-1items 的根因): - download/error.rs:`DownloadError` 增 `leaf: Option<DownloadErrorKind>` 与 `effective_kind()`;`leaf_of()` 汇总规则只此一处:全 404 ⇒ NotFound、有 Network/Timeout ⇒ Network、全 5xx ⇒ ServerError、混合按「网络错 > 5xx > 404」 取最可诊断的那个(**不从 suppressed 文本猜状态码**)。 - download/bytes.rs:候选源循环收集逐源 kind,收尾时把叶子挂到 AllSourcesFailed 上。 - online/transport.rs:`From<DownloadError>` 改用 `effective_kind()`,于是 NotFound/Server/Integrity 在生产路径可达(项目被删/id 拼错/限速不再画成「未联网」)。 - import/fetch.rs:新增 `FetchErrorKind::Offline`(真正的「没联网」)并与 Network 分开; `From<DownloadError>`/`From<OnlineError>` 都按叶子/大类映射。 - import/complete.rs:补全只在 `Offline` 时早停;404(真实 mrpack 大多托管在 CF CDN, Modrinth 必然 404)与 5xx 只影响那一条,不再掐掉整轮;注记「因未联网提前停止」因此 只会在真断网时出现。 P2-1:presenter 安装路径不再自己拼 Modrinth URL / 自己解析 JSON——改用 `core::addon::online::Modrinth::versions` + `primary_file()` + `VersionFile::sha1/sha512`; `HostTask::InstallDone.result` 改成 `Result<String, ContentFailure>`, `InstallProgressView` 增 `failure_kind`(离线 ≠ 查无此项)。 P2-2:`app/update/content.rs` 把被详情方法抢走的「装入闸门」注释还回 `content_install_request`,详情方法只留自己那一行。 P2-3:`ContentState::apply_detail/fail_detail` 带上 project(事件与 HostTask 都携带请求时的 项目 id),迟到结果一律丢弃;`request_detail` 记下在飞项目,`detail_pending` 不再被迟到 结果清掉。 顺手项:`usable_hash` 让形状非法的那一档摘要被**丢弃**(sha512 仍校验,不再整条跳过; 两档都不可用才拒绝);`digest_file` 的 IO 失败先删落盘文件再返回。i18n 的 ContentOfflineBody 改成不暗示「有缓存」的措辞(golden 同步)。 用例:download/error.rs 叶子四档 + effective_kind;online/tests/classify.rs 5 条 (AllSourcesFailed+leaf 404 ⇒ NotFound、无叶子 ⇒ NotConnected 保守、叶子一一映射、 单源自身 kind、导入侧 404≠Offline);import_online 增「404 不停」「真离线才停」 (共 7 条);download 增「sha1 形状非法仍用 sha512 校验成功」;presenter 增详情迟到闸门。
332 lines
18 KiB
Rust
332 lines
18 KiB
Rust
//! 内容库文案(M109 `CONTENTDL`)。
|
||
//!
|
||
//! 一句话一个 key:带变量的句子用 `{name}`,页面只做 [`crate::Catalog::render`],不拼片段。
|
||
//!
|
||
//! ## 三条口径
|
||
//!
|
||
//! 1. **只说能做的事**:整合包那条写的是「要等导入管线」,不是「稍后自动支持」;
|
||
//! 2. **站名也进词表**:`Modrinth` / `CurseForge` 的**中文译文必须与英文不同**
|
||
//! (`catalog/tests.rs` 的 `chinese_catalog_is_actually_chinese` 会红),所以中文写成「Modrinth 源」;
|
||
//! 3. **禁用态要能解释自己**:`ContentIncompatible*` / `ContentModpackNotReady` 就是禁用按钮旁边那几句话——
|
||
//! 没有它们,禁用就等于「点了没反应」。
|
||
//!
|
||
//! 域文件的形状见 `crate::catalog` 的「域装配」一节。
|
||
|
||
macro_rules! content_entries {
|
||
($next:ident, $($rest:ident,)* { $($acc:tt)* }) => {
|
||
$next! { $($rest,)* {
|
||
$($acc)*
|
||
// --- content library page (M109) ---
|
||
ContentTitle => ("Content", "内容库"),
|
||
ContentSubtitle => (
|
||
"Modpacks, mods, resource packs and shaders",
|
||
"整合包、Mod、资源包与光影"
|
||
),
|
||
ContentSectionVersions => ("Version management", "版本管理"),
|
||
ContentSectionContent => ("Content library", "内容库"),
|
||
ContentKindModpack => ("Modpacks", "整合包"),
|
||
ContentKindMod => ("Mods", "Mod"),
|
||
ContentKindResourcepack => ("Resource packs", "资源包"),
|
||
ContentKindShader => ("Shaders", "光影"),
|
||
ContentSourceModrinth => ("Modrinth", "Modrinth 源"),
|
||
ContentSourceCurseforge => ("CurseForge", "CurseForge 源"),
|
||
ContentSortRelevance => ("Relevance", "相关度"),
|
||
ContentSortDownloads => ("Downloads", "下载量"),
|
||
ContentSortNewest => ("Newest", "最新发布"),
|
||
ContentSortUpdated => ("Recently updated", "最近更新"),
|
||
ContentSearchPlaceholder => ("Search content…", "搜索内容…"),
|
||
ContentSearch => ("Search", "搜索"),
|
||
ContentFilterVersion => ("Game version", "游戏版本"),
|
||
ContentFilterLoader => ("Loader", "加载器"),
|
||
ContentFilterAny => ("Any", "不限"),
|
||
ContentCount => ("{count} results", "{count} 个结果"),
|
||
ContentLoading => ("Loading content…", "正在加载内容…"),
|
||
ContentLoadingHint => (
|
||
"Results come from the network; nothing is cached offline yet.",
|
||
"结果来自网络,暂时没有离线缓存。"
|
||
),
|
||
ContentEmptyTitle => ("Nothing matched", "没有匹配的内容"),
|
||
ContentEmptyBody => (
|
||
"Try another keyword, or clear a filter.",
|
||
"换个关键词,或清掉一个筛选条件。"
|
||
),
|
||
ContentFailedTitle => ("Could not load content", "内容加载失败"),
|
||
ContentFailedBody => ("Check the network, then retry.", "检查网络后重试。"),
|
||
ContentOfflineTitle => ("No connection", "未联网"),
|
||
ContentOfflineBody => (
|
||
"Content search needs the network. Check your connection, then retry.",
|
||
"内容检索需要联网。检查网络后重试。"
|
||
),
|
||
ContentRetry => ("Retry", "重试"),
|
||
ContentLoadMore => ("Load more", "加载更多"),
|
||
ContentNoMore => ("No more results", "没有更多了"),
|
||
ContentDetailEmpty => (
|
||
"Pick an item to see what it supports.",
|
||
"选一个项目看它支持什么。"
|
||
),
|
||
ContentDetailDownloads => ("{count} downloads", "下载量 {count}"),
|
||
ContentDetailUpdated => ("Updated {date}", "更新于 {date}"),
|
||
ContentDetailLoaders => ("Loaders: {list}", "加载器:{list}"),
|
||
ContentDetailVersions => ("Game versions: {list}", "游戏版本:{list}"),
|
||
ContentDetailFetching => ("Loading versions…", "正在取版本列表…"),
|
||
ContentDetailFetchFailed => (
|
||
"Could not load versions: {reason}",
|
||
"版本列表取不到:{reason}"
|
||
),
|
||
ContentDetailVersionCount => ("{count} versions", "{count} 个版本"),
|
||
ContentDetailVersionLine => ("{number} · {kind} · {date}", "{number}({kind})· {date}"),
|
||
ContentInstall => ("Install into instance", "装入实例"),
|
||
ContentTargetInstance => ("Target instance", "目标实例"),
|
||
ContentNoInstances => ("No instance to install into", "没有可装入的实例"),
|
||
ContentIncompatibleVersion => (
|
||
"Needs {version}; this instance runs {actual}",
|
||
"需要 {version},该实例运行的是 {actual}"
|
||
),
|
||
ContentIncompatibleLoader => ("Not built for {loader}", "不支持 {loader}"),
|
||
ContentModpackNotReady => (
|
||
"Installing modpacks from the library into an instance is not available yet",
|
||
"从内容库一键安装整合包到实例还没做(本地的包可以直接导入)"
|
||
),
|
||
ContentSourceUnavailable => (
|
||
"CurseForge needs an API key: add one in Settings",
|
||
"CurseForge 需要在设置里配置密钥才能搜索"
|
||
),
|
||
ContentSourceNotWired => (
|
||
"Installing from CurseForge is not available yet",
|
||
"CurseForge 来源的安装尚未开放"
|
||
),
|
||
// --- M113: pack import / export(PACKUI)---
|
||
PackTitle => ("Modpack import & export", "整合包导入 / 导出"),
|
||
PackImportTitle => ("Import a modpack", "导入整合包"),
|
||
PackDropHint => (
|
||
"Drop a .mrpack, .zip or a MultiMC/Prism instance folder here",
|
||
"把 .mrpack / .zip 或 MultiMC·Prism 实例目录拖到这里"
|
||
),
|
||
PackDropActive => ("Release to probe the pack", "松手开始探测"),
|
||
PackNoDialog => (
|
||
"This build has no system file picker: drag the file in, or paste its path below.",
|
||
"本版没有系统文件选择框:请把文件拖进来,或在下面粘贴路径。"
|
||
),
|
||
PackPathLabel => ("Pack path", "整合包路径"),
|
||
PackPathPlaceholder => (
|
||
"D:\\packs\\Better MC.mrpack",
|
||
"D:\\整合包\\更好的MC.mrpack"
|
||
),
|
||
PackImportAction => ("Import", "导入"),
|
||
PackCancel => ("Cancel import", "取消导入"),
|
||
PackDismiss => ("Dismiss", "关闭"),
|
||
PackTargetLabel => ("Install into", "装到实例"),
|
||
PackTargetNew => ("New instance", "新建实例"),
|
||
PackInstanceName => ("Instance name", "实例名"),
|
||
PackStageLine => ("Step {index}/{total} · {stage}", "步骤 {index}/{total} · {stage}"),
|
||
PackStageProbe => ("Reading the manifest", "读取清单"),
|
||
PackStageExtract => ("Unpacking overrides", "解包覆盖内容"),
|
||
PackStageFetch => ("Fetching missing files", "补下载文件"),
|
||
PackStageCommit => ("Writing files", "落地写盘"),
|
||
PackStageRegister => ("Registering the instance", "登记实例"),
|
||
PackProbeTitle => (
|
||
"This pack is almost a known format",
|
||
"这个包差一点就是已知格式"
|
||
),
|
||
PackProbeLooksLike => (
|
||
"Looks like {format}, but missing: {missing}",
|
||
"看起来像 {format},但缺:{missing}"
|
||
),
|
||
PackProbeUnknown => (
|
||
"No known format claims this pack",
|
||
"没有任何已知格式认领这个包"
|
||
),
|
||
PackProbeSaw => ("Found in the pack: {saw}", "包里看到:{saw}"),
|
||
PackProbeNothing => ("nothing recognisable", "没有可识别的东西"),
|
||
PackStatusSuccess => ("Imported", "已导入"),
|
||
PackStatusPartial => ("Imported with gaps", "导入完成,但有缺口"),
|
||
PackStatusFailed => ("Import failed — rolled back", "导入失败 —— 已整包回滚"),
|
||
PackReportWritten => ("Files written: {count} · {bytes}", "{count} 个文件已写盘 · {bytes}"),
|
||
PackReportMissing => (
|
||
"Could not fetch (tolerated): {count}",
|
||
"有 {count} 个文件没搞到(可容忍)"
|
||
),
|
||
PackReportConflicts => (
|
||
"Conflicts: {count}",
|
||
"冲突:{count} 个"
|
||
),
|
||
PackReportRejected => (
|
||
"The client does not support these: {count}",
|
||
"这些组件客户端不支持:{count} 个"
|
||
),
|
||
PackReportRejectedNote => ("not supported by the client", "客户端不支持"),
|
||
PackCancelled => (
|
||
"Import keeps running in the background — this result is discarded. Files already written stay.",
|
||
"导入会在后台继续跑完 —— 本次结果作废;已经写下的文件会留下。"
|
||
),
|
||
PackOverwriteAction => ("Re-import, the pack wins", "以包为准重新导入"),
|
||
PackOverwriteNote => (
|
||
"Keeping both (renamed) is not supported yet: for each existing file you pick either “keep” or “overwrite”.",
|
||
"「两者都留(改名)」暂不支持:每个已存在的文件只能选「保留」或「覆盖」。"
|
||
),
|
||
PackMissingOffline => ("offline import", "离线导入"),
|
||
PackMissingNoSource => (
|
||
"no download source available yet",
|
||
"暂时拿不到下载地址"
|
||
),
|
||
PackMissingFetchFailed => ("download failed: {detail}", "下载失败:{detail}"),
|
||
PackFatalTitle => (
|
||
"Rolled back — nothing was left behind",
|
||
"已回滚 —— 目标目录零残留"
|
||
),
|
||
PackFatalHint => ("What to try next", "下一步可以试"),
|
||
PackConflictUserModified => ("you changed it", "你改过"),
|
||
PackConflictUserDeleted => ("you deleted it", "你删过"),
|
||
PackConflictPreExisting => ("was already there", "本来就在"),
|
||
PackChoiceKeepUser => ("kept yours", "保留我的"),
|
||
PackChoiceOverwrite => ("pack version used", "用包里的"),
|
||
PackConflictHashes => (
|
||
"pack {pack} · base {base} · disk {disk}",
|
||
"包内 {pack} · 基线 {base} · 磁盘 {disk}"
|
||
),
|
||
PackConflictDetail => ("Hashes", "三方哈希"),
|
||
PackPickTitle => (
|
||
"Before importing: {count} files already exist with different content",
|
||
"导入前:有 {count} 个文件已存在且内容不同"
|
||
),
|
||
PackPickSummary => (
|
||
"Keep {keep} · overwrite {overwrite} · overwriting discards {bytes}",
|
||
"保留 {keep} · 覆盖 {overwrite} · 覆盖将丢弃 {bytes}"
|
||
),
|
||
PackPickKeepAll => ("Keep all mine", "全部保留我的"),
|
||
PackPickOverwriteAll => ("Overwrite all", "全部覆盖"),
|
||
PackPickRowKeep => ("Keep", "保留"),
|
||
PackPickRowOverwrite => ("Overwrite", "覆盖"),
|
||
PackPickPage => ("Items {from}-{to} of {total}", "第 {from}-{to} 项,共 {total} 项"),
|
||
PackPickPrev => ("Previous", "上一页"),
|
||
PackPickNext => ("Next", "下一页"),
|
||
PackPickConfirm => ("Import with these choices", "按这些选择导入"),
|
||
PackPickCancel => ("Cancel import", "取消导入"),
|
||
PackPickDefaultNote => (
|
||
"Anything you do not change stays as you have it: file content is only replaced where you pick “Overwrite”.",
|
||
"没有改动的一律按「保留」:只有你点了「覆盖」的文件才会被包内内容替换。"
|
||
),
|
||
PackPickUnknownRemote => (
|
||
"{count} remote entries only learn their path after download; they follow “keep”.",
|
||
"还有 {count} 条远端条目要下载后才知道路径,一律按「保留」处置。"
|
||
),
|
||
PackPickSourceOverrides => ("in the pack (overrides/)", "包内 overrides/"),
|
||
PackPickSourceClientOverrides => ("in the pack (client-overrides/)", "包内 client-overrides/"),
|
||
PackPickSourceManifest => ("manifest files[]", "清单 files[]"),
|
||
PackPickDisk => ("yours: {size}", "你的:{size}"),
|
||
PackPickIncoming => ("pack: {size}", "包内:{size}"),
|
||
PackFormatModrinth => ("Modrinth .mrpack", "Modrinth 的 .mrpack"),
|
||
PackFormatCurseforge => ("CurseForge manifest", "CurseForge 清单"),
|
||
PackFormatMultimc => ("MultiMC / Prism instance", "MultiMC·Prism 实例"),
|
||
PackExportTitle => ("Export this instance", "导出这个实例"),
|
||
PackExportOpen => ("Export…", "导出…"),
|
||
PackExportFormat => ("Format", "格式"),
|
||
PackExportName => ("Pack name", "包名"),
|
||
PackExportVersion => ("Version", "版本"),
|
||
PackExportOutput => ("Save to", "保存到"),
|
||
PackExportOutputPlaceholder => (
|
||
"empty = suggested path",
|
||
"留空 = 自动给一个路径"
|
||
),
|
||
PackExportPrecheck => ("Dry run", "预检"),
|
||
PackExportConfirm => ("Write the pack", "确认写盘"),
|
||
PackExportAbort => ("Back", "返回"),
|
||
PackExportMultimcNote => (
|
||
"MultiMC/Prism can only be imported, not exported (by design).",
|
||
"MultiMC·Prism 只能导入、不能导出(有意省略)。"
|
||
),
|
||
PackExportDone => ("Wrote {path} · {bytes}", "已写出 {path} · {bytes}"),
|
||
PackExportFailed => ("Export failed: {reason}", "导出失败:{reason}"),
|
||
PackExportPending => ("Scanning the instance…", "正在扫描实例…"),
|
||
PackPreviewHead => (
|
||
"Will inline {inline} files · cannot export: {skipped}",
|
||
"将内联 {inline} 个文件 · 无法导出 {skipped} 项"
|
||
),
|
||
PackPreviewRemote => (
|
||
"{count} files can be referenced remotely",
|
||
"{count} 个文件可走远程引用"
|
||
),
|
||
PackPreviewInlineBytes => (
|
||
"{bytes} copied into overrides/",
|
||
"{bytes} 会原样拷进 overrides/"
|
||
),
|
||
PackPreviewInlineTitle => (
|
||
"Copied into overrides/ (first {shown}):",
|
||
"原样拷进 overrides/(只列前 {shown} 个):"
|
||
),
|
||
PackPreviewSkippedTitle => (
|
||
"Not in the pack: {count}",
|
||
"没进包:{count} 项"
|
||
),
|
||
PackPreviewNotes => ("Known losses", "已知损失"),
|
||
PackPreviewTarget => ("Will be written to {path}", "将写到 {path}"),
|
||
PackPreviewMore => (
|
||
"…and {count} more files (not shown here)",
|
||
"…还有 {count} 个文件(这里不展开)"
|
||
),
|
||
PackPreviewComplete => ("Nothing is left out", "没有遗漏任何文件"),
|
||
PackPreviewIncomplete => (
|
||
"Some files will not be in the pack",
|
||
"有文件不会进包"
|
||
),
|
||
PackReasonModrinthSha1 => ("Modrinth match (SHA-1)", "Modrinth 命中(SHA-1)"),
|
||
PackReasonCurseFingerprint => (
|
||
"CurseForge match (fingerprint)",
|
||
"CurseForge 命中(文件指纹)"
|
||
),
|
||
PackReasonNoRemoteMatch => (
|
||
"unknown to both catalogs, inlined",
|
||
"两个目录都不认识,已内联"
|
||
),
|
||
PackReasonCurseOnly => (
|
||
"only CurseForge knows it, this format cannot use it, inlined",
|
||
"只有 CF 认识,目标格式用不上,已内联"
|
||
),
|
||
PackReasonModrinthOnly => (
|
||
"only Modrinth knows it, the CF manifest cannot use it, inlined",
|
||
"只有 Modrinth 认识,CF 清单用不上,已内联"
|
||
),
|
||
PackReasonMissingSha512 => (
|
||
"the catalogue entry has no sha512 hash, inlined",
|
||
"命中缺 sha512,已内联"
|
||
),
|
||
PackReasonMissingCurseIds => (
|
||
"the catalogue entry has no CurseForge ids, inlined",
|
||
"命中缺 projectID/fileID,已内联"
|
||
),
|
||
PackReasonDisabled => (
|
||
"disabled file, this format cannot express it (.disabled kept)",
|
||
"禁用文件,目标格式表达不了(保留 .disabled)"
|
||
),
|
||
PackReasonSymlink => ("symbolic link, skipped", "符号链接,已跳过"),
|
||
PackReasonUnsafePath => (
|
||
"path fails the pack path rules, skipped",
|
||
"路径过不了包内规整,已跳过"
|
||
),
|
||
PackReasonUnreadable => ("cannot read it, skipped", "读不出来,已跳过"),
|
||
// --- M161:导入结果的两种失败**不许共用一句话** ---
|
||
// 「预检没通过(一个字节都没写)」与「写进去之后回滚完成」是两件事:
|
||
// 前者用户去修包/修配置再试,后者说明已经动过目标目录。此前两者共用
|
||
// `PackStatusFailed`("Import failed — rolled back"),预检失败时那句话是假的。
|
||
PackStatusFailedNotStarted => (
|
||
"Import failed — nothing was written",
|
||
"导入失败 —— 还没写任何文件"
|
||
),
|
||
PackStatusFailedIncomplete => (
|
||
"Import failed — rollback did not finish",
|
||
"导入失败 —— 回滚没做完"
|
||
),
|
||
PackFatalTitleNotStarted => (
|
||
"Preflight failed — nothing was written",
|
||
"预检未通过 —— 一个字节都没写"
|
||
),
|
||
PackFatalTitleIncomplete => (
|
||
"Rollback did not finish — the target folder may differ",
|
||
"回滚没做完 —— 目标目录可能与导入前不同"
|
||
),
|
||
} }
|
||
};
|
||
}
|
||
|
||
pub(crate) use content_entries;
|