edit | blame | history | raw

Smart Start Notes 按语言拆分资源架构评估

日期:2026-05-20
状态:已评估,当前发布版暂缓执行
相关模块:SmartStartService、Smart Start catalog 生成脚本、build.sh、CatalogOps 多语言备注 QA

1. 背景

当前 Smart Start 运行时目录使用单个资源文件:

SmartStartUltimateDefaultCatalog.json

该 JSON 同时承载两类信息:

  • 语言无关的基础目录信息:ranknamenormalizedNamebundleIdentifierdefaultTagsourceEvidence
  • 自然语言备注:notes

产品侧提出一个合理问题:用户通常只使用一种界面语言,很少频繁切换语言。如果所有语言 notes 都汇总在同一个 JSON 中,App 会解析大量当前用户用不到的备注内容。后续如果支持按需下载语言包,所有语言混在单文件里也不利于拆包。

2. 原始方案

将 Smart Start 资源拆成两层:

SmartStartUltimateDefaultCatalog.json
SmartStartUltimateDefaultCatalog.notes.zh-Hans.json
SmartStartUltimateDefaultCatalog.notes.en.json
SmartStartUltimateDefaultCatalog.notes.ja.json
...

其中:

  • 基础 catalog JSON 只保留语言无关字段。
  • 每种语言一个 notes 文件。
  • App 启动或 Smart Start 运行时,先加载基础 catalog,再按当前语言只加载一个 notes 文件。
  • fallback 顺序为:当前语言 -> en -> zh-Hans
  • 用户切换语言时,再懒加载对应语言 notes 文件。

该方案的长期优点:

  • 运行时只加载当前语言 notes,内存和解析成本更低。
  • 未来可以将 notes 语言包从 bundle 资源迁移到远端下载资源。
  • 用户可只保留需要的语言资源,降低安装包体积。
  • 基础目录和自然语言内容分离,资源职责更清楚。

3. 架构师评估结论

架构师结论:**不建议在临近市场发布前执行这次拆分**,除非它是为解决明确的包体或启动性能阻塞。

主要理由:

  • 当前 SmartStart_UltimateDefaultCatalog.json 约 1.7 MB,体积并不大。
  • 当前实际 notes 主要还是 zh-Hans,尚未真正塞入 29 种语言 notes。
  • 因此现在拆分带来的性能和包体收益有限,但会引入资源格式、加载、fallback、语言切换和数据安全相关的结构性风险。

结论不是否定方向,而是建议把它作为下一版资源格式升级处理,不进入当前市场发布版。

4. 关键风险

4.1 语言切换时默认备注识别风险

当前 SmartStartService.relocalizeDefaultNotesForCurrentLanguage 通过 matched.notes.values 判断用户当前备注是否属于系统默认备注。只有确认当前备注仍是系统默认备注时,才会切换成新语言备注。

如果运行时只加载当前语言 notes,系统可能不知道用户当前保存的旧语言默认备注,导致两个问题:

  • 应该更新语言时无法更新。
  • 或者为了更新而降低保护,误覆盖用户手写备注。

这是最高风险点,因为它触及用户数据安全。

相关代码:

Apptag/SmartCategorization/SmartStartService.swift
- relocalizeDefaultNotesForCurrentLanguage
- makeDraft
- applyDraft

4.2 默认备注候选集变窄

当前 makeDraft 会把 matched.notes?.values 作为 defaultNoteCandidates,用于后续判断是否可以安全替换已有备注。

如果拆分后只加载当前语言 notes,候选集会变窄。首次语言切换、重新应用系统初始方案、恢复默认方案时,系统对“哪些备注是系统生成的默认备注”的判断会变弱。

4.3 构建资源完整性风险

当前 build.sh 只复制:

SmartStartUltimateDefaultCatalog.json
SmartStartUltimateDefaultCatalog.csv
SmartStartAppDefaultTags.csv

拆分后必须复制全部 SmartStartUltimateDefaultCatalog.notes.<lang>.json 文件,并且应对关键 fallback 文件做构建门禁:

  • notes.zh-Hans.json 必须存在。
  • notes.en.json 必须存在。
  • 当前声明的 supportedLanguages 若生成 notes,则对应文件缺失应阻断构建。

4.4 notes join key 风险

拆分 notes 后,notes 文件必须通过稳定 key 与基础 catalog entry 关联。

候选方案:

  • normalizedName
  • bundleIdentifier
  • rank
  • 新增 entryID

