Skip to content
bitzorcas
中EN

Guide

I18n 测试、运维与商业 GA

给出 I18n 的真实测试证据、HTTP/双 ORM/多实例/故障/安全/容量矩阵、指标告警、资源发布、备份恢复、迁移策略和商业 GA 阻断项。

Last updated

I18n 的 GA 不是“能返回中文”就完成。它必须证明同一请求在不同租户、办公室、实例、数据库、部署版本与故障阶段都得到可解释结果,而且翻译运营不会破坏全站可用性。

1. 当前自动化证据

测试面已有证据
Query Handler语言启用列表、Store Failure 传播、翻译 Prefix 调用
LocalizerStore Failure 后回退 Key 最后一段
ArchitectureQuery 依赖 ReadModelStore、生产默认端口 fail-closed、生成适配器存在
双 ORM parityPlatform/Tenant/Office 覆盖、删除后平台 fallback、语言启用/全部/默认
API Shell smokeReadModelStore 在组合根可解析/调用

没有 LanguageResolutionMiddleware 测试、SaveTranslation 测试、GetI18nResources 成功测试、JSON 测试、授权 HTTP 测试、缓存同步消费者测试、多实例一致性、并发、故障注入、容量与恢复证据。

2. 最小源码回归组合

Terminal window
# I18n 的三个证据面使用不同过滤式,避免把宽泛 parity 当成模块单测。
I18N_APP_FILTER="FullyQualifiedName~I18n"
I18N_ARCH_FILTER="FullyQualifiedName~I18n|FullyQualifiedName~MasterDataInfrastructure"
I18N_PARITY_FILTER="FullyQualifiedName~PortRepositoryParity"
# 应用层先证明 Handler 与 Localizer 的确定性行为。
dotnet test tests/BitzOrcas.Application.Tests/BitzOrcas.Application.Tests.csproj --filter "$I18N_APP_FILTER"
# 架构与集成层分别证明端口边界和双 ORM 适配器。
dotnet test tests/BitzOrcas.Architecture.Tests/BitzOrcas.Architecture.Tests.csproj --filter "$I18N_ARCH_FILTER"
dotnet test tests/BitzOrcas.Integration.Tests/BitzOrcas.Integration.Tests.csproj --filter "$I18N_PARITY_FILTER"

过滤器能快速定位,但 GA 流水线还应执行完整测试集,避免 I18n 对 Settings、MasterData、Authorization、缓存和 Host 的回归被漏掉。

3. HTTP 协商矩阵

用真实 WebApplicationFactory 覆盖 Header/Cookie/Query/Setting 的每种组合、q 权重、大小写、无效 Culture、禁用语言和无租户调用。对每个结果断言:响应值、Content-Language、Culture、缓存键和降级原因一致。

目标:显式偏好与响应语言保持一致
// 故意让 Header 第一项质量值更低,用来证明解析器真正比较 q 值。
using var request = new HttpRequestMessage(HttpMethod.Get, "/api/i18n/translations?LanguageCode=en-US");
request.Headers.TryAddWithoutValidation("Accept-Language", "fr-FR;q=0.1,en-US;q=1.0");
request.Headers.Add("Cookie", "bitzorcas.lang=en-US");
var response = await client.SendAsync(request, cancellationToken);
// 目标实现的响应头和可观测解析原因必须报告同一个规范语言。
response.EnsureSuccessStatusCode();
response.Content.Headers.ContentLanguage.ShouldContain("en-US");
await evidence.AssertResolvedLanguageAsync("en-US", reason: "explicit-user-preference");

这是目标行为示例,不是当前优先级断言。最终优先级必须由产品合同固定。

4. 授权与租户矩阵

分别测试匿名、普通 User、平台管理员、System、受信 Application、租户 A/B、Office 1/2。读取与保存都要验证 Resource/Action 到稳定 permission code 的真实映射。

关键攻击包括:无 Tenant 调用读取所有租户候选、TenantOffice 无 Office 退化为 0、Application caller 任意改平台文案、Tenant A 构造 Tenant B 标识、禁用语言仍可读写、未知 Scope int 绕过规则。

5. 资源 API 回归

先用数据库、JSON 与 fallback language 构造互补 Key,再分别调用 GetString、GetStringsAsync、GetTranslations 和 GetI18nResources。期望同一公开合同下值一致,并验证 Prefix 不泄漏其他模块资源。

目标:完整资源包合并三个来源
// 三个来源故意各提供不同 Key,避免同值掩盖错误的优先级。
await database.SaveAsync("Identity.Login.Title", "zh-CN", "登录", cancellationToken);
files.WriteJson("Identity.zh-CN.json", new { Identity_Login_Submit = "继续" });
await database.SaveAsync("Identity.Login.Help", "en-US", "Need help?", cancellationToken);
// Prefix 只能裁剪最终合并结果,不能让某个来源绕过模块边界。
var resources = await api.GetResourcesAsync("zh-CN", "Identity.", cancellationToken);
resources["Identity.Login.Title"].ShouldBe("登录"); // 请求语言 DB
resources["Identity.Login.Submit"].ShouldBe("继续"); // 请求语言 JSON
resources["Identity.Login.Help"].ShouldBe("Need help?"); // fallback language

测试辅助器应保留真实点分 Key;示例中的匿名对象字段只是表达来源,不是建议的 JSON 写法。

6. 保存故障注入

在 Repository Upsert、当前缓存清除、Sync Notify、前端 Notification 四个位置逐点失败。每次记录数据库、各实例缓存、同步事件和客户端响应,证明重试不会重复污染也不会永久失联。

当前顺序下 Upsert 后失败会部分成功。GA 目标应在事务内写 Translation + Outbox,提交后由独立消费者重试缓存/前端事件;Command 返回包含稳定 resource version。

