# Smart Start Notes 按语言拆分资源架构评估 日期:2026-05-20 状态:已评估,当前发布版暂缓执行 相关模块:`SmartStartService`、Smart Start catalog 生成脚本、`build.sh`、CatalogOps 多语言备注 QA ## 1. 背景 当前 Smart Start 运行时目录使用单个资源文件: ```text SmartStartUltimateDefaultCatalog.json ``` 该 JSON 同时承载两类信息: - 语言无关的基础目录信息:`rank`、`name`、`normalizedName`、`bundleIdentifier`、`defaultTag`、`sourceEvidence` - 自然语言备注:`notes` 产品侧提出一个合理问题:用户通常只使用一种界面语言,很少频繁切换语言。如果所有语言 notes 都汇总在同一个 JSON 中,App 会解析大量当前用户用不到的备注内容。后续如果支持按需下载语言包,所有语言混在单文件里也不利于拆包。 ## 2. 原始方案 将 Smart Start 资源拆成两层: ```text 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,系统可能不知道用户当前保存的旧语言默认备注,导致两个问题: - 应该更新语言时无法更新。 - 或者为了更新而降低保护,误覆盖用户手写备注。 这是最高风险点,因为它触及用户数据安全。 相关代码: ```text Apptag/SmartCategorization/SmartStartService.swift - relocalizeDefaultNotesForCurrentLanguage - makeDraft - applyDraft ``` ### 4.2 默认备注候选集变窄 当前 `makeDraft` 会把 `matched.notes?.values` 作为 `defaultNoteCandidates`,用于后续判断是否可以安全替换已有备注。 如果拆分后只加载当前语言 notes,候选集会变窄。首次语言切换、重新应用系统初始方案、恢复默认方案时,系统对“哪些备注是系统生成的默认备注”的判断会变弱。 ### 4.3 构建资源完整性风险 当前 `build.sh` 只复制: ```text SmartStartUltimateDefaultCatalog.json SmartStartUltimateDefaultCatalog.csv SmartStartAppDefaultTags.csv ``` 拆分后必须复制全部 `SmartStartUltimateDefaultCatalog.notes..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` 架构师认为不能只草率使用 `normalizedName` 或 `rank`: - `normalizedName` 可能碰撞。 - `rank` 会随排序变化而不稳定。 - `bundleIdentifier` 对无 bundle ID 的 App 不够完整。 更稳妥的做法是新增稳定 `entryID`,或至少使用 `bundleIdentifier ?? normalizedName` 并在生成脚本里强制校验碰撞为 0。 ### 4.5 catalogVersion 再触发风险 如果将资源格式升级为新版本,可能需要将 `SmartStartService.catalogVersion` 从 2 升到 3。 但当前逻辑中: ```text store.smartStart.catalogVersion < catalogVersion ``` 会触发 Smart Start 重新运行。发布前必须明确这是否是期望行为,否则可能让老用户再次被 Smart Start 打扰。 ## 5. 当前决策 当前市场发布版暂不执行 notes 按语言拆分。 本版继续使用单 JSON 结构,优先完成: - 最新 CSV 吸收。 - 多语言备注从源备注机器翻译生成。 - Translation QA。 - 80 字符上限校验。 - 前导标点、空翻译、缺失翻译、明显机器翻译问题检查。 - 打包与 Git 归档。 ## 6. 后续执行建议 下一版若执行 notes 按语言拆分,应采用双读兼容策略: 1. App 优先读取新格式: ```text base catalog + notes..json ``` 2. 任一关键文件缺失、解析失败、当前语言 notes 为空时,回退旧单文件: ```text SmartStartUltimateDefaultCatalog.json ``` 3. 旧单文件失败时,再回退 legacy CSV。 4. 为语言切换和用户备注保护,至少补充一种安全机制: - 额外加载 fallback 链 notes:当前语言、`en`、`zh-Hans`。 - 或生成轻量 `SmartStartUltimateDefaultCatalog.noteFingerprints.json`,只用于判断当前用户备注是否属于系统默认备注。 - 或在用户数据中记录系统默认备注的来源标识,避免纯文本反查。 5. 新资源格式建议显式版本化,例如: ```json { "version": 3, "notesResourceMode": "split-by-language" } ``` 但是否提升 `SmartStartService.catalogVersion` 必须单独评估,不能自动等同。 ## 7. 必须满足的 QA Gates 执行该拆分前,至少需要通过以下 QA: 1. 构建产物检查 App bundle 内必须存在 base JSON、`notes.zh-Hans.json`、`notes.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。 - 没有源备注时不能编造备注。