merge: origin/dev(1623f68)合入 M197 分支——解 golden/tail.rs 冲突
基线:合的是 **origin/dev**(`1623f68`)。`origin/main` 是 D41 的孤儿发布快照 (`d616d65` parents 为空),不参与合并。用 merge 而不是 rebase,保住三轮复审 clean 的 tip。 冲突只有一个:`crates/i18n/src/catalog/golden/tail.rs`(热点文件,多条 mission 都往这里加 key)。 解法(不重排别人的行、不改别人的译文): 1. 以 **dev 侧 398 行**为底稿(含其它 mission 后加的 key); 2. 把 M197 的 68 行(`SettingsJavaTooNew` … `SettingsJavaReasonInstallRootTooLong`) 整块插在 `SettingsJavaReasonWriteFailed` 之后、M174 助手域(`SettingsGroupAssistant`)之前 —— 即 `settings_java_entries` 那段之后,装配链的正确位置; 3. 两侧合起来 463 行会越过 400 行硬线,于是按既有先例把**M174 助手域起的那半截** (dev 的 260 行)整段纯搬移到 `golden/tail_ai.rs`:`tail.rs` 211 行 / `tail_ai.rs` 274 行, 链序 `golden → tail → tail_ai → tail2` 不变,逐字节不删不改; 4. 解冲突时发现 dev 的 golden 行还是 M197 之前的旧文案(`SettingsJavaEmptyBody` / `SettingsJavaAddHint` 的反引号、`SettingsJavaNeverScannedBody` 的旧说法),已把这三条的 金表行同步回域文件里的现行文案(`split_keeps_every_translation` 逐位置比对当场抓出来的)。 顺带(合并导致的越线):`crates/core/src/download/engine.rs` 两侧各 400 行、合并后 401 (我 +1 行 `kind: err.kind`,dev +1 行),把模块文档里那三行散文重排成两行(一字未删)→ 400 行。 同一次还把这文件里对 `MirrorTable::set_gh_proxy` 的旧描述改成 M193 的现实(那两个函数已被删)。 验证:fmt 0 · check(app-iced+core+i18n all-targets)0 · i18n 36 · engine_mock 8 · app-iced java 45 · core lib java 71 · setting 116 · app-iced lib 全量 832;全仓 file-size 守门脚本复制执行 → 超限 0。
This commit is contained in:
commit
6e80fd3563
283 files changed
+22601
-2188
No files matched your search
@@ -57,7 +57,9 @@ macro_rules! account_entries {
|
||||
AccountDeviceRetry => ("Try again", "重试"),
|
||||
AccountDeviceCancel => ("Cancel", "取消"),
|
||||
AccountDeviceBrowser => ("Sign in with browser", "用浏览器登录"),
|
||||
AccountDeviceUseCode => ("Use a device code instead", "改用设备码"),
|
||||
// M178 复审 r1 P2-5:这是**主按钮**(设备码流),不能读起来像备选。
|
||||
AccountDeviceSignIn => ("Sign in with your Microsoft account", "使用 Microsoft 账户登录"),
|
||||
AccountDeviceFallbackHint => ("No browser handy? This way shows a code you can type on any device.", "浏览器打不开就用这个:它会给一串用户码,在任何设备上输入即可。"),
|
||||
// ── 第三方站表单
|
||||
AccountServerLabel => ("Server address", "服务器地址"),
|
||||
AccountServerHint => ("API root, e.g. https://littleskin.cn/api/yggdrasil/", "API 根,例:https://littleskin.cn/api/yggdrasil/"),
|
||||
@@ -115,6 +117,23 @@ macro_rules! account_entries {
|
||||
// ── 重新登录的引导
|
||||
AccountHintAuthlibReauth => ("Enter the password again for {server}.", "请输入 {server} 的密码。"),
|
||||
AccountHintOfflineNoRelogin => ("Local profiles have no server account to sign in again — create a new profile instead.", "本地档案没有可重新登录的服务器账户,请新建一个档案。"),
|
||||
// ── 浏览器登录回退到设备码的说明(M178)
|
||||
// 口径:这不是「登录失败」——设备码接着跑。第一句说清「已改用设备码」,
|
||||
// 需要改配置的那一条**必须给可照抄的下一步**(哪个门户、加什么值、端口不参与匹配),
|
||||
// 且**不许**出现 AAD 的原始报文(`invalid_request` / `AADSTS…` / JSON)——
|
||||
// 用户 2026-11-08 实测撞到的就是那张原始错误页。
|
||||
AccountBrowserFallbackNotRegistered => (
|
||||
"This build's app registration has no loopback redirect URI, so browser sign-in cannot work; switched to a device code. To fix: add http://localhost/auth under Redirect URIs in the Azure app registration (the port is ignored), then restart the launcher.",
|
||||
"这个构建的应用注册里没有登记回环重定向 URI,浏览器登录用不了,已自动改用设备码。怎么修:在 Azure 应用注册的「重定向 URI」里加上 http://localhost/auth(端口不参与匹配),然后重启启动器。"
|
||||
),
|
||||
AccountBrowserFallbackUnverified => (
|
||||
"Could not confirm that browser sign-in is usable, so it switched to a device code.",
|
||||
"没能确认浏览器登录是否可用(预检没成功),已自动改用设备码。"
|
||||
),
|
||||
AccountBrowserFallbackFailed => (
|
||||
"Browser sign-in did not finish, so it switched to a device code. If the browser page said the redirect URI is not valid, add http://localhost/auth under Redirect URIs of the app registration.",
|
||||
"浏览器登录没有完成,已自动改用设备码。如果浏览器里提示 redirect_uri 无效,请把 http://localhost/auth 加进应用注册的「重定向 URI」。"
|
||||
),
|
||||
} }
|
||||
};
|
||||
}
|
||||
@@ -125,6 +144,48 @@ pub(crate) use account_entries;
|
||||
mod tests {
|
||||
use crate::{Catalog, Key, Lang};
|
||||
|
||||
/// **M178**:浏览器登录回退到设备码时那一句,必须「说清为什么 + 怎么修」,且**不许**把
|
||||
/// AAD 的原始报文搬上屏(用户实测撞到的正是那张 `invalid_request ... redirect_uri` 错误页)。
|
||||
///
|
||||
/// 抓得住:文案退回「登录失败,完整原因在日志里」、删掉可照抄的重定向 URI、或有人把
|
||||
/// AAD 原始错误码/JSON 塞进词条。
|
||||
/// 抓不住:读起来顺不顺(那靠 review 与截图)。
|
||||
#[test]
|
||||
fn the_browser_fallback_copy_explains_itself_without_raw_aad_text() {
|
||||
for &lang in Lang::ALL {
|
||||
let catalog = Catalog::of(lang);
|
||||
let not_registered = catalog.text(Key::AccountBrowserFallbackNotRegistered);
|
||||
let unverified = catalog.text(Key::AccountBrowserFallbackUnverified);
|
||||
let failed = catalog.text(Key::AccountBrowserFallbackFailed);
|
||||
// 「已改用设备码」这条结论三句里都得有(用户要知道画面为什么变了)。
|
||||
for copy in [not_registered, unverified, failed] {
|
||||
assert!(
|
||||
copy.contains("device code") || copy.contains("设备码"),
|
||||
"{}: {copy}",
|
||||
lang.tag()
|
||||
);
|
||||
// 原文不上屏:这两条是 AAD 的机器码,出现即语言/脱敏纪律失效。
|
||||
assert!(!copy.contains("invalid_request"), "{}: {copy}", lang.tag());
|
||||
assert!(!copy.contains("AADSTS"), "{}: {copy}", lang.tag());
|
||||
assert!(!copy.contains('{'), "{}: {copy}", lang.tag());
|
||||
}
|
||||
// 「没登记回环」这一条必须给**可照抄**的下一步(门户 + 值 + 端口不参与匹配)。
|
||||
assert!(
|
||||
not_registered.contains("http://localhost/auth"),
|
||||
"{not_registered}"
|
||||
);
|
||||
assert!(
|
||||
not_registered.to_lowercase().contains("redirect uri")
|
||||
|| not_registered.contains("重定向 URI"),
|
||||
"{not_registered}"
|
||||
);
|
||||
assert!(
|
||||
not_registered.to_lowercase().contains("port") || not_registered.contains("端口"),
|
||||
"{not_registered}"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// **M132**:缺 client id 这条是本页唯一「说清原因」的失败文案,它的纪律比别的正文更硬。
|
||||
///
|
||||
/// 抓得住:文案退回「登录失败 / 完整原因在日志里」、退化成「未支持」、或者只说缺 id 却
|
||||
|
||||
@@ -95,6 +95,29 @@ macro_rules! content_entries {
|
||||
"Installing from CurseForge is not available yet",
|
||||
"CurseForge 来源的安装尚未开放"
|
||||
),
|
||||
// --- M191:安装失败的两档分组与「只重试这些」---
|
||||
// 两档**各一条**:不兼容要换版本/加载器,取不到要检查网络,合并成一句会让用户
|
||||
// 对着一条重试必然失败的项反复点重试。
|
||||
ContentInstallFailedTitle => (
|
||||
"Install did not finish — {count} items failed",
|
||||
"安装未完成 —— {count} 项没装上"
|
||||
),
|
||||
ContentInstallRetryIncompatible => (
|
||||
"Not available for this version/loader",
|
||||
"不兼容所选版本/加载器"
|
||||
),
|
||||
ContentInstallRetryUnreachable => (
|
||||
"Could not fetch (network or missing project)",
|
||||
"取不到(网络 / 项目不存在)"
|
||||
),
|
||||
ContentInstallRetryOnly => (
|
||||
"Retry just these {count}",
|
||||
"只重试这 {count} 个"
|
||||
),
|
||||
ContentInstallRetryHint => (
|
||||
"Retry asks again only for the items above; files that already landed stay put.",
|
||||
"重试只重新解析上面这几项,已落盘的文件不动。"
|
||||
),
|
||||
// --- M113: pack import / export(PACKUI)---
|
||||
PackTitle => ("Modpack import & export", "整合包导入 / 导出"),
|
||||
PackImportTitle => ("Import a modpack", "导入整合包"),
|
||||
@@ -156,8 +179,17 @@ macro_rules! content_entries {
|
||||
),
|
||||
PackReportRejectedNote => ("not supported by the client", "客户端不支持"),
|
||||
PackCancelled => (
|
||||
"Import keeps running in the background — this result is discarded. Files already written stay.",
|
||||
"导入会在后台继续跑完 —— 本次结果作废;已经写下的文件会留下。"
|
||||
"Cancel requested — the pipeline is stopping now; the real outcome follows.",
|
||||
"已请求取消 —— 后台正在停手,真实结果随后给出。"
|
||||
),
|
||||
PackStatusCancelled => ("Cancelled — rolled back", "已取消 —— 已回滚"),
|
||||
PackCancelIncomplete => (
|
||||
"Cancelled — rollback incomplete: the target directory may differ from before the import.",
|
||||
"已取消 —— 回滚没做完:目标目录可能与导入前不同。"
|
||||
),
|
||||
PackCancelTooLate => (
|
||||
"The cancel arrived too late: this import already finished. The instance is kept; same-name files may already be overwritten.",
|
||||
"取消来得太晚:这次导入已经跑完,实例会保留,同名文件可能已被覆盖。"
|
||||
),
|
||||
PackOverwriteAction => ("Re-import, the pack wins", "以包为准重新导入"),
|
||||
PackOverwriteNote => (
|
||||
|
||||
@@ -47,10 +47,6 @@ macro_rules! general_entries {
|
||||
SettingsGroupAppearance => ("Appearance", "外观"),
|
||||
SettingsGroupJava => ("Java", "Java 运行时"),
|
||||
SettingsGroupDownloads => ("Downloads", "下载"),
|
||||
// 逐实例覆盖的空态(M106)
|
||||
SettingsOverridesEmptyTitle => ("No per-instance overrides", "没有逐实例覆盖"),
|
||||
SettingsOverridesEmptyBody => ("Overrides let one instance keep its own Java runtime, memory or JVM flags while the rest of the launcher keeps the defaults above.", "逐实例覆盖让某个实例保留自己的 Java 运行时、内存或 JVM 参数,其余实例沿用上面的默认值。"),
|
||||
SettingsOverridesAdd => ("Add override", "新增覆盖"),
|
||||
// `Route::Agent` 的回退卡正文(M106:`app/view.rs` 里最后一条硬编码英文)
|
||||
AgentFallbackBody => ("The assistant page is not built yet. Chat lives in the docked panel once the AI mission lands; this screen is the M68 skeleton fallback.", "助手页还没做。AI 接线落地后对话会出现在右侧停靠面板;这一屏是 M68 的骨架回退。"),
|
||||
} }
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
//! 金表的**第二段**(`InstancesLaunchReset` 之后到 M175 的 Java 域结束)。
|
||||
//! 金表的**后半段的前半截**(`InstancesLaunchReset` 之后、到 M197 的 Java 域结束)。
|
||||
//!
|
||||
//! 为什么拆:`golden.rs` 在 M157 加了 19 行启动参数文案后到 416 行,越过 400 行硬线;
|
||||
//! 拆分口径与仓库其它 `-p1/-p2` 一致——**纯搬移、一行不改**,顺序即 `Key::ALL` 的后半段。
|
||||
//! 拼装与断言在 `golden.rs` 的 [`rows`] 与 `catalog/tests.rs`。
|
||||
//!
|
||||
//! M197 又拆了一次:本段原本一路写到旧 `tail.rs` 末尾(329 行),加了「下载一个 JDK / 三个设置键」
|
||||
//! 那 65 条文案后会越过 400 行硬线,于是把 **M174 助手域起的后半截** 搬进 [`super::tail_ai`]
|
||||
//! (同样纯搬移、一行不改)。现在的链序是 `golden` → 本段 → `tail_ai` → `tail2`。
|
||||
//! **M197 起本段只到 Java 域结束**:M197 往 `settings_java_entries` 追加了 68 行,加上 dev 侧
|
||||
//! 其它 mission 后加的行会涨到 470+(越过 400 行硬线),于是把 **M174 助手域起的那半截**
|
||||
//! 纯搬移到 [`super::tail_ai`](链序:`golden` → `tail` → `tail_ai` → `tail2`)。
|
||||
//! 搬移只改位置:其余每一行逐字节原样(含其它 mission 后加的行)。
|
||||
pub(super) const ROWS: &[(&str, &str, &str)] = &[
|
||||
("LoaderVanilla", "Vanilla", "原版"),
|
||||
("LaunchOutcomeNormal", "Finished normally", "正常结束"),
|
||||
@@ -28,11 +29,58 @@ pub(super) const ROWS: &[(&str, &str, &str)] = &[
|
||||
("FontCjkReady", "Chinese font ready: {family}", "中文字体已就绪:{family}"),
|
||||
("SettingsLanguage", "Language", "界面语言"),
|
||||
("SettingsLanguageHint", "Switches the interface language. Chinese text needs a Chinese font.", "切换界面语言。显示中文需要中文字体。"),
|
||||
("SettingsLogsFolder", "Log folder", "日志文件夹"),
|
||||
("SettingsLogsFolderHint", "What explains a crash lives here: the launcher's own runtime log (launcher.log, rolled over at 1 MiB with one old copy kept) and one jsonl per game session under logs/game/. Sessions from older versions (data/agent/sessions) are still read, not moved.", "解释一次崩溃要看的东西都在这:启动器自己的运行日志(launcher.log,满 1 MiB 滚动一次、只留一份旧档)与每次游戏会话的 jsonl(logs/game/)。旧版本留下的会话(data/agent/sessions)仍然读得出来,不会被搬动。"),
|
||||
("SettingsLogsCopy", "Copy path", "复制路径"),
|
||||
("SettingsLogsUnknown", "Data directory not resolved — restart the launcher from its own folder", "数据根没解析出来——从启动器所在目录重新启动试试"),
|
||||
("SettingsAppearanceCorner", "Window corners", "窗口圆角"),
|
||||
("SettingsAppearanceCornerSystem", "Follow system", "跟随系统"),
|
||||
("SettingsAppearanceCornerSquare", "Square", "直角"),
|
||||
("SettingsAppearanceCornerHint", "On Windows 11 and Server 2025 we ask the system for rounded corners; Windows 10 keeps square corners. On macOS and Linux the desktop's own rules apply. We never draw corners ourselves.", "Windows 11 与 Windows Server 2025 上我们请求系统处理外框圆角;Windows 10 保持直角;macOS 与 Linux 交给桌面合成器自己的规则;我们不自己画圆角。"),
|
||||
("SettingsAppearanceCornerSuspended", "Not in effect right now: window transparency is below 100%. Setting it back to 100% restores the rounded corners.", "当前因整窗透明度不生效:把整窗透明度调回 100% 圆角即恢复。"),
|
||||
// M180:外观分组补齐(`catalog/settings/appearance.rs` 的第二批,M116 圆角那 5 条之后)。
|
||||
// 位置就是装配链里那个域的位置——`settings_appearance_entries` 在 `settings_java_entries` 之前,
|
||||
// 所以这 40 行必须落在 `SettingsAppearanceCornerSuspended` 与 `SettingsJavaScan` 之间
|
||||
// (顺序 = `Key::ALL`;为排到文件尾而重排别人的族会让 `split_keeps_every_translation` 整片红)。
|
||||
("SettingsAppearanceWindowOpacity", "Window transparency", "整窗透明度"),
|
||||
("SettingsAppearanceWindowOpacityHint", "Applies to the whole window. Floor {floor}; 1.00 = fully opaque.", "作用于整个窗口。下限 {floor};1.00 = 完全不透明。"),
|
||||
("SettingsAppearancePanelOpacity", "Panel transparency", "面板透明度"),
|
||||
("SettingsAppearancePanelOpacityHint", "Only the panel/card layer. Floor {floor}; independent of window transparency.", "只作用于面板 / 卡片这一层。下限 {floor};与整窗透明度互不影响。"),
|
||||
("SettingsAppearancePanelOpacityForcedValue", "100% (forced from {requested})", "100%(强制,原请求值 {requested})"),
|
||||
("SettingsAppearancePanelOpacityForcedHint", "Software renderer: panels and glass stay fully opaque (window transparency still works).", "软件渲染:面板与玻璃效果恒为不透明(整窗透明度仍然有效)。"),
|
||||
("SettingsAppearanceBackgroundImage", "Background image", "背景图"),
|
||||
("SettingsAppearanceOff", "Off", "关"),
|
||||
("SettingsAppearanceOn", "On", "开"),
|
||||
("SettingsAppearanceImagePath", "Image path", "图片路径"),
|
||||
("SettingsAppearancePaletteLevel", "Colours: {level}", "配色:{level}"),
|
||||
("SettingsAppearanceBgFill", "Fill", "填充方式"),
|
||||
("SettingsAppearanceBgFillCover", "Cover", "铺满"),
|
||||
("SettingsAppearanceBgFillContain", "Contain", "完整显示"),
|
||||
("SettingsAppearanceBgFillTile", "Tile", "平铺"),
|
||||
("SettingsAppearanceBgDim", "Dim", "暗化"),
|
||||
("SettingsAppearanceBgDimHint", "Blends the image toward the panel colour so text stays readable. Max {max}.", "把背景图往面板底色上退,让文字读得清。上限 {max}。"),
|
||||
("SettingsAppearancePaletteSource", "Colour source", "配色来源"),
|
||||
("SettingsAppearancePaletteBackground", "Background", "取自背景图"),
|
||||
("SettingsAppearancePaletteWallpaper", "Wallpaper", "取自壁纸"),
|
||||
("SettingsAppearancePalettePreset", "Preset", "内置色卡"),
|
||||
("SettingsAppearanceBgReadFailed", "Background image could not be read: {reason}", "背景图读不出来:{reason}"),
|
||||
("SettingsAppearanceBgDecodeFailed", "Background image could not be decoded ({reason}; this build decodes PNG, JPEG and BMP)", "背景图解不开({reason};这个构建只支持 PNG、JPEG 与 BMP)"),
|
||||
("SettingsAppearanceBgTooLarge", "Background image is too large to decode: {width}×{height} (limit {limit} px per edge)", "背景图太大,解不开:{width}×{height}(每边上限 {limit} 像素)"),
|
||||
("SettingsAppearanceBgSmallTile", "Small image ({width}×{height}): tiles are scaled up so the window stays covered (max {max} tiles, no gaps).", "图片很小({width}×{height}):图块会被放大,保证窗口铺满(最多 {max} 块,不留缝)。"),
|
||||
("SettingsAppearancePaletteBgUndecodable", "Background image could not be read; colours fall back.", "背景图读不出来,配色回落到下一级。"),
|
||||
("SettingsAppearancePaletteBgTooSmall", "Background image is too small to sample; colours fall back.", "背景图太小,取不了色,配色回落到下一级。"),
|
||||
("SettingsAppearancePaletteBgBadDimensions", "Background image data looks inconsistent; colours fall back.", "背景图数据前后不一致,配色回落到下一级。"),
|
||||
("SettingsAppearancePaletteBgNoOpaquePixels", "Background image is almost fully transparent; colours fall back.", "背景图几乎全透明,配色回落到下一级。"),
|
||||
("SettingsAppearancePaletteBgLowChroma", "Background image is too monochrome to tint from; colours fall back.", "背景图太单调(彩度不足),取不出配色,回落到下一级。"),
|
||||
("SettingsAppearancePaletteBgContrast", "Background image colours would be unreadable; colours fall back.", "按背景图取的颜色读不清,配色回落到下一级。"),
|
||||
("SettingsAppearancePaletteWallpaperUndecodable", "Desktop wallpaper could not be read.", "桌面壁纸读不出来。"),
|
||||
("SettingsAppearancePaletteWallpaperTooSmall", "Desktop wallpaper is too small to sample.", "桌面壁纸太小,取不了色。"),
|
||||
("SettingsAppearancePaletteWallpaperBadDimensions", "Desktop wallpaper data looks inconsistent.", "桌面壁纸数据前后不一致。"),
|
||||
("SettingsAppearancePaletteWallpaperNoOpaquePixels", "Desktop wallpaper is almost fully transparent.", "桌面壁纸几乎全透明。"),
|
||||
("SettingsAppearancePaletteWallpaperLowChroma", "Desktop wallpaper is too monochrome to tint from.", "桌面壁纸太单调(彩度不足),取不出配色。"),
|
||||
("SettingsAppearancePaletteWallpaperContrast", "Desktop wallpaper colours would be unreadable; colours fall back.", "按桌面壁纸取的颜色读不清,配色回落到下一级。"),
|
||||
("SettingsAppearancePalettePresetContrast", "The built-in card needed a small contrast correction.", "内置色卡做了一点对比度修正。"),
|
||||
("SettingsAppearancePaletteAdjusted", "A few colours were nudged for readability.", "有几个颜色为了可读性做了微调。"),
|
||||
// M175:设置页「Java」分组(`catalog/settings/java.rs` 的 `settings_java_entries`)。
|
||||
// 位置就是装配链里那个域的位置:`settings_java_entries` 在 `settings_appearance_entries` 之后、
|
||||
// `settings_download_entries`(空)与 `settings_ai_entries`(M174)之前,所以这几行必须落在
|
||||
@@ -45,11 +93,8 @@ pub(super) const ROWS: &[(&str, &str, &str)] = &[
|
||||
("SettingsJavaCancelled", "The scan was cancelled; the list below is from the previous run.", "扫描已取消;下面的列表还是上一次的结果。"),
|
||||
("SettingsJavaScanDone", "{count} JDK found", "找到 {count} 个 JDK"),
|
||||
("SettingsJavaNeverScanned", "Not scanned yet", "还没扫描过"),
|
||||
// M197:这句以前写「在那之前启动器不会自己去扫」,与首屏自动扫(`app/state/construct.rs`
|
||||
// 的 `hydrate_java_settings`)互相打脸;现在说的是同一件事。
|
||||
("SettingsJavaNeverScannedBody", "The list is still empty. The launcher scans once by itself when the settings page opens; press Scan now to look again right away.", "列表还是空的。打开设置页时启动器会自己扫一次;也可以点「立即扫描」立刻再扫一遍。"),
|
||||
("SettingsJavaEmptyTitle", "No Java runtime found", "未检测到 Java 运行时"),
|
||||
// M197:删掉 markdown 反引号(渲染层没有内联代码通道,反引号会原样上屏)。
|
||||
("SettingsJavaEmptyBody", "Nothing matched. Add the path of a JDK you already unpacked below, or install one with ull java install 21 on the command line.", "没找到任何 JDK。可以在下面手动加上你自己解压的那份,或者用命令行 ull java install 21 装一个。"),
|
||||
("SettingsJavaDefault", "Default", "默认"),
|
||||
("SettingsJavaSetDefault", "Use as default", "设为默认"),
|
||||
|
||||
@@ -4,6 +4,11 @@
|
||||
//! 拼装见 `golden.rs` 的 [`super::rows`]。
|
||||
|
||||
pub(super) const ROWS: &[(&str, &str, &str)] = &[
|
||||
("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", "读取清单"),
|
||||
@@ -24,7 +29,10 @@ pub(super) const ROWS: &[(&str, &str, &str)] = &[
|
||||
("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.", "导入会在后台继续跑完 —— 本次结果作废;已经写下的文件会留下。"),
|
||||
("PackCancelled", "Cancel requested — the pipeline is stopping now; the real outcome follows.", "已请求取消 —— 后台正在停手,真实结果随后给出。"),
|
||||
("PackStatusCancelled", "Cancelled — rolled back", "已取消 —— 已回滚"),
|
||||
("PackCancelIncomplete", "Cancelled — rollback incomplete: the target directory may differ from before the import.", "已取消 —— 回滚没做完:目标目录可能与导入前不同。"),
|
||||
("PackCancelTooLate", "The cancel arrived too late: this import already finished. The instance is kept; same-name files may already be overwritten.", "取消来得太晚:这次导入已经跑完,实例会保留,同名文件可能已被覆盖。"),
|
||||
("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", "离线导入"),
|
||||
@@ -135,7 +143,8 @@ pub(super) const ROWS: &[(&str, &str, &str)] = &[
|
||||
("AccountDeviceRetry", "Try again", "重试"),
|
||||
("AccountDeviceCancel", "Cancel", "取消"),
|
||||
("AccountDeviceBrowser", "Sign in with browser", "用浏览器登录"),
|
||||
("AccountDeviceUseCode", "Use a device code instead", "改用设备码"),
|
||||
("AccountDeviceSignIn", "Sign in with your Microsoft account", "使用 Microsoft 账户登录"),
|
||||
("AccountDeviceFallbackHint", "No browser handy? This way shows a code you can type on any device.", "浏览器打不开就用这个:它会给一串用户码,在任何设备上输入即可。"),
|
||||
("AccountServerLabel", "Server address", "服务器地址"),
|
||||
("AccountServerHint", "API root, e.g. https://littleskin.cn/api/yggdrasil/", "API 根,例:https://littleskin.cn/api/yggdrasil/"),
|
||||
("AccountServerProbe", "Check server", "校验服务器"),
|
||||
@@ -180,6 +189,9 @@ pub(super) const ROWS: &[(&str, &str, &str)] = &[
|
||||
("AccountNoticeTitleDone", "Account updated", "账户已更新"),
|
||||
("AccountHintAuthlibReauth", "Enter the password again for {server}.", "请输入 {server} 的密码。"),
|
||||
("AccountHintOfflineNoRelogin", "Local profiles have no server account to sign in again — create a new profile instead.", "本地档案没有可重新登录的服务器账户,请新建一个档案。"),
|
||||
("AccountBrowserFallbackNotRegistered", "This build's app registration has no loopback redirect URI, so browser sign-in cannot work; switched to a device code. To fix: add http://localhost/auth under Redirect URIs in the Azure app registration (the port is ignored), then restart the launcher.", "这个构建的应用注册里没有登记回环重定向 URI,浏览器登录用不了,已自动改用设备码。怎么修:在 Azure 应用注册的「重定向 URI」里加上 http://localhost/auth(端口不参与匹配),然后重启启动器。"),
|
||||
("AccountBrowserFallbackUnverified", "Could not confirm that browser sign-in is usable, so it switched to a device code.", "没能确认浏览器登录是否可用(预检没成功),已自动改用设备码。"),
|
||||
("AccountBrowserFallbackFailed", "Browser sign-in did not finish, so it switched to a device code. If the browser page said the redirect URI is not valid, add http://localhost/auth under Redirect URIs of the app registration.", "浏览器登录没有完成,已自动改用设备码。如果浏览器里提示 redirect_uri 无效,请把 http://localhost/auth 加进应用注册的「重定向 URI」。"),
|
||||
("SettingsGroupUpdate", "Updates", "更新"),
|
||||
("SettingsUpdateCurrent", "Current version", "当前版本"),
|
||||
("SettingsUpdateChannel", "Channel", "更新渠道"),
|
||||
|
||||
@@ -7,7 +7,9 @@
|
||||
//! content / pack 各行,它们本来就在这个位置)。
|
||||
//!
|
||||
//! 拼装见 [`super::super::golden::rows`](链序:`golden` → `tail` → 本段 → `tail2`)。
|
||||
|
||||
//!
|
||||
//! 正文是 **dev 侧 `tail.rs` 从 M174 助手域起** 的全部行(含其它 mission 后加的行),
|
||||
//! 逐字节原样搬来;本段只负责把它们放到 `Key::ALL` 的同一位置。
|
||||
pub(super) const ROWS: &[(&str, &str, &str)] = &[
|
||||
// M174:设置页「助手(AI)」分组(`catalog/settings/ai.rs` 的 `settings_ai_entries`)。
|
||||
// 位置就是装配链里那个域的位置:`settings_ai_entries` 在 `settings_download_entries` 之后、
|
||||
@@ -122,6 +124,31 @@ pub(super) const ROWS: &[(&str, &str, &str)] = &[
|
||||
("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", "选择实例"),
|
||||
("CrashDialogTitleFailed", "Launch failed", "启动失败"),
|
||||
("CrashDialogTitleCrashed", "The game crashed", "游戏崩溃"),
|
||||
("CrashDialogExitCode", "Exit code {code}", "退出码 {code}"),
|
||||
("CrashDialogStartedThenDied", "The game process started, then exited abnormally.", "游戏进程起来了,随后异常退出。"),
|
||||
("CrashDialogSilentFailure", "The game exited with code 0 but never really started.", "游戏以退出码 0 结束,但并没有真正起来。"),
|
||||
("CrashDialogStageReason", "It failed before the game could start: {stage}", "游戏还没起来就失败了:{stage}"),
|
||||
("CrashDialogCrashReportFound", "The game wrote a crash report — the details are below.", "游戏写出了崩溃报告,详见下方说明。"),
|
||||
("CrashDialogNoGameLog", "No crash report this time: the game stopped before it could write one. The last lines the launcher captured are below.", "这次没有生成崩溃报告:游戏还没来得及写就停了。下面是启动器捕获的最后若干行。"),
|
||||
("CrashDialogNoLogAtAll", "No crash report and no captured output. Most likely: missing Java, not enough memory, wrong launch arguments, or missing libraries.", "既没有崩溃报告,也没有捕获到输出。最可能的原因:缺少 Java、内存不足、启动参数错误、或库文件缺失。"),
|
||||
("CrashDialogShowDetail", "Show crash details", "展开崩溃说明"),
|
||||
("CrashDialogHideDetail", "Hide crash details", "收起崩溃说明"),
|
||||
("CrashDialogLauncherLogs", "Launcher logs", "启动器日志"),
|
||||
("CrashDialogSessionLog", "This session", "本次会话"),
|
||||
("CrashDialogGameLog", "Game latest.log", "游戏 latest.log"),
|
||||
("CrashDialogCrashReports", "Crash reports", "崩溃报告目录"),
|
||||
("CrashDialogCapturedTail", "CAPTURED OUTPUT (last {count} lines)", "捕获输出(最后 {count} 行)"),
|
||||
("CrashDialogExport", "Export report bundle", "导出诊断包"),
|
||||
("CrashDialogOpenFolder", "Open log folder", "打开日志文件夹"),
|
||||
("CrashDialogCopyPaths", "Copy file paths", "复制文件路径"),
|
||||
("CrashDialogClose", "Close", "关闭"),
|
||||
("CrashDialogCopied", "File paths copied to the clipboard", "文件路径已复制到剪贴板"),
|
||||
("CrashDialogExportDone", "Exported {bytes} bytes to {path}", "已导出 {bytes} 字节到 {path}"),
|
||||
("CrashDialogExportFailed", "Export failed: {error}", "导出失败:{error}"),
|
||||
("CrashDialogExportTruncated", "{count} file(s) were too large and are truncated (head + tail kept).", "有 {count} 个文件太大,已截断(保留头 + 尾)。"),
|
||||
("CrashDialogOpenFailed", "Could not open the folder: {error}", "打不开文件夹:{error}"),
|
||||
("AgentTitle", "Assistant", "助手"),
|
||||
("AgentSubtitle", "Simulated assistant · no real model is connected", "模拟助手 · 还没接真实模型"),
|
||||
("AgentCollapse", "Collapse", "收起"),
|
||||
@@ -170,9 +197,6 @@ pub(super) const ROWS: &[(&str, &str, &str)] = &[
|
||||
("SettingsGroupAppearance", "Appearance", "外观"),
|
||||
("SettingsGroupJava", "Java", "Java 运行时"),
|
||||
("SettingsGroupDownloads", "Downloads", "下载"),
|
||||
("SettingsOverridesEmptyTitle", "No per-instance overrides", "没有逐实例覆盖"),
|
||||
("SettingsOverridesEmptyBody", "Overrides let one instance keep its own Java runtime, memory or JVM flags while the rest of the launcher keeps the defaults above.", "逐实例覆盖让某个实例保留自己的 Java 运行时、内存或 JVM 参数,其余实例沿用上面的默认值。"),
|
||||
("SettingsOverridesAdd", "Add override", "新增覆盖"),
|
||||
("AgentFallbackBody", "The assistant page is not built yet. Chat lives in the docked panel once the AI mission lands; this screen is the M68 skeleton fallback.", "助手页还没做。AI 接线落地后对话会出现在右侧停靠面板;这一屏是 M68 的骨架回退。"),
|
||||
("VersionRailTitle", "Version type", "版本类型"),
|
||||
("VersionMajorTitle", "Major version", "主版本"),
|
||||
@@ -235,6 +259,11 @@ pub(super) const ROWS: &[(&str, &str, &str)] = &[
|
||||
("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 来源的安装尚未开放"),
|
||||
("ContentInstallFailedTitle", "Install did not finish — {count} items failed", "安装未完成 —— {count} 项没装上"),
|
||||
("ContentInstallRetryIncompatible", "Not available for this version/loader", "不兼容所选版本/加载器"),
|
||||
("ContentInstallRetryUnreachable", "Could not fetch (network or missing project)", "取不到(网络 / 项目不存在)"),
|
||||
("ContentInstallRetryOnly", "Retry just these {count}", "只重试这 {count} 个"),
|
||||
("ContentInstallRetryHint", "Retry asks again only for the items above; files that already landed stay put.", "重试只重新解析上面这几项,已落盘的文件不动。"),
|
||||
("PackTitle", "Modpack import & export", "整合包导入 / 导出"),
|
||||
("PackImportTitle", "Import a modpack", "导入整合包"),
|
||||
("PackDropHint", "Drop a .mrpack, .zip or a MultiMC/Prism instance folder here", "把 .mrpack / .zip 或 MultiMC·Prism 实例目录拖到这里"),
|
||||
@@ -242,9 +271,4 @@ pub(super) const ROWS: &[(&str, &str, &str)] = &[
|
||||
("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", "新建实例"),
|
||||
];
|
||||
@@ -33,6 +33,32 @@ macro_rules! logs_entries {
|
||||
"启动器或游戏运行时,日志会实时出现在这里。选择一个实例并启动,就能看到第一批日志。"
|
||||
),
|
||||
LogsEmptyAction => ("Choose an instance", "选择实例"),
|
||||
// --- 启动失败弹窗(M187)---
|
||||
CrashDialogTitleFailed => ("Launch failed", "启动失败"),
|
||||
CrashDialogTitleCrashed => ("The game crashed", "游戏崩溃"),
|
||||
CrashDialogExitCode => ("Exit code {code}", "退出码 {code}"),
|
||||
CrashDialogStartedThenDied => ("The game process started, then exited abnormally.", "游戏进程起来了,随后异常退出。"),
|
||||
CrashDialogSilentFailure => ("The game exited with code 0 but never really started.", "游戏以退出码 0 结束,但并没有真正起来。"),
|
||||
CrashDialogStageReason => ("It failed before the game could start: {stage}", "游戏还没起来就失败了:{stage}"),
|
||||
CrashDialogCrashReportFound => ("The game wrote a crash report — the details are below.", "游戏写出了崩溃报告,详见下方说明。"),
|
||||
CrashDialogNoGameLog => ("No crash report this time: the game stopped before it could write one. The last lines the launcher captured are below.", "这次没有生成崩溃报告:游戏还没来得及写就停了。下面是启动器捕获的最后若干行。"),
|
||||
CrashDialogNoLogAtAll => ("No crash report and no captured output. Most likely: missing Java, not enough memory, wrong launch arguments, or missing libraries.", "既没有崩溃报告,也没有捕获到输出。最可能的原因:缺少 Java、内存不足、启动参数错误、或库文件缺失。"),
|
||||
CrashDialogShowDetail => ("Show crash details", "展开崩溃说明"),
|
||||
CrashDialogHideDetail => ("Hide crash details", "收起崩溃说明"),
|
||||
CrashDialogLauncherLogs => ("Launcher logs", "启动器日志"),
|
||||
CrashDialogSessionLog => ("This session", "本次会话"),
|
||||
CrashDialogGameLog => ("Game latest.log", "游戏 latest.log"),
|
||||
CrashDialogCrashReports => ("Crash reports", "崩溃报告目录"),
|
||||
CrashDialogCapturedTail => ("CAPTURED OUTPUT (last {count} lines)", "捕获输出(最后 {count} 行)"),
|
||||
CrashDialogExport => ("Export report bundle", "导出诊断包"),
|
||||
CrashDialogOpenFolder => ("Open log folder", "打开日志文件夹"),
|
||||
CrashDialogCopyPaths => ("Copy file paths", "复制文件路径"),
|
||||
CrashDialogClose => ("Close", "关闭"),
|
||||
CrashDialogCopied => ("File paths copied to the clipboard", "文件路径已复制到剪贴板"),
|
||||
CrashDialogExportDone => ("Exported {bytes} bytes to {path}", "已导出 {bytes} 字节到 {path}"),
|
||||
CrashDialogExportFailed => ("Export failed: {error}", "导出失败:{error}"),
|
||||
CrashDialogExportTruncated => ("{count} file(s) were too large and are truncated (head + tail kept).", "有 {count} 个文件太大,已截断(保留头 + 尾)。"),
|
||||
CrashDialogOpenFailed => ("Could not open the folder: {error}", "打不开文件夹:{error}"),
|
||||
} }
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,12 +1,38 @@
|
||||
//! 设置页「Appearance」分组文案(M116 首次落地:窗口圆角一档)。
|
||||
//! 设置页「Appearance」分组文案(M116 落了圆角一族;M180 `I18NAPPEAR` 补齐全组)。
|
||||
//!
|
||||
//! 加 key 时把条目写进 `$($acc)*` 后面即可(形状见 [`crate::catalog`] 的「域装配」一节)。
|
||||
//!
|
||||
//! ## 为什么只有圆角这一组的 key
|
||||
//! ## 两批 key:圆角(M116)在前,其余(M180)在后
|
||||
//!
|
||||
//! 本分组的既有控件(整窗/面板透明度、背景图、配色来源……)是 M93 落的,文案是英文基线字面量
|
||||
//! (`pages/appearance/section.rs`)。M116 新增的圆角档位**从一开始就走词表**(tower 口径:
|
||||
//! 设置项文案不许硬编码),所以这里先只有它这一组;老控件接词表归 i18n 接线 mission。
|
||||
//! M116 只落了圆角那 5 条;本分组其余控件(整窗/面板透明度、背景图、填充、暗化、配色来源)
|
||||
//! 是 M93 落的,文案一直是英文基线字面量。M172 审计量出这一页 27 处文案里 22 处硬编码、中文界面
|
||||
//! 本地化 0/27。M180 把**控件文案 + 运行时诊断 + 配色回落提示**全部搬进本文件:
|
||||
//!
|
||||
//! | 批次 | 条目 | 来源 |
|
||||
//! |---|---|---|
|
||||
//! | M116 | `Corner` … `CornerSuspended` | `pages/appearance/section/window.rs` 的圆角一段 |
|
||||
//! | M180 | `WindowOpacity` … `PaletteAdjusted` | 卡片各段标签/说明 + `state.rs` / `background.rs` / `palette.rs` 的诊断 prose |
|
||||
//!
|
||||
//! ## 为什么诊断也在这里(而不是留在 `state.rs`)
|
||||
//!
|
||||
//! `pages/appearance/state.rs` 已经 390 行、贴着 400 行硬线,放不下任何一张文案表。所以那一层只
|
||||
//! 给 **key + 已格式化好的参数**(`background::Note`),句子一律在本文件里。
|
||||
//!
|
||||
//! ## 数值参数:模板里只有 `{name}`,格式在 Rust 侧做
|
||||
//!
|
||||
//! [`crate::catalog::Catalog::render`] 只做 `{name}` → 值的字面替换,**不认** `{:.2}` 这类格式说明。
|
||||
//! 所以「位置/尺寸/上限」这些数字由调用方先 `format!("{:.2}")` 好再当参数传进来:
|
||||
//!
|
||||
//! | key | 原字面量 | 现在 |
|
||||
//! |---|---|---|
|
||||
//! | `SettingsAppearancePanelOpacityForcedValue` | `format!("100% (forced from {:.2})")` | `{requested}` = `format!("{:.2}")` |
|
||||
//! | `SettingsAppearanceWindowOpacityHint` | `Floor {:.2}` = `WINDOW_ALPHA_FLOOR` | `{floor}` |
|
||||
//! | `SettingsAppearancePanelOpacityHint` | `Floor 0.40` = `PANEL_ALPHA_FLOOR` | `{floor}` |
|
||||
//! | `SettingsAppearanceBgDimHint` | `Max 0.95` = `BG_DIM_MAX` | `{max}` |
|
||||
//! | `SettingsAppearanceBgTooLarge` | `(limit 8192 px per edge)` = `MAX_SOURCE_EDGE` | `{limit}` |
|
||||
//! | `SettingsAppearanceBgSmallTile` | `(max 64 tiles …)` = `MAX_TILES` | `{max}` |
|
||||
//!
|
||||
//! 常量走占位符而不是写进译文,是为了「改常量不用改两套译文」(M180 任务口径)。
|
||||
//!
|
||||
//! ## 文案口径(不许暗示所有平台一致)
|
||||
//!
|
||||
@@ -28,8 +54,205 @@ macro_rules! settings_appearance_entries {
|
||||
"Not in effect right now: window transparency is below 100%. Setting it back to 100% restores the rounded corners.",
|
||||
"当前因整窗透明度不生效:把整窗透明度调回 100% 圆角即恢复。"
|
||||
),
|
||||
// ---- M180 `I18NAPPEAR`:卡片内文案(① 整窗 / ③ 面板 / ④~⑧ 背景图 / ⑨ 配色来源)
|
||||
SettingsAppearanceWindowOpacity => ("Window transparency", "整窗透明度"),
|
||||
SettingsAppearanceWindowOpacityHint => (
|
||||
"Applies to the whole window. Floor {floor}; 1.00 = fully opaque.",
|
||||
"作用于整个窗口。下限 {floor};1.00 = 完全不透明。"
|
||||
),
|
||||
SettingsAppearancePanelOpacity => ("Panel transparency", "面板透明度"),
|
||||
SettingsAppearancePanelOpacityHint => (
|
||||
"Only the panel/card layer. Floor {floor}; independent of window transparency.",
|
||||
"只作用于面板 / 卡片这一层。下限 {floor};与整窗透明度互不影响。"
|
||||
),
|
||||
SettingsAppearancePanelOpacityForcedValue => (
|
||||
"100% (forced from {requested})",
|
||||
"100%(强制,原请求值 {requested})"
|
||||
),
|
||||
SettingsAppearancePanelOpacityForcedHint => (
|
||||
"Software renderer: panels and glass stay fully opaque (window transparency still works).",
|
||||
"软件渲染:面板与玻璃效果恒为不透明(整窗透明度仍然有效)。"
|
||||
),
|
||||
SettingsAppearanceBackgroundImage => ("Background image", "背景图"),
|
||||
SettingsAppearanceOff => ("Off", "关"),
|
||||
SettingsAppearanceOn => ("On", "开"),
|
||||
SettingsAppearanceImagePath => ("Image path", "图片路径"),
|
||||
SettingsAppearancePaletteLevel => ("Colours: {level}", "配色:{level}"),
|
||||
SettingsAppearanceBgFill => ("Fill", "填充方式"),
|
||||
SettingsAppearanceBgFillCover => ("Cover", "铺满"),
|
||||
SettingsAppearanceBgFillContain => ("Contain", "完整显示"),
|
||||
SettingsAppearanceBgFillTile => ("Tile", "平铺"),
|
||||
SettingsAppearanceBgDim => ("Dim", "暗化"),
|
||||
SettingsAppearanceBgDimHint => (
|
||||
"Blends the image toward the panel colour so text stays readable. Max {max}.",
|
||||
"把背景图往面板底色上退,让文字读得清。上限 {max}。"
|
||||
),
|
||||
SettingsAppearancePaletteSource => ("Colour source", "配色来源"),
|
||||
SettingsAppearancePaletteBackground => ("Background", "取自背景图"),
|
||||
SettingsAppearancePaletteWallpaper => ("Wallpaper", "取自壁纸"),
|
||||
SettingsAppearancePalettePreset => ("Preset", "内置色卡"),
|
||||
// ---- M180:运行时诊断(`background.rs` 的三种读盘失败 + 小图平铺)。
|
||||
//
|
||||
// M180 r1 复审 P2-1:软件渲染那句**只有一条**,就挂在面板那一行(`PanelOpacityForcedHint`)
|
||||
// 上——它挨着被禁用的滑块,正好回答「为什么拖不动」;这里不再有第二句。
|
||||
SettingsAppearanceBgReadFailed => (
|
||||
"Background image could not be read: {reason}",
|
||||
"背景图读不出来:{reason}"
|
||||
),
|
||||
SettingsAppearanceBgDecodeFailed => (
|
||||
"Background image could not be decoded ({reason}; this build decodes PNG, JPEG and BMP)",
|
||||
"背景图解不开({reason};这个构建只支持 PNG、JPEG 与 BMP)"
|
||||
),
|
||||
SettingsAppearanceBgTooLarge => (
|
||||
"Background image is too large to decode: {width}×{height} (limit {limit} px per edge)",
|
||||
"背景图太大,解不开:{width}×{height}(每边上限 {limit} 像素)"
|
||||
),
|
||||
SettingsAppearanceBgSmallTile => (
|
||||
"Small image ({width}×{height}): tiles are scaled up so the window stays covered (max {max} tiles, no gaps).",
|
||||
"图片很小({width}×{height}):图块会被放大,保证窗口铺满(最多 {max} 块,不留缝)。"
|
||||
),
|
||||
// ---- M180:配色回落提示(`palette.rs` 把 design 的 `hint_key()` 翻成下面这些)
|
||||
SettingsAppearancePaletteBgUndecodable => (
|
||||
"Background image could not be read; colours fall back.",
|
||||
"背景图读不出来,配色回落到下一级。"
|
||||
),
|
||||
SettingsAppearancePaletteBgTooSmall => (
|
||||
"Background image is too small to sample; colours fall back.",
|
||||
"背景图太小,取不了色,配色回落到下一级。"
|
||||
),
|
||||
SettingsAppearancePaletteBgBadDimensions => (
|
||||
"Background image data looks inconsistent; colours fall back.",
|
||||
"背景图数据前后不一致,配色回落到下一级。"
|
||||
),
|
||||
SettingsAppearancePaletteBgNoOpaquePixels => (
|
||||
"Background image is almost fully transparent; colours fall back.",
|
||||
"背景图几乎全透明,配色回落到下一级。"
|
||||
),
|
||||
SettingsAppearancePaletteBgLowChroma => (
|
||||
"Background image is too monochrome to tint from; colours fall back.",
|
||||
"背景图太单调(彩度不足),取不出配色,回落到下一级。"
|
||||
),
|
||||
SettingsAppearancePaletteBgContrast => (
|
||||
"Background image colours would be unreadable; colours fall back.",
|
||||
"按背景图取的颜色读不清,配色回落到下一级。"
|
||||
),
|
||||
SettingsAppearancePaletteWallpaperUndecodable => (
|
||||
"Desktop wallpaper could not be read.",
|
||||
"桌面壁纸读不出来。"
|
||||
),
|
||||
SettingsAppearancePaletteWallpaperTooSmall => (
|
||||
"Desktop wallpaper is too small to sample.",
|
||||
"桌面壁纸太小,取不了色。"
|
||||
),
|
||||
SettingsAppearancePaletteWallpaperBadDimensions => (
|
||||
"Desktop wallpaper data looks inconsistent.",
|
||||
"桌面壁纸数据前后不一致。"
|
||||
),
|
||||
SettingsAppearancePaletteWallpaperNoOpaquePixels => (
|
||||
"Desktop wallpaper is almost fully transparent.",
|
||||
"桌面壁纸几乎全透明。"
|
||||
),
|
||||
SettingsAppearancePaletteWallpaperLowChroma => (
|
||||
"Desktop wallpaper is too monochrome to tint from.",
|
||||
"桌面壁纸太单调(彩度不足),取不出配色。"
|
||||
),
|
||||
SettingsAppearancePaletteWallpaperContrast => (
|
||||
"Desktop wallpaper colours would be unreadable; colours fall back.",
|
||||
"按桌面壁纸取的颜色读不清,配色回落到下一级。"
|
||||
),
|
||||
SettingsAppearancePalettePresetContrast => (
|
||||
"The built-in card needed a small contrast correction.",
|
||||
"内置色卡做了一点对比度修正。"
|
||||
),
|
||||
SettingsAppearancePaletteAdjusted => (
|
||||
"A few colours were nudged for readability.",
|
||||
"有几个颜色为了可读性做了微调。"
|
||||
),
|
||||
} }
|
||||
};
|
||||
}
|
||||
|
||||
pub(crate) use settings_appearance_entries;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::catalog::{placeholders, Catalog, Key};
|
||||
use crate::Lang;
|
||||
|
||||
/// 本域的 key(`Key::ALL` 里按名字前缀筛;不手工列表,漏一条就少一条守卫)。
|
||||
fn appearance_keys() -> Vec<Key> {
|
||||
Key::ALL
|
||||
.iter()
|
||||
.copied()
|
||||
.filter(|key| key.name().starts_with("SettingsAppearance"))
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// 有没有汉字(与 `pages/settings/tests/appearance_section.rs` 的 `is_han` 同一段码位)。
|
||||
fn is_han(ch: char) -> bool {
|
||||
('\u{4e00}'..='\u{9fff}').contains(&ch)
|
||||
}
|
||||
|
||||
/// **M180 守门 ②**:外观分组的每条 key 都必须真翻过——英文照抄进中文表就地判红。
|
||||
///
|
||||
/// 抓得住:① 新加 key 时中文栏直接抄英文(`chinese_catalog_is_actually_chinese` 也抓这条,
|
||||
/// 但那条在 `catalog/tests.rs`、不属于本 mission 的 scope,而且不查「中文字里有没有汉字」);
|
||||
/// ② 中文译文变成纯 ASCII(比如只写了 `PNG` / `Preset` 这种专名);③ 占位符集合两边不一致
|
||||
/// (`{floor}` 只在一边有 ⇒ `render()` 吐出半截句子)。
|
||||
/// 抓不住:译文翻得对不对(语义只能人审)。
|
||||
///
|
||||
/// 变异证明:把任一条的第二个字符串换成第一个(`("Cover", "Cover")`)⇒ 本条立刻红(`assert_ne!`
|
||||
/// 与 `is_han` 命中同一行)。
|
||||
#[test]
|
||||
fn every_appearance_key_is_translated_into_chinese() {
|
||||
let keys = appearance_keys();
|
||||
assert!(
|
||||
keys.len() >= 40,
|
||||
"外观分组至少 40 条 key(M180 落地时是 45 条),实际 {}:\
|
||||
key 被搬走或前缀改名了?",
|
||||
keys.len()
|
||||
);
|
||||
|
||||
let en = Catalog::of(Lang::En);
|
||||
let zh = Catalog::of(Lang::ZhCn);
|
||||
for key in keys {
|
||||
let (english, chinese) = (en.text(key), zh.text(key));
|
||||
assert_ne!(english, chinese, "{} 的中文是英文照抄", key.name());
|
||||
assert!(
|
||||
chinese.chars().any(is_han),
|
||||
"{} 的中文里一个汉字都没有:{chinese}",
|
||||
key.name()
|
||||
);
|
||||
let mut left = placeholders(english);
|
||||
let mut right = placeholders(chinese);
|
||||
left.sort_unstable();
|
||||
right.sort_unstable();
|
||||
assert_eq!(left, right, "{} 的占位符两套译文不一致", key.name());
|
||||
}
|
||||
}
|
||||
|
||||
/// 数值参数必须走占位符,不许把常量写进译文(改常量时不该还要改两套译文)。
|
||||
#[test]
|
||||
fn numeric_limits_are_placeholders_not_text() {
|
||||
let en = Catalog::of(Lang::En);
|
||||
for (key, needle, placeholder) in [
|
||||
(Key::SettingsAppearanceWindowOpacityHint, "0.08", "floor"),
|
||||
(Key::SettingsAppearancePanelOpacityHint, "0.40", "floor"),
|
||||
(Key::SettingsAppearanceBgDimHint, "0.95", "max"),
|
||||
(Key::SettingsAppearanceBgTooLarge, "8192", "limit"),
|
||||
(Key::SettingsAppearanceBgSmallTile, "64", "max"),
|
||||
] {
|
||||
let text = en.text(key);
|
||||
assert!(
|
||||
!text.contains(needle),
|
||||
"{} 把常量 {needle} 写死在译文里了(应当走 {{{placeholder}}})",
|
||||
key.name()
|
||||
);
|
||||
assert!(
|
||||
placeholders(text).contains(&placeholder),
|
||||
"{} 缺 {{{placeholder}}} 占位符:{text}",
|
||||
key.name()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,17 @@ macro_rules! settings_general_entries {
|
||||
"Switches the interface language. Chinese text needs a Chinese font.",
|
||||
"切换界面语言。显示中文需要中文字体。"
|
||||
),
|
||||
// ── 日志文件夹(M188:用户投诉「找不到崩溃日志」的直接落点)
|
||||
SettingsLogsFolder => ("Log folder", "日志文件夹"),
|
||||
SettingsLogsFolderHint => (
|
||||
"What explains a crash lives here: the launcher's own runtime log (launcher.log, rolled over at 1 MiB with one old copy kept) and one jsonl per game session under logs/game/. Sessions from older versions (data/agent/sessions) are still read, not moved.",
|
||||
"解释一次崩溃要看的东西都在这:启动器自己的运行日志(launcher.log,满 1 MiB 滚动一次、只留一份旧档)与每次游戏会话的 jsonl(logs/game/)。旧版本留下的会话(data/agent/sessions)仍然读得出来,不会被搬动。"
|
||||
),
|
||||
SettingsLogsCopy => ("Copy path", "复制路径"),
|
||||
SettingsLogsUnknown => (
|
||||
"Data directory not resolved — restart the launcher from its own folder",
|
||||
"数据根没解析出来——从启动器所在目录重新启动试试"
|
||||
),
|
||||
} }
|
||||
};
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user