7. 多实例一致性

至少启动两个真实 Host 和共享消息/数据库:实例 A 预热翻译,实例 B 保存,随后 A 必须在 SLO 内读到新值。再丢弃、延迟、乱序和重复同步事件,验证版本比较与 TTL 最终收敛。

不能只直接调用 Consumer;要覆盖消息路由、序列化、ResourceType、作用域、重试和 poison 策略。Platform 变更应影响所有租户缓存,Tenant/Office 变更不应全局惊群。

8. 双 ORM 与数据库差异

在每个支持数据库验证唯一索引、大小写 Collation、StartsWith SQL、软删除过滤、并发 upsert、长值/Unicode、OfficeId 字符串比较和事务隔离。SqlSugar 与 EF Core 最终外部 Result/Problem Details 必须一致。

Prefix 查询要保存实际执行计划与 P95;Contains(keys) 在大集合下可能生成超长 IN,应限制 Key 数量或使用临时表/批次策略。

9. 容量基线

建议维度:100/1万/100万 Key、2/10/50 语言、1/1000 租户、1/100 办公室、1/1000 并发、1/100 Key 批量、10/1000 次每分钟编辑。

记录 P50/P95/P99、Store 行数/扫描、缓存 hit/miss、字典对象大小、GC、线程池排队、同步 L() 阻塞、广播延迟、资源包 bytes、压缩率和 CDN hit。全语言字典按每个 Office 缓存可能形成显著内存乘数。

10. 指标与日志

  • i18n_language_resolution_total{source,result,fallback_reason};
  • i18n_translation_hit_total{source,scope,language},不带 tenant 原值;
  • i18n_missing_key_total{module,language};
  • i18n_cache_hit/miss/evict 与 key cardinality;
  • i18n_sync_lag_seconds、consumer failure、stale snapshot age;
  • save/review/publish/conflict/rollback;
  • resource package keys/bytes/generation duration;
  • invalid tag、disabled language、format placeholder error;
  • JSON load/parse/conflict/version。

日志记录 Key、规范化语言、scope、correlation 与 version,不默认记录 Value;翻译可能包含客户品牌、姓名模板或敏感运营内容。

11. 告警

重点告警:资源 API 连续返回空包、missing-key 比例激增、平台翻译批量改变、同步延迟超过 SLO、实例 snapshot version 分叉、缓存 cardinality/内存突增、JSON 解析失败、无租户读取、平台写身份异常和占位符格式错误。

告警应区分“翻译缺失但安全回退”和“持久化不可用导致降级”,否则表面 200 会掩盖数据库故障。

12. 资源发布流程

JSON 资源应在构建期校验 schema、Key 命名、重复、语言覆盖率、占位符一致性、HTML 策略和 UTF-8。生成 manifest:resource version、commit、语言、文件 hash、Key count。

滚动发布期间资源版本可能不同。API 可返回 ETag/manifest version,前端按版本缓存;数据库覆盖也应记录基于哪个基线资源版本制作,防止升级后 override 语义漂移。

13. 备份与恢复

备份 SysTranslation、SysLanguage、翻译 Revision/Published pointer、审计与 Outbox(完成 GA 后)。恢复后逐租户验证默认语言唯一、启用语言、自然键、Scope 组合、Active/Published 投影、软删除和 resource version。

数据库恢复与应用 JSON 版本必须兼容。若恢复到旧数据库但部署了新基线资源,需要 reconciliation 报告:新增/删除 Key、过期 override、占位符变化和冲突。

14. 迁移与回滚

规范化 LanguageCode 前先生成 dry-run 清单,检测大小写/别名合并冲突;迁移自然键时先修复非法 Tenant/Office/Scope 行。不要直接加唯一约束让生产部署在脏数据上失败。

发布状态机迁移应把现有 Active 行创建为初始 Published Revision,并保留旧 ID 映射。回滚应用版本时,数据库 schema、资源 manifest 与旧客户端缓存协议都要保持兼容。

15. 商业 GA 阻断项

  1. GetI18nResources 当前空结果;
  2. 单键与批量 JSON/fallback 语义不一致;
  3. 语言协商不处理 q 值、支持目录与 canonical tag;
  4. 默认语言种子与多租户语言目录没有闭环;
  5. Tenant/Office 缺失与非法 Scope 未失败关闭;
  6. Platform 写仅依赖 caller type,缺专权、审批与审计;
  7. TranslationStatus 没有真实审核/发布生命周期;
  8. 保存与通知无事务 Outbox、版本与补偿;
  9. 全 Tag 失效带来跨租户惊群,广播失败可陈旧 30 分钟;
  10. JSON 无资源文件、嵌入读取、确定冲突、热更新和错误可观测性;
  11. 参数、复数、富文本、时区/货币合同缺失;
  12. HTTP、多实例、并发、故障、容量、迁移和恢复证据不足。

16. 发布门禁清单

Terminal window
# 不允许资源端点继续以空 Keys 调用会丢弃结果的批量方法。
rg -n "GetStringsAsync\(\s*Array.Empty<string>" src/Platform/I18n -g '*.cs'
# 目标生命周期、版本、Outbox 与协商能力。
rg -n "PublishedRevision|ExpectedVersion|Outbox|ResolvedLanguage|Quality" \
src/Platform/I18n src/Hosts/BitzOrcas.Api -g '*.cs'
# I18n 测试面应从当前两类文件扩展到 HTTP、缓存、JSON 与故障套件。
rg -n "I18n|LanguageResolution|JsonResourceLoader|TranslationCacheSync" \
tests -g '*.cs'

I18n 总览 · 翻译持久化 · Localizer 与资源

100%

滚轮或按钮缩放 · 放大后拖动画面 · 双击切换 100% / 200%