解决MySql布尔型新旧版本兼容问题,采用枚举来表示布尔型的数据表。由正向工程赋值
大石头 authored at 2018-05-15 21:21:05
14.57 KiB
X
# 多语言本地化 NewLife.Core 的异常、日志与界面提示统一支持多语言。内置 31 种语言,英文为源文与兜底;默认跟随当前 UI 语言,缺失翻译自动回退英文。 ## 1. 选择语言 | 设置 | 作用 | |---|---| | `Setting.Language` | 全局语言设置。留空自动跟随系统;填写受支持语言后固定界面与日志语言。配置加载与保存时自动应用,清空后恢复自动识别 | | `CultureInfo.CurrentUICulture` | 默认语言来源。`HttpServer` 默认按请求 `Accept-Language` 设置;ASP.NET Core 启用 `RequestLocalization` 时同样按请求变化 | | `Localization.Culture` | 强制指定异常与常规文本语言;`null` 表示跟随 `CurrentUICulture` | | `Localization.LogCulture` | 日志与配置注释的语言;`null` 表示跟随 `Culture` | ```csharp // 全局固定中文(配置项,推荐) Setting.Current.Language = "zh-CN"; Setting.Current.Save(); // 代码内直接设置(进程级) Localization.SetLanguage("zh-CN"); Localization.SetLanguage(""); // 回到自动识别 // 启用请求语言识别时,让日志和配置注释保持英文 Localization.LogCulture = new CultureInfo("en-US"); ``` - `Localization.GetLanguages()` 返回当前支持的语言清单(内置 + 运行时注册),每项含配置键 `Name`、自称 `NativeName` 与英文名 `EnglishName`;`GetDisplayName()` 给出下拉框文本,如中文界面的 `德语 (Deutsch)`、英文界面的 `Chinese (Simplified) (简体中文)`。旧版别名(`in`/`iw`/`no`/`mo`)自动归一到 `id`/`he`/`nb`/`ro`,不会重复出现在清单里。区域数据未本地化(如英文界面、缺少 ICU 数据)时按英文名显示,可用词表以英文名为键改写;本包已内置全部 31 种语言的语言名,显式固定界面语言时也能按目标语言显示语言名。 - `Localization.GetLanguageOptions()` 直接把语言清单转成「语言键 → 显示名称」字典,管理端下拉框一行代码即可绑定;“自动”项由宿主添加,因为它的文本也要随界面语言本地化。 - `Localization.NormalizeCulture(name)` 归一化语言名称,如 `en`→`en-US`、`de-AT`→`de-DE`、`zh-Hant`/`zh-HK`→`zh-TW`、`in`→`id-ID`;不支持的语言返回 `null`。 - `Localization.IsSupported(culture)` 判断某区域是否有可用译文。 - Xml/Ini 配置文件注释会列出全部内置语言码;没有界面时,可直接按配置注释填写 `Language`。 - ASP.NET Core 仍可启用 `RequestLocalization` 做请求级识别;当 `Setting.Language` 非空时,管理端应在请求管道中强制覆盖,保证界面与日志都使用固定语言。 - 语言表在配置加载之后才可用(如插件延迟加载)时,宿主可在插件初始化完成后调用 `Setting.Current.ApplyLanguage()` 重新应用配置语言。 `HttpServer`/`HttpSession` 默认对每个请求按 `Accept-Language` 设置 UI 文化,无需注册中间件: - 语言优先级:`Setting.Language`/`Localization.Culture`(固定语言)→ `Accept-Language` 中 q 权重最高且受支持的语言 → 服务器当前语言; - 只修改 `CurrentUICulture`,不影响数字与日期格式; - 宿主固定语言后,`Accept-Language` 不再生效。 其它 Web 宿主(如自研 HTTP 协议处理)可显式使用 `RequestCultureScope`: ```csharp using var culture = RequestCultureScope.Create(acceptLanguage); ``` ## 2. 在代码中使用 | 场景 | 写法 | 是否需要改调用点 | |---|---|---| | 自定义异常 | `throw new XException("Session {0} is closed.", sessionId);` | 否,构造器自动翻译 | | BCL 异常 | `throw new InvalidOperationException(L.T("Session {0} is closed.", sessionId));` | 是,替换消息 | | 日志 | `XTrace.WriteLine("Session {0} is closed.", sessionId);` | 否,`Logger.Format` 自动翻译 | | 手工拼装的日志片段 | `L.TLog("Session {0} is closed.", sessionId);` | 是,改用日志语言入口 | | 带实例前缀的日志 | `Log?.InfoPrefixed(prefix, format, args)` | 是,改用 ILog 扩展 | | 自定义 Logger | 重写 `Format` 时调用 `Localization.TranslateForLog(format, args)` | 是,一行 | - 日志使用**格式字符串 + 参数**,不要用字符串插值(`$"..."`):插值后模板已展开,无法再按原文查表。 - 模板按**整句**查表,参数本身不会翻译。不要把英文状态词作为 `{n}` 参数塞进模板,否则会得到中英混排;应按语义拆成多条完整英文句子。 - 实例前缀(如 `[Session]`、`LogPrefix`)必须在翻译之后拼接;`InfoPrefixed` / `ErrorPrefixed` / `WritePrefixed` 已按此处理。 - Xml/Ini 配置提供者在保存时按 `Localization.LogCulture ?? Localization.Culture` 翻译 `[Description]`/`[DisplayName]` 注释;两者都未显式设置时输出英文源文,不跟随 `CurrentUICulture`。 ## 3. 下游组件接入 在组件项目下新建 `Locales/<culture>.yml`,每行一条: ```yaml "Session {0} is closed.": "会话 {0} 已关闭。" ``` - 键必须是**英文原文**,且与代码中的模板完全一致;占位符集合必须一致,否则构建失败。 - `L.T(...)`/`Localization.Translate(...)` 的字面量必须有词条(`check-locales.ps1` 会报「缺少词条」),否则该处永远只能得到英文。 - 英文是消息源文与兜底,通常**不需要 `en.yml`**;缺失翻译不报错,运行时回退英文。若元数据源文为中文,可增加 `en.yml` 只放中文 key → 英文译文;`check-locales.ps1` 对 `en` 放行缺失的消息键(回退英文源文),运行时同样先查 `en` 词表再回退原文。 - 用 `PackageReference` 引用主包时,`Locales/*.yml` 会自动挂载,通常无需修改 csproj;用 `ProjectReference` 直连源码时需显式声明: ```xml <ItemGroup> <AdditionalFiles Include="Locales\*.yml" /> </ItemGroup> ``` - 应用(可执行程序集)的译文默认覆盖 Core 同名词条,类库默认只补充新词条;类库确需覆盖时: ```xml <PropertyGroup> <NewLifeLocalizationOverwrite>true</NewLifeLocalizationOverwrite> </PropertyGroup> ``` - 运行时新增语言(插件、租户自定义等,无需重新编译): ```csharp Localization.Register(new CultureInfo("th-TH"), thaiTable); ``` ### 3.1 应用标题与描述 应用自身的标题/描述(`AssemblyTitle`/`Description`)按以下约定处理: - `AssemblyTitle` 与 `Description` 都用**英文源文**,并加入应用自己的 `Locales`;英文源文同时是运行时词表键。 - 显示与安装注册都按当前语言翻译:控制台标题、状态输出、管理面板等显示点,以及 Windows SCM、systemd、procd 等服务注册,都使用翻译后的副本;原始 `DisplayName`/`Description` 配置值保持不变。 - 服务名永不翻译;用户配置的 `DisplayName`/`Description` 也统一走 `L.T`,没有词条时原样使用,因此自定义品牌名不受影响。 - 服务注册信息在安装时确定;安装后切换界面语言不会改写已注册信息,需要时重新执行安装/重装。 - NuGet 包元数据不随界面语言翻译;`Description` 保持纯英文供反射使用,允许用 `PackageDescription` 承接中英混排的描述;运行时翻译只影响显示与注册。 - 下游词表校验应把 `.csproj` 的 `AssemblyTitle`/`Description` 纳入源文扫描,避免被判定为孤儿键。 ### 3.2 数据与元数据文本 实体/控制器/枚举的描述属于**表结构元数据**,不是运行时消息: - 元数据保持项目母语:`Model.xml` 为简体中文时,词表 key 就是中文,例如 `"用户": "User"`、`"名称": "Name"`、`"查看": "View"`。`Localization` 只做精确字符串匹配,不要求 key 是英文;同一份 `Locales/<culture>.yml` 里可以同时存在英文消息 key 与中文元数据 key。框架自身(如 `MachineInfo`)的属性文案用英文源文。 - 元数据词条由**所属程序集**自带(数据类库放自己的 `Locales/<culture>.yml`,XCode 内置实体放 `XCode/Locales/`),随包分发,运行时跨程序集自动合并。 - **所有语言必须齐全**:元数据键与消息键同等待遇,任何语言缺键都报错。仅当词表中存在源码里找不到的键(如外部 `Model.xml` 提供的显示名)时,才用 `metadata-keys.json` 登记以豁免孤儿键判定;`check-locales.ps1` 会自动读取同名文件,也可用 `-MetadataKeys` 指定。本仓所有键均可溯源到源码,故未启用该清单。 - 元数据只在 UI 渲染层翻译:日志、审计、数据库注释、原始 API 数据、数据库内部标识保持源文。 - 源文改名会导致 key 变化,旧译文失配;用 `collect-metadata-keys.ps1` 审计覆盖率:默认报告各语言缺键与「全语言覆盖」结论,`-Update` 重写非源码来源键清单,`-OutFile coverage.csv` 导出覆盖矩阵。 - 框架内置模型(如 XCode Membership)是产品 UI 的一部分,可在框架自己的 `Locales` 中补齐 en/繁中等译文;其他语言可先回退中文源文。 ## 4. 语言清单与回退 | Culture | 回退映射 | |---|---| | `en` | 默认与兜底 | | `zh-Hans` | `zh-CN` / `zh-SG` → `zh-Hans` | | `zh-Hant` | `zh-TW` / `zh-HK` / `zh-MO` → `zh-Hant` | | 其余语言 | 语言子标签相同即命中,如 `de-AT` → `de`、`pt-BR` → `pt`、`es-MX` → `es` | 当前内置 31 种语言: | 区域 | 语言 | |---|---| | 东亚 | `zh-Hans`、`zh-Hant`、`ja`、`ko` | | 东南亚/南亚 | `vi`、`th`、`id`、`ms`、`hi`、`bn` | | 中东 | `ar`、`he`、`fa` | | 欧洲 | `de`、`fr`、`es`、`pt`、`it`、`nl`、`pl`、`cs`、`sv`、`da`、`nb`、`fi`、`el`、`ro`、`hu` | | 其他 | `ru`、`uk`、`tr` | 新增语言时把 `<culture>.yml` 放入 `NewLife.Core/Locales/` 即可,构建期会校验键集合、占位符与格式说明符。 ## 5. 测试注意 - `Localization.Culture` 是进程级全局状态,并行用例会互相干扰;改写它的测试应归入串行集合(本仓使用 `[Collection("GlobalState")]`)。 - 库测试优先断言异常类型、错误码或行为,不要依赖具体译文;必须断言文案时,显式设置语言。 - CI 的 `CurrentUICulture` 常为 en 或 invariant,断言中文会失败;`InvariantGlobalization=true` 下自动回退英文。 ## 6. 品牌、术语与元数据 ### 6.1 品牌不译 以下名称是商标或产品代号,所有语言**原样保留**,不翻译、不拆词、不加大小写变体,也不作为词条加入 `Locales/*.yml`: `NewLife`、`newlifex.com`、`XTrace`、`XCode`、`Cube`、`Stardust` - 词表是整句精确匹配且区分大小写,句中出现的品牌名不会被一条单词词条替换;不要新增 `"NewLife"`、`"newlife"` 这类条目。 - 团队中文名「新生命团队」是官方命名而非译文。仅当确有一条消息整句就是团队名时才入表,并受 §6.2 键集合一致性约束(31 份齐全,非中文语种填英文原文)。 - 品牌名本身不单独成条;但**含品牌的产品全名**在确有官方本地化名称时可以入表(如 `NewLife Core Library` → `NewLife核心组件库`)——它是整条完整消息,不是对品牌名的替换。 ### 6.2 词表只收源码中真实存在的完整消息 两条校验决定了这条规则: - 31 份词表的键集合必须完全一致,**不能只给部分语言加键**(`Tools/Scripts/check-locales.ps1`); - 基准表的键必须在源码中找到字面量,**不能预置尚未使用的键**(同脚本的孤儿键校验);确属外部来源的键用 `metadata-keys.json` 登记后豁免。 - 源码里显式调用 `L.T` 的字面量必须已在基准表,否则该处永远只能得到英文(同脚本的「缺少词条」校验)。 因此不做通用术语预置:独立成句的短标签(如 `Error`、`Enabled`)本身就是完整消息,正常入表;句子中间的术语由译者侧术语表统一译法,不靠新增词条。技术词允许按目标语言习惯音译或改写,如波斯语把 `Unix` 写作 `یونیکس`。 ### 6.3 程序集与 NuGet 元数据 程序集元数据(`AssemblyTitle`、`AssemblyDescription`)与 NuGet 元数据(`PackageDescription`、`PackageTags`)都是单值:Windows 文件属性与 nuget.org 只读中立元数据,不会随界面语言切换。因此采用"英文源文 + 词表 + 展示层翻译"三段式: - csproj 的 `AssemblyTitle`/`Description` 用**英文源文**,并把**同一个字符串**作为 key 写入本程序集的 `Locales/*.yml`;中文给译文,其余语言填英文原文。 - 下游反射读取 `AssemblyTitleAttribute`/`AssemblyDescriptionAttribute` 后,在展示处调用 `L.T(value)` 即可本地化;元数据与配置中的值保持英文,不被改写。 - `check-locales.ps1` 的孤儿键扫描已把 `.csproj` 纳入源文范围,因此这些 key 不会被判为孤儿键。 - NuGet 包允许中英混排:用 `PackageDescription` 承接中英双语描述,`Description` 保持纯英文供反射使用;`PackageTags` 同样保留中英关键词。 - 长描述不进界面,关于页只显示标题、版本与公司;多语言说明页交给由 `README.md` 生成的 `README.nuget.md`,其顶部语言切换行已指向各语言 README。 ### 6.4 下游术语整理流程 `check-english-exceptions.ps1` 与 `check-locales.ps1` 都支持下游复用: 1. `check-english-exceptions.ps1 -Dir src -Csv hits.csv` 盘点自家中文消息; 2. 改成英文源文,日志与异常用格式字符串 + 参数,不要字符串插值; 3. 在组件目录新建 `Locales/<culture>.yml` 批量翻译,术语表放在项目内部约束一致性; 4. `check-locales.ps1 -LocaleDir ... -SourceDir ...` 校验键集合、占位符与孤儿键; 5. 编译期覆盖不了的场景用 `Localization.Register(culture, table, overwrite)` 运行时注入。 ## 7. 收口记录 以下为本地化审计中**已决策不处理**的项,后续扫描不再重复登记。 | 项 | 位置 | 处置与理由 | |---|---|---| | 目标语言恰好同词的消息 | `es` 的 `Error`、`nl` 的 `Open`、`da`/`nb` 的 `Send`、10 种语言的 `Online:` | 这些词在目标语言中本就如此书写,保留原文即正确译法 | | 波斯语把 `Unix` 音译为 `یونیکس` | `fa.yml` | 技术词允许按目标语言习惯音译(§6.2),不做"必须保留原词"的硬校验 | | `PackageReleaseNotes` 仍为中文 | `NewLife.Core.csproj` | 属发版流程专属字段,日常提交不改动,发版时统一整理 | | `XException` 未纳入「缺少词条」校验 | `Tools/Scripts/check-locales.ps1` | 现状覆盖率完整(未入表的 3 处为注释代码或纯格式模板);`L.T` 是显式本地化入口,作为门禁更精准 |