架构师认为不能只草率使用 normalizedNamerank

  • normalizedName 可能碰撞。
  • rank 会随排序变化而不稳定。
  • bundleIdentifier 对无 bundle ID 的 App 不够完整。

更稳妥的做法是新增稳定 entryID,或至少使用 bundleIdentifier ?? normalizedName 并在生成脚本里强制校验碰撞为 0。

4.5 catalogVersion 再触发风险

如果将资源格式升级为新版本,可能需要将 SmartStartService.catalogVersion 从 2 升到 3。

但当前逻辑中:

store.smartStart.catalogVersion < catalogVersion

会触发 Smart Start 重新运行。发布前必须明确这是否是期望行为,否则可能让老用户再次被 Smart Start 打扰。

5. 当前决策

当前市场发布版暂不执行 notes 按语言拆分。

本版继续使用单 JSON 结构,优先完成:

  • 最新 CSV 吸收。
  • 多语言备注从源备注机器翻译生成。
  • Translation QA。
  • 80 字符上限校验。
  • 前导标点、空翻译、缺失翻译、明显机器翻译问题检查。
  • 打包与 Git 归档。

6. 后续执行建议

下一版若执行 notes 按语言拆分,应采用双读兼容策略:

  1. App 优先读取新格式:
base catalog + notes.<current>.json
  1. 任一关键文件缺失、解析失败、当前语言 notes 为空时,回退旧单文件:
SmartStartUltimateDefaultCatalog.json
  1. 旧单文件失败时,再回退 legacy CSV。

  2. 为语言切换和用户备注保护,至少补充一种安全机制:

  • 额外加载 fallback 链 notes:当前语言、enzh-Hans
  • 或生成轻量 SmartStartUltimateDefaultCatalog.noteFingerprints.json,只用于判断当前用户备注是否属于系统默认备注。
  • 或在用户数据中记录系统默认备注的来源标识,避免纯文本反查。
  1. 新资源格式建议显式版本化,例如:
{
  "version": 3,
  "notesResourceMode": "split-by-language"
}

但是否提升 SmartStartService.catalogVersion 必须单独评估,不能自动等同。

7. 必须满足的 QA Gates

执行该拆分前,至少需要通过以下 QA:

  1. 构建产物检查
    App bundle 内必须存在 base JSON、notes.zh-Hans.jsonnotes.en.json。声明支持的 notes 文件缺失时构建失败。

  2. 解析兼容
    新格式可加载;缺 notes 文件可回退旧格式;损坏 notes 文件不会导致 Smart Start 空跑或崩溃。

  3. 首次启动一致性
    中英文系统语言下,Smart Start 匹配数量、标签数量与旧格式一致。

  4. 语言切换安全
    先用 zh-Hans 写入默认 note,再切 en,再切回 zh-Hans,系统默认 note 能更新;用户手写 note 不被覆盖。

  5. fallback 链
    当前语言无 note 时按 当前语言 -> en -> zh-Hans 命中;三者都无时不写 note。

  6. join 数据质量
    base entries 数、可匹配 bundle 数、有效 tag 数与旧 JSON 一致;notes join miss 为 0;join collision 为 0。

  7. 多语言备注质量
    所有 notes 不超过 80 个 Unicode 字符;不以标点开头;不重复 App 名称;不是标签名或分类名拼接;缺失、空翻译、超限翻译进入 Translation QA 并阻断发布。

  8. 性能
    冷启动和首次 makeDraft 不慢于旧版;语言切换懒加载不阻塞主线程。

8. 建议的后续实施顺序

建议拆成三个小版本或三个独立 PR:

  1. 生成器准备
    新增 base JSON + per-language notes JSON 输出,同时保留旧单 JSON 输出;生成 join QA 和 Translation QA。

  2. 运行时双读
    App 支持新旧两种格式,默认仍读取旧格式;验证兼容性和用户备注保护。

  3. 默认切换
    QA 通过后,将默认读取切到新格式,并保留旧格式 fallback 至少一个版本。

9. 与 CatalogOps 规则的关系

本评估不改变多语言备注质量规则。

无论 notes 存在单文件还是按语言拆分,都必须遵守:

  • 多语言备注从已审核源备注翻译而来。
  • 禁止用标签名、分类名、本地化 tag 名称拼备注。
  • 机器翻译必须经过 Translation QA。
  • 超过 80 字符不能硬截断后发布。
  • 存在源备注时,应覆盖全部 supportedLanguages。
  • 没有源备注时不能编造备注。