Runtime 模块概述

CliffLocalizationSuite(Type: Runtime,LoadingPhase: Default)是纯数据与算法模块:CLIFF 1.0 的解析、校验、序列化、语义映射与插件设置都住在这里。它不依赖 UnrealEd / AssetTools / Slate,可以在 Game 目标、命令行工具甚至单元测试中直接使用。

依赖

PublicDependencyModuleNames: Core, CoreUObject, Engine, DeveloperSettings
PrivateDependencyModuleNames: (空)

提供什么

类型文件职责
FCliffDocumentPublic/Cliff/CliffDocument.h解析入口与文档根对象
FCliffHeader / FCliffGroup / FCliffEntry同上CLIFF 数据模型(header / 分组 / 条目)
FCliffValidationIssue / ECliffValidationCategory同上校验问题与 7 类分类
FCliffSemanticMappingPublic/Cliff/CliffSemanticMapping.hclan ↔ UE Namespace、type / emotion 规则
FCliffSerializerPublic/Cliff/CliffSerializer.h规范化 CLIFF 1.0 序列化
UCliffLocalizationSuiteSettingsPublic/Settings/CliffLocalizationSuiteSettings.hProject Settings 与生效映射
ECliffDeepSeekThinkingLevel同上思考强度枚举(配置用)

典型用法

解析并校验

#include "Cliff/CliffDocument.h"

const FString Text = FFileHelper::LoadFileToString(CliffFilePath);
FCliffDocument Document;
TArray<FCliffValidationIssue> Issues;

// 传路径可额外做「文件名/目录 ↔ header」布局一致性检查
const bool bOk = FCliffDocument::ParseAndValidate(Text, CliffFilePath, Document, Issues, /*bCheckWidth=*/false);
if (!bOk)
{
    for (const FCliffValidationIssue& Issue : Issues)
    {
        if (Issue.IsError())
        {
            UE_LOG(LogTemp, Error, TEXT("line %d [%d] %s"), Issue.Line, static_cast<int32>(Issue.Category), *Issue.Message);
        }
    }
    return;
}

遍历条目并生成规范 ID

for (const FCliffGroup& Group : Document.Groups)
{
    for (const FCliffEntry& Entry : Group.Entries)
    {
        const FString CanonicalId = Document.GetCanonicalId(Group, Entry); // namespace.clan.group.entry
        const FString Source = Entry.Source.Get(TEXT(""));
        const FString Type = Entry.Type.IsSet() ? Entry.Type.GetValue() : Group.Type.GetValue();
        const FString Status = Entry.Status.Get(TEXT("initial"));
        // …
    }
}

语义映射

#include "Settings/CliffLocalizationSuiteSettings.h"

const UCliffLocalizationSuiteSettings* Settings = GetDefault<UCliffLocalizationSuiteSettings>();
const FCliffSemanticMapping Mapping = Settings->GetSemanticMapping();

bool bExplicit = false;
const FString UENamespace = Mapping.ResolveNamespace(TEXT("category"), &bExplicit); // UObjectCategory
const FString Clan        = Mapping.ResolveClanFromNamespace(TEXT("UObjectToolTips"), &bExplicit); // tooltip
const FString Type        = Mapping.ResolveTypeForNamespace(TEXT("Dialogue"));  // dialogue
const FString Emotion     = Mapping.ResolveEmotionForNamespace(TEXT("Dialogue"));// neutral

序列化

#include "Cliff/CliffSerializer.h"

const FString CanonicalText = FCliffSerializer::Serialize(Document); // UTF-8 无 BOM、LF 由调用方写盘时决定
FFileHelper::SaveStringToFile(CanonicalText, *OutPath, FFileHelper::EEncodingOptions::ForceUTF8WithoutBOM);

从零构造文档并导出

FCliffDocument Document;
Document.Header.Namespace       = TEXT("my-game");
Document.Header.Clan            = TEXT("ui");
Document.Header.SourceLanguage  = TEXT("en-US");
Document.Header.TargetLanguage  = TEXT("zh-Hans");

FCliffGroup& Group = Document.Groups.AddDefaulted_GetRef();
Group.Path = TEXT("options");

FCliffEntry& Entry = Group.Entries.AddDefaulted_GetRef();
Entry.Id     = TEXT("resolution");
Entry.Source = TEXT("Resolution");
Entry.Target = TEXT("分辨率");
Entry.Type   = TEXT("noun");
Entry.Status = TEXT("final");

NOTE

模型层不做词表校验——构造出非法字段后调用 Document.Validate() 才会报出 vocabulary 类问题。写盘前建议总是跑一次 ParseAndValidate

注意事项

事项说明
线程所有类型都是纯值类型/无状态工具,可在任意线程构造与解析;但 UCliffLocalizationSuiteSettings::Get() 属于 UObject,只能在游戏线程访问
大文件解析是整个文本一次性处理(无流式 API);超大文件请自行分片
注释保留解析器在数据模型中保留注释(与 CLIFF 规范一致),只有导出器主动写入的文件头注释会在序列化时带出
编码输入允许 BOM(会被剥离)、CRLF(视为 LF);裸 CR 视为语法错误;序列化输出应使用 ForceUTF8WithoutBOM
x- 扩展保留在 Extensions 映射中,校验时归为 extension 类,不计失败

下一步