架构总览
插件的核心设计原则只有一条:CLIFF 是官方本地化链路的一个步骤,而不是它的替代品。因此它不修改引擎源码,不自己写 .locres 二进制,也不自建 Dashboard 窗口,而是把自己挂到官方已有的扩展点上。
设计目标与约束
| 目标 | 落地方式 |
|---|---|
| 不修改引擎 | 只使用 Core / Developer / Editor 的公开类;引擎源码零 diff |
| 不旁路官方数据 | 写入走官方 FLocTextHelper 与 FTextLocalizationResourceGenerator |
| 融入官方工作流 | 注册官方 LocalizationService Provider,按钮注入官方 Dashboard 工具栏 |
| 可无头运行 | 导入/导出/AI 翻译都是官方 UGatherTextCommandletBase 子类,可由 -run=GatherText 驱动 |
| 不引入运行时依赖 | Runtime 模块零编辑器依赖、零 Python、零 HTTP |
模块划分
Plugins/CliffLocalizationSuite/
├── CliffLocalizationSuite.uplugin
├── Content/Localization/ # 插件自带译文(7 Target × 15 语言)
└── Source/
├── CliffLocalizationSuite/ # Runtime:数据模型与解析
│ ├── Public/Cliff/CliffDocument.h
│ ├── Public/Cliff/CliffSemanticMapping.h
│ ├── Public/Cliff/CliffSerializer.h
│ ├── Public/Settings/CliffLocalizationSuiteSettings.h
│ └── Private/… # 解析器、校验器、设置实现、单测
└── CliffLocalizationSuiteEditor/ # Editor:管线集成
├── Public/Cliff/CliffImporter.h
├── Public/Cliff/CliffExporter.h
├── Public/AI/CliffAITranslator.h
├── Public/Commandlets/CliffGatherCommandlets.h
└── Private/… # 工具箱、Provider、进度窗口、AI 客户端、单测
| 模块 | 类型 | LoadingPhase | 职责 |
|---|---|---|---|
CliffLocalizationSuite | Runtime | Default | CLIFF 数据模型、解析、校验、序列化、语义映射、UDeveloperSettings |
CliffLocalizationSuiteEditor | Editor | Default | 导入器、导出器、AI 翻译、Dashboard 集成、GatherText 命令let、控制台命令 |
依赖
// Runtime:纯数据与算法,可被 Game 目标引用
PublicDependencyModuleNames: Core, CoreUObject, Engine, DeveloperSettings
// Editor:官方管线与编辑器 UI
PublicDependencyModuleNames: CliffLocalizationSuite, Core, CoreUObject, Engine,
LocalizationService, Slate, SlateCore
PrivateDependencyModuleNames: ApplicationCore, DesktopPlatform, EngineSettings, HTTP, InputCore,
Json, Localization, LocalizationCommandletExecution, UnrealEd
NOTE
Runtime 模块不依赖
UnrealEd、AssetTools、Slate,所以 Game / Shipping 目标可以正常打包;Editor 模块Type: Editor,不会进入运行时。
官方链路的接入点
UE 5.8 的官方本地化链路是:Editor 操作 → 生成 ini 配置 → GatherText 子进程按步骤执行 → manifest / archive → FTextLocalizationResourceGenerator 生成 .locmeta / .locres → FTextLocalizationManager 查表。
官方没有第三方文件格式的注册接口(PO / CSV 都是 Dashboard 生成的 ini 里硬编码的 commandlet 行为),因此插件选择以下四个官方扩展点:
| # | 接入点 | 官方类型 | 插件用法 |
|---|---|---|---|
| 1 | GatherText 步骤动态发现 | ini 的 GatherTextStep{N} + CommandletClass,由 UGatherTextCommandlet 反射 NewObject | CliffImport / CliffExport / CliffAITranslate 成为管线的普通步骤 |
| 2 | Localization Service Provider | ILocalizationServiceProvider modular feature(注册名 "LocalizationService") | 把 CLIFF 按钮注入官方 Dashboard 的 Target / TargetSet 工具栏 |
| 3 | 官方编译生成器 | FTextLocalizationResourceGenerator::GenerateLocMeta/GenerateLocRes | 导入后编译 .locmeta / .locres,不自己写二进制 |
| 4 | 官方刷新委托 | FTextLocalizationManager::UpdateFromLocalizationResource、LocalizationDelegates::OnLocalizationTargetDataUpdated | 导入后立即热刷新并刷新 Dashboard 缓存 |
关键类一览
| 类 / 结构 | 模块 | 说明 |
|---|---|---|
FCliffDocument | Runtime | 解析入口:Parse / ParseAndValidate / Validate / GetCanonicalId |
FCliffHeader / FCliffGroup / FCliffEntry | Runtime | CLIFF 数据模型 |
FCliffValidationIssue / ECliffValidationCategory | Runtime | 7 类 issue;IsError() 把 extension 与 warning 排除在失败之外 |
FCliffSemanticMapping | Runtime | clan ↔ UE Namespace、Namespace → type / emotion 规则表 |
FCliffSerializer | Runtime | 规范化 CLIFF 1.0 序列化(UTF-8 无 BOM、LF) |
UCliffLocalizationSuiteSettings | Runtime | Project Settings(Identity / Semantic Mapping / Workflow / AI Translation) |
FCliffImporter | Editor | .cliff → manifest / archive → 编译 → 热刷新 |
FCliffExporter | Editor | manifest + archive(或 .locres)→ 分组 .cliff + 原键侧车 |
FCliffEditorToolbox | Editor | Dashboard 按钮、文件对话框、GatherText 配置生成、任务装配 |
FCliffLocalizationServiceProvider | Editor | 官方 Provider 实现(工具栏注入) |
FCliffAITranslator | Editor | DeepSeek 翻译编排、自翻译判定、连接测试 |
UCliffImportCommandlet / UCliffExportCommandlet / UCliffAITranslateCommandlet | Editor | 三个官方 GatherText 步骤 |
FCliffCommandletExecutorUI | Editor | 复刻官方样式的 Slate 进度窗口 |
设计取舍
为什么用「自定义 Commandlet + Provider 注入」而不是自建窗口
官方 Dashboard 不提供新增格式按钮的接口,但提供 ILocalizationServiceProvider 工具栏扩展点与 GatherText 步骤发现机制。走这两条路的收益是:日志与官方完全一致(LogGatherTextCommandlet 分段输出)、子进程行为一致、CI 可以直接复用同一套 ini 配置。代价是按钮行为受官方 Dashboard 的 Target / TargetSet 上下文约束。
为什么必须导出侧车
.locres 只有 SourceStringHash,没有源文本也没有元数据;而 UE 的 Key 常常就是源语言文本本身(可含中文、|、大写、/),无法直接作为 CLIFF 的 name。因此导出时生成稳定 ID 并把「原 UE Namespace / Key ↔ canonical ID」写进 .cliffmap.json,导入时还原,才能做到无损往返。
为什么状态要降级
UE 官方审核模型只有「有译文且源文本匹配 = 已审」的二元语义,没有 reviewed / final 的细分,也没有 emotion。插件因此把 CLIFF 的四态与 emotion 写入 archive 的 KeyMetadataObj(cliff.status / cliff.emotion)并同时保留在侧车中;当这些信息丢失时,状态按官方语义降级(无译文 → initial,有译文 → 视为已审)。
为什么 ICU 原样透传
UE 的 FText::Format 只认识 plural / ordinal / gender / hpp 等修饰符,并不实现 ICU MessageFormat 的运行时求值。插件因此把 ICU 字符串原样存取,并在编译 .locres 时使用 EGenerateLocResFlags::None 关闭格式校验;CLIFF 侧的平衡校验仍在解析阶段执行。
插件自带的本地化数据
插件自身 UI 文本随包分发,.uplugin 声明 7 个 Localization Target:
| Target | LoadingPolicy |
|---|---|
CliffLocalizationSuiteRuntime | Always |
CliffLocalizationSuiteEditor | Editor |
CliffLocalizationSuiteEditorTutorials | Never |
CliffLocalizationSuitePropertyNames | PropertyNames |
CliffLocalizationSuiteToolTips | ToolTips |
CliffLocalizationSuiteKeywords | Editor |
CliffLocalizationSuiteCategory | Editor |
每个 Target 均为 native en + 15 种语言(zh-Hans、en、ja、ko、es、pt、ar、pl、de、ru、fr、pt-BR、tr、es-419、it),数据位于 Content/Localization/。正因如此,插件的 AI 翻译管线必须把 NativeCulture 也纳入编译——否则编辑器界面使用的原生语言 .locres 永远不会重建。
下一步
- 数据流 —— 三条链路的逐步细节
- 导入管线 与 导出管线
- API:Runtime 类参考