Runtime Module Overview

CliffLocalizationSuite (Type: Runtime, LoadingPhase: Default) is a pure data and algorithms module: CLIFF 1.0 parsing, validation, serialization, semantic mapping and the plugin settings all live here. It does not depend on UnrealEd / AssetTools / Slate, so it can be used directly in Game targets, command line tools and even unit tests.

Dependencies

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

What it provides

TypeFileResponsibility
FCliffDocumentPublic/Cliff/CliffDocument.hParse entry point and document root object
FCliffHeader / FCliffGroup / FCliffEntrySame as aboveThe CLIFF data model (header / group / entry)
FCliffValidationIssue / ECliffValidationCategorySame as aboveValidation issues and their 7 categories
FCliffSemanticMappingPublic/Cliff/CliffSemanticMapping.hclan ↔ UE Namespace, type / emotion rules
FCliffSerializerPublic/Cliff/CliffSerializer.hCanonical CLIFF 1.0 serialization
UCliffLocalizationSuiteSettingsPublic/Settings/CliffLocalizationSuiteSettings.hProject Settings and the effective mapping
ECliffDeepSeekThinkingLevelSame as aboveThinking level enum (for configuration)

Typical usage

Parse and validate

#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;
}

Iterate entries and generate canonical IDs

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"));
        // …
    }
}

Semantic mapping

#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

Serialization

#include "Cliff/CliffSerializer.h"

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

Build a document from scratch and export it

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

The model layer performs no vocabulary validation — illegal fields constructed this way are only reported as vocabulary issues once you call Document.Validate(). Running ParseAndValidate before writing to disk is always recommended.

Notes

ItemDescription
ThreadingAll types are pure value types / stateless utilities and can be constructed and parsed on any thread; but UCliffLocalizationSuiteSettings::Get() is a UObject and can only be accessed on the game thread
Large filesParsing processes the whole text in one go (no streaming API); shard very large files yourself
Comment retentionThe parser does not keep comments in the data model (consistent with the CLIFF specification); only the file header comment the exporter deliberately writes is carried through serialization
EncodingInput may contain a BOM (stripped) and CRLF (treated as LF); a bare CR is a syntax error; serialized output should use ForceUTF8WithoutBOM
x- extensionsKept in the Extensions map, classified as extension during validation and not counted as a failure

Next