数据流
三条链路共享同一个骨架:编辑器入口生成官方 GatherText 配置 → 官方执行器按步骤调度 → 插件命令let 读写 CLIFF → 官方生成器编译二进制。
┌──────────── 编辑器入口 ────────────┐
│ Dashboard 按钮 / 控制台命令 / CI │
└───────────────┬───────────────────┘
▼
生成 GatherText ini(CommonSettings + GatherTextStep{N})
▼
UGatherTextCommandlet 反射 CommandletClass → 执行插件命令let
▼
┌──────────────┬───────────────────────┬──────────────────────┐
│ CliffImport │ CliffExport │ CliffAITranslate │
└──────┬───────┴───────────┬───────────┴──────────┬───────────┘
▼ ▼ ▼
manifest/archive <culture>/<clan>.cliff DeepSeek 写回 .cliff
▼ ▼ ▼
.locmeta/.locres .cliffmap.json 侧车 再走 CliffImport
链路一:导入(CLIFF → UE)
| 步骤 | 执行者 | 细节 |
|---|---|---|
| 1 | 编辑器入口 | 生成 <Target>_CliffImport.ini:CommonSettings + GatherTextStep0(CommandletClass=CliffImport)+ GatherTextStep1(CommandletClass=GenerateTextLocalizationResource) |
| 2 | 官方执行器 | GUI 走官方子进程 + 同款 Slate 窗口;无头走嵌入式 UGatherTextCommandlet::Execute |
| 3 | UCliffImportCommandlet | 读取 SourcePath / ManifestName / ArchiveName / NativeCulture / CulturesToGenerate,以及 CliffFilePath(可多个)或 CliffDirectory + CliffImportCulture |
| 4 | 解析 | 每个 .cliff 文本交给 FCliffDocument::ParseAndValidate;有任何 error 级 issue 即整体失败并逐条打印行号 |
| 5 | 装配 | 为 Target 构造 FLocTextHelper(TargetPath, ManifestName, ArchiveName, NativeCulture, Cultures) 并 LoadAll |
| 6 | 写 manifest | 每条 entry 调 AddSourceText(namespace, key, source, context);上下文带上 cliff.* 元数据 |
| 7 | 写 archive | status: initial 只写 manifest;其余状态写对应 culture 的 archive 译文(空译文与自翻译被拒绝写入) |
| 8 | 保存 | SaveManifest / SaveArchive,依次写各 culture |
| 9 | 编译 | 官方 GenerateTextLocalizationResource 用 FTextLocalizationResourceGenerator 产出 .locmeta 与每个 culture(含 NativeCulture)的 .locres,flags 为 EGenerateLocResFlags::None |
| 10 | 刷新 | FTextLocalizationManager::UpdateFromLocalizationResource 热刷新;任务结束后广播 LocalizationDelegates::OnLocalizationTargetDataUpdated |
导入时的两个关键约定:
- 先原生语言后外语:任务内的文件按「原生语言优先」排序,外语归档条目的「生效源」(官方
NativeText语义)也随之同步,因此导入顺序不影响最终一致性。 - 元数据就地挂接:同一身份(Namespace + Key + Source)不再新增重复 manifest context,而是把
cliff.*挂到已存在的 context 上;否则官方GenerateLocRes会因同一 key 的多个 context 解析出冲突译文。
链路二:导出(UE → CLIFF)
| 步骤 | 执行者 | 细节 |
|---|---|---|
| 1 | 编辑器入口 | 生成 <Target>_CliffExport.ini:CommonSettings + GatherTextStep0(CommandletClass=CliffExport,带 DestinationPath 与 CliffNamespace) |
| 2 | UCliffExportCommandlet | 读取配置并调用 FCliffExporter::ExportTarget |
| 3 | 读数据 | 主路径:FLocTextHelper::LoadAll 读 manifest + 全部 culture archive;辅助路径:ExportFromLocResFile 直接读编译后的 .locres(此时源文本靠 manifest 补齐,配不上则逐条告警) |
| 4 | 分 clan | 每个 clan 生成一个 FCliffDocument。clan 的确定顺序:cliff.clan 元数据 → Namespace 反向映射表 → kebab-slug 回退(并记 warning) |
| 5 | 造 ID | 分组路径与条目 ID 由 SourceLocation / Key 切片生成稳定 kebab-case;冲突时追加 `CRC32(Namespace |
| 6 | 回填语义 | type / emotion / status / context / reference 优先取 cliff.* 元数据,其次按映射表推导;完全无信息 → sentence + warning |
| 7 | 自校验 | 序列化结果先过一遍 ParseAndValidate,有 error 级 issue 则导出失败 |
| 8 | 落盘 | 写 <OutputDirectory>/<Culture>/<Clan>.cliff(UTF-8 无 BOM、LF) |
| 9 | 侧车 | 写 <OutputDirectory>/<Manifest 基础名>.cliffmap.json,记录 canonical id ↔ (namespace, key) |
链路三:AI 翻译
Saved/CliffAITranslate/<Target>/ ← 每次执行前清空重建
① CliffExport → <Culture>/<Clan>.cliff(只含 source 与已有 target)
② CliffAITranslate → 逐语言扫描 .cliff、只翻译缺 target 的条目、写回同目录
③ CliffImport → manifest / archive + GenerateTextLocalizationResource
④ Generate Reports→ 官方字数报告
- 每个目标语言一条独立对话,多语言并发执行(上限
Max Concurrent Cultures)。 - AI 请求全部发生在命令let(Editor 子进程)内,用异步 HTTP + 统一事件循环驱动,不阻塞主编辑器 UI。
- 管线结束后,翻译好的
.cliff会被拷回Content/Localization/<Target>/,成为可评审、可提交的中间产物。
官方数据模型速查
| 官方结构 | 关键字段 | 插件如何对应 |
|---|---|---|
FManifestEntry | Namespace + Source + Contexts[] | CLIFF 的 clan → Namespace,source → Source |
FManifestContext | Key / SourceLocation / DevNotes / KeyMetadataObj | cliff.* 元数据挂在这里 |
FArchiveEntry | Namespace / Key / Source / Translation | target 写 Translation;外语条目 Source 记录「生效源」 |
FTextLocalizationResource | LocalizedString / SourceStringHash / Priority | 由官方生成器产出,插件只读回验证 |
FTextLocalizationMetaDataResource | NativeCulture / CompiledCultures | 决定导出时的 native 文化发现 |
NOTE
.locres不保存源文本与元数据(只有SourceStringHash与译文),所以「只用二进制导出」必然丢语义。这也是导出侧车主路径存在的原因,详细规则见 导出管线。