fix(i18n): M92 r1 —— Windows 真 locale 走注册表子进程(零 FFI)+ 删掉假守卫
复审 p1:`detect_env()` 在 Windows 上恒为 `en`(`LC_ALL/LC_MESSAGES/LANG` 都不存在), D28 整条中文链(探测 → 扫 CJK → 需下载)在基线平台永不触发。复审在本机(zh-CN)实测 `lang=en raw=None`,这条我认。 **p1 修法(tower 定路线:子进程读注册表,不碰 FFI、不加依赖)** - 新增 `crates/app-iced/src/platform/locale.rs`:`SystemLocale` 实现 `unlucky_i18n::lang::LocaleSource`,内部 `reg.exe query "HKCU\Control Panel\International" /v LocaleName`(本机实测 `REG_SZ zh-CN`),读不到时退 `/v Locale` 的十六进制 LCID (本机 `00000804`),用一张 20 条的短表翻成标签,表外语言返回 `None`(宁可英文,不猜语种)。 先例同 M73 壁纸探测(`core::platform::wallpaper` 的 `reg.exe` 路线)。 - 子进程走注入的 `RegIo` 接口,单测用 `FakeReg`(不 spawn);夹具是**本机真机抓的原文** (含 CRLF / 前导空白),另有一条 `#[cfg(windows)]` 真机测试证明 `reg.exe` 这条路真的通。 - i18n 仍是纯函数:`lang::resolve(setting, platform, env)` 是四级优先级的唯一实现—— **`ui.language` 显式值 > 平台源 > POSIX 环境变量 > 英文**,`Detection` 新增 `origin` (`setting` / 源自报的 `label()` / `env` / `default`),启动日志因此能写清 `source=registry`。 配置值不认识时**跳过**继续往下走,不当成任何一种语言。 - `fonts::cjk::probe_with(setting, faces)` 把 `ui.language` 那一级的位置先留好(目前传 `None`: 设置层还没有读取点,写在模块文档的诚实边界里);`probe()` 走全链。 - `platform::mod` 的模块表补一行说明 locale 也是「零 unsafe」那一类。 **本机(zh-CN)实跑证据**(`--software-renderer --screenshot …`,exit 0): `[font] … | lang=zh-CN locale=zh-CN source=registry cjk=system:"Microsoft YaHei UI"` ——探测出中文、且走的是「扫 CJK 字体」而不是 `not-needed`。 **p2**:删掉 `catalog` 那条 `compile_fail` 守卫(复审两个变异仍全绿 ⇒ 它守的是「这段代码 编译不过」,不是「编译期校验还在」)。改为陈述事实:「`Key` 是枚举,拼错 `E0599`」, 并写明这里**没有**守卫测试及原因。 未动 `ui.language` 默认值(`core` schema 不在本 mission scope,tower 已另记)。
This commit is contained in:
1 parent
e77a5df6e8
commit
ce2c42032b
8 files changed
+513
-39
No files matched your search
@@ -4,6 +4,8 @@
|
||||
//!
|
||||
//! - **判据**(要不要中文字体、哪些族算候选、缺了要不要下载)全在 `unlucky_i18n::font`,是纯函数,
|
||||
//! 零 UI 依赖、脱机可测;
|
||||
//! - **语言**从 [`unlucky_i18n::lang::resolve`] 拿:`ui.language` 显式值 > [`SystemLocale`]
|
||||
//! (Windows 读注册表,零 FFI)> 环境变量 > 英文;
|
||||
//! - **本模块只做适配**:把 [`super::Face`] 列表变成一个「某族装没装」的查询,并处理
|
||||
//! **读不到字体库**这种情况。
|
||||
//!
|
||||
@@ -15,26 +17,28 @@
|
||||
//!
|
||||
//! ## 诚实边界(抓得住什么 / 抓不住什么)
|
||||
//!
|
||||
//! - **抓得住**:语言 + 字体库族名 → 策略(含 `Unprobeable` 这一支);
|
||||
//! - **抓不住**:族名存在 ≠ 字形覆盖(`SimSun` 也可能是个残缺子集)。真判据要开 `ttf-parser` 查 cmap,
|
||||
//! 本 mission 不引依赖(M60 Q3 spike 做过,结论见 `DevDocs`)。
|
||||
//! - **抓得住**:语言 + 字体库族名 → 策略(含 `Unprobeable` 这一支);优先级顺序(注入的假源可测);
|
||||
//! - **抓不住**:①族名存在 ≠ 字形覆盖(`SimSun` 也可能是个残缺子集)。真判据要开 `ttf-parser`
|
||||
//! 查 cmap,本 mission 不引依赖(M60 Q3 spike 做过,结论见 `DevDocs`);
|
||||
//! ②`ui.language` 这一级目前**没有接线**(设置层还没有读取点),调用点传 `None`。
|
||||
|
||||
use unlucky_i18n::font::{self, Strategy};
|
||||
use unlucky_i18n::lang::{self, Detection};
|
||||
use unlucky_i18n::lang::{self, Detection, EnvLocale};
|
||||
|
||||
use super::Face;
|
||||
use crate::platform::locale::SystemLocale;
|
||||
|
||||
/// 一次中文探测的结论(启动日志的第二段)。
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct CjkReport {
|
||||
/// 语言探测(含原始 locale,便于解释「为什么是英文」)。
|
||||
/// 语言探测(含原始 locale 与来源,便于解释「为什么是英文」)。
|
||||
pub detection: Detection,
|
||||
/// 字体策略结论。
|
||||
pub strategy: Strategy,
|
||||
}
|
||||
|
||||
impl CjkReport {
|
||||
/// 一行日志片段(`lang=zh-CN locale=zh_CN.UTF-8 cjk=system:"Microsoft YaHei"`)。
|
||||
/// 一行日志片段(`lang=zh-CN locale=zh-CN source=registry cjk=system:"Microsoft YaHei"`)。
|
||||
pub fn describe(&self) -> String {
|
||||
format!("{} {}", self.detection.describe(), self.strategy.describe())
|
||||
}
|
||||
@@ -55,9 +59,18 @@ pub fn report(detection: Detection, faces: Option<&[Face]>) -> CjkReport {
|
||||
}
|
||||
}
|
||||
|
||||
/// 探测本机(locale 走环境变量;换 Win32 真值源是后续 mission 的注入点)。
|
||||
/// 探测本机:优先级见 [`unlucky_i18n::lang::resolve`]。
|
||||
///
|
||||
/// `setting` 是配置里 `ui.language` 的原文(`zh_cn` / `en_us`);`None` = **还没读配置**
|
||||
/// (设置层的读取点尚未接线,见模块文档的诚实边界)。注意 `None` 与 `Some("不认识的值")`
|
||||
/// 效果相同:都不当显式选择,继续往下走。
|
||||
pub fn probe_with(setting: Option<&str>, faces: Option<&[Face]>) -> CjkReport {
|
||||
report(lang::resolve(setting, &SystemLocale, &EnvLocale), faces)
|
||||
}
|
||||
|
||||
/// 不读配置时的入口(启动路径用)。
|
||||
pub fn probe(faces: Option<&[Face]>) -> CjkReport {
|
||||
report(lang::detect_env(), faces)
|
||||
probe_with(None, faces)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -77,14 +90,16 @@ mod tests {
|
||||
fn zh() -> Detection {
|
||||
Detection {
|
||||
lang: unlucky_i18n::Lang::ZhCn,
|
||||
raw: Some("zh_CN.UTF-8".to_owned()),
|
||||
raw: Some("zh-CN".to_owned()),
|
||||
origin: "injected",
|
||||
}
|
||||
}
|
||||
|
||||
fn en() -> Detection {
|
||||
Detection {
|
||||
lang: unlucky_i18n::Lang::En,
|
||||
raw: Some("en_US.UTF-8".to_owned()),
|
||||
raw: Some("en-US".to_owned()),
|
||||
origin: "injected",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,7 +117,7 @@ mod tests {
|
||||
);
|
||||
assert_eq!(
|
||||
report.describe(),
|
||||
"lang=zh-CN locale=zh_CN.UTF-8 cjk=system:\"Microsoft YaHei\""
|
||||
"lang=zh-CN locale=zh-CN source=injected cjk=system:\"Microsoft YaHei\""
|
||||
);
|
||||
}
|
||||
|
||||
@@ -144,4 +159,29 @@ mod tests {
|
||||
report.describe()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn explicit_setting_overrides_the_platform_source() {
|
||||
// 抓得住:`ui.language` 显式值被平台源盖掉(用户手选中文/英文必须赢过系统语言);
|
||||
// 也让「平台源真的接在链路上」有个不依赖本机语言的证据。
|
||||
// 抓不住:`ui.language` 在真机上读到什么(设置层还没接线,调用点传 None)。
|
||||
let installed = faces(&["Microsoft YaHei"]);
|
||||
|
||||
let forced_english = probe_with(Some("en_us"), Some(&installed));
|
||||
assert_eq!(forced_english.detection.lang, unlucky_i18n::Lang::En);
|
||||
assert_eq!(
|
||||
forced_english.detection.origin,
|
||||
unlucky_i18n::lang::ORIGIN_SETTING
|
||||
);
|
||||
assert_eq!(forced_english.strategy, Strategy::NotNeeded);
|
||||
|
||||
let forced_chinese = probe_with(Some("zh_cn"), Some(&installed));
|
||||
assert_eq!(
|
||||
forced_chinese.strategy,
|
||||
Strategy::System {
|
||||
family: "Microsoft YaHei"
|
||||
}
|
||||
);
|
||||
assert!(forced_chinese.describe().contains("source=setting"));
|
||||
}
|
||||
}
|
||||
@@ -146,6 +146,7 @@ fn describe_is_one_line_and_mentions_all_three_stacks() {
|
||||
detection: Detection {
|
||||
lang: unlucky_i18n::Lang::En,
|
||||
raw: None,
|
||||
origin: unlucky_i18n::lang::ORIGIN_DEFAULT,
|
||||
},
|
||||
strategy: Strategy::NotNeeded,
|
||||
},
|
||||
|
||||
Reference in new issue
Block a user