导出管线

导出把官方本地化数据(manifest + archive,或编译后的 .locres)转成按 culture 输出的 .cliff 文件,并额外写出原键侧车。

配置项

必需说明
SourcePathTarget 的本地化数据目录
ManifestName / ArchiveNameGame.manifest / Game.archive
NativeCulture原生语言
CulturesToGenerate要导出的 culture(可重复出现)
DestinationPath输出目录,布局为 <DestinationPath>/<Culture>/<Clan>.cliff
CliffNamespaceCLIFF header 的 namespace(来自 Project Settings 的 CLIFF Namespace
SourceLanguage覆盖 header 的 source-language(默认取 NativeCulture)

数据来源

路径使用场景能力
FLocTextHelper::LoadAll(manifest + 全部 culture archive)主路径完整:源文本、译文、cliff.* 元数据、SourceLocation 全都在
ExportFromLocResFile(直接读 .locres只有二进制产物时降级:源文本靠同目录 manifest 补齐;补不上的条目逐条告警并落到 sentence

WARNING

.locres 只有 SourceStringHash 与译文,没有源文本、没有元数据。纯二进制导出必然丢失 type / emotion / status 细分与原始 Key,只建议用于一致性验证(“二进制能不能读回来”),不要作为日常导出路径。

clan 与文件划分

文件级 clan(写入 header 与文件名):取 ManifestName 的基础名并 kebab 化,即 一个 culture 一个 .cliff 文件

ManifestNameheader clan文件名
Game.manifestcliff-gamecliff-game.cliff
CliffLocalizationSuiteRuntime.manifestcliff-localization-suite-runtimecliff-localization-suite-runtime.cliff

kebab 化后若不是合法 name(须以字母开头、只含 [a-z0-9-]),回退为 cliff

条目级 clan:每条目还会单独解析一个语义 clan,写入侧车的 clan 字段(供审计与还原使用):

  1. cliff.clan 元数据(archive 条目的 KeyMetadataObj)—— 最可靠,来自上一次导入
  2. Namespace 反向映射表Clan to Namespace 的逆查)
  3. kebab-slug 回退:把 UE Namespace 转成小写 kebab-case(UObjectDisplayNamesu-object-display-names),并记录告警:UE namespace '…' has no semantic clan mapping; exported as clan '…'.

TIP

第 3 条只应作为兜底。若导出结果里出现一堆 slug 化的 clan 名,说明映射表缺条目——补上 Clan to Namespace 即可,见 语义映射

NOTE

文件名只有 <Manifest 基础名>.cliff 一个,而不是每个 clan 一个文件:CLIFF 的文件单位是 (namespace, clan, target-language),插件把整个 Target 的一个 culture 视为一个 clan 文件,条目之间按分组路径区分语义层级。

分组路径与条目 ID

CLIFF 的 name 只允许小写 kebab-case([a-z][a-z0-9-]*),而 UE 的 Key 常常是源文本本身(含中文、|、大写、/)。导出按以下优先级生成:

  1. 元数据cliff.group / cliff.entry 合法时直接沿用(保证往返稳定)
  2. SourceLocation 推导:取代码路径的最后一段(/Script/MyGame.GlyphAnimationLayer:AddShadowColorAtglyph-animation-layer),截断 [ 之后的元数据描述,剔除 .cpp/.h/.uasset 等扩展名与 _C 结尾
  3. Key 分段:把 Key 中的 |/ 视作层级分隔,逐段 kebab 化,最后一段作为条目 ID,其余拼成分组路径
  4. 稳定哈希 ID:Key 是 32 位十六进制 GUID 时用 key-<小写 hex>;无法从 Key 推导时用 entry-<CRC32(Namespace|Key) 的 8 位十六进制>

同一 culture 内按 Namespace|Key 去重,因此一个 identity 只会出现一次。原键不丢失——它被写入侧车,导入时原样还原。示例:

UE 输入:Namespace = "UObjectCategory"
        Key       = "MyGame|层|字形微调|阴影染色"
        SourceLocation = "/Script/MyGame.GlyphAnimationLayer:AddShadowColorAt [Function Category meta-data]"

CLIFF 输出(header: namespace: my-game, clan: category):
[glyph-animation-layer]
<add-shadow-color-at>
source: "MyGame|层|字形微调|阴影染色"
type: label
status: translated
reference: ["/Script/MyGame.GlyphAnimationLayer:AddShadowColorAt"]

侧车:my-game.category.glyph-animation-layer.add-shadow-color-at
      ↔ (UObjectCategory, MyGame|层|字形微调|阴影染色)

语义字段回填

CLIFF 字段来源优先级
typecliff.type 元数据 ② Namespace to Type 映射表 ③ 无 → sentence + warning
emotioncliff.emotion 元数据 ② Namespace to Emotion 映射表 ③ 无 → 按 type 的默认值(dialogue/monologue/idiom[neutral],其余 → [objective]
statuscliff.status 元数据 ② 有译文 → translated;无译文 → initial
contextcliff.context 元数据 ② SourceLocation + SiteDescription + DevNotes 拼接
referenceSourceLocation(文件:行 或 资产路径)
max-width仅当元数据中存在时导出(UE 没有对应来源)
reviewer仅当元数据中存在时导出

NOTE

官方审核模型只有「有译文且源文本匹配 = 已审」的二元语义,因此导出的 status 在元数据缺失时只能落到 translated / initialreviewed / final 的细分完全依赖上一次导入写入的 cliff.status 元数据与侧车。

文本保真

  • source / target 原样写出,ICU 花括号、占位符、HTML / 富文本标记都不做改写
  • 字符串按 CLIFF 转义表处理(\n\t\"\\),其余字符原样保留
  • 输出为 UTF-8 无 BOMLF 换行,字段采用规范形式(key: value,冒号后一个空格)

输出布局与侧车

<DestinationPath>/
├── Game.cliffmap.json              # 原键侧车(本次导出的全部条目)
├── zh-Hans/
│   └── cliff-game.cliff             # 一个 culture 一个文件
└── ja/
    └── cliff-game.cliff

侧车结构:

{
  "version": 1,
  "namespace": "my-game",
  "entries": [
    {
      "id": "my-game.category.glyph-animation-layer.add-shadow-color-at",
      "namespace": "UObjectCategory",
      "key": "MyGame|层|字形微调|阴影染色",
      "clan": "category",
      "culture": "zh-Hans"
    }
  ]
}

字段说明与还原规则见 原键侧车

自校验与失败

  1. 序列化后的每个文档先过 FCliffDocument::ParseAndValidate
  2. 有 error 级 issue → 导出失败,列出 issue(含行号)
  3. 命令let 汇总输出:CLIFF export succeeded: <documents> documents, <entries> entries -> '<DestinationPath>'.

TIP

导出是评审入口:.cliff 是纯文本、diff 友好的稳定文件名,建议把导出目录纳入版本控制(连同侧车),让翻译与评审发生在 Git 里,而不是在二进制资产里。