Runtime Class Reference

All types live in the module CliffLocalizationSuite, with the export macro CLIFFLOCALIZATIONSUITE_API.

FCliffDocument

class CLIFFLOCALIZATIONSUITE_API FCliffDocument
{
public:
    FCliffHeader Header;
    TArray<FCliffGroup> Groups;
    FString Path;

    FString GetCanonicalId(const FCliffGroup& InGroup, const FCliffEntry& InEntry) const;

    static bool Parse(const FString& InText, const FString& InPath, FCliffDocument& OutDocument, TArray<FCliffValidationIssue>& OutIssues);
    static bool Parse(const FString& InText, FCliffDocument& OutDocument, TArray<FCliffValidationIssue>& OutIssues);
    static bool ParseAndValidate(const FString& InText, const FString& InPath, FCliffDocument& OutDocument, TArray<FCliffValidationIssue>& OutIssues, const bool bCheckWidth = false);
    TArray<FCliffValidationIssue> Validate(const bool bCheckWidth = false) const;
    static bool HasErrors(const TArray<FCliffValidationIssue>& InIssues);
};
MemberDescription
HeaderThe parsed header
GroupsEvery section (file order preserved)
PathThe file path passed in during parsing (used for diagnostics)
GetCanonicalId(Group, Entry)Returns namespace.clan.<group-path>.<entry-id>, skipping empty segments automatically
Parse(Text, Path, …)Syntax parsing only; on failure OutDocument keeps partial state and OutIssues contains the first fatal issue
Parse(Text, …)Overload without a path
ParseAndValidate(Text, Path, …, bCheckWidth)Parse + full validation (equivalent to cliff_format.validate()), never throws; returns true when there is no error-level issue
Validate(bCheckWidth)Validates an already parsed document and returns all issues (including warnings)
HasErrors(Issues)Whether any error-level issue exists (extension / warning do not count)

When bCheckWidth = true it additionally runs the max-width display width check (producing warnings only); the import / export / AI paths do not enable that check.

FCliffHeader

struct CLIFFLOCALIZATIONSUITE_API FCliffHeader
{
    FString Namespace;              // 必需
    FString Clan;                   // 必需
    FString SourceLanguage;         // 必需(BCP 47)
    FString TargetLanguage;         // 必需(BCP 47)
    TOptional<FString> Version;
    FString Variant = TEXT("standard");
    TOptional<FString> Title;
    TOptional<FString> Info;
    TOptional<FString> Standard;
    TArray<FString> Dependency;     // 路径列表
    TArray<FString> Comments;       // 整行 # 注释(按出现顺序)
    TMap<FString, FString> Extensions; // x- 扩展,原样保留
};

FCliffGroup

struct CLIFFLOCALIZATIONSUITE_API FCliffGroup
{
    FString Path;                   // 点号分组路径,如 "video.advanced"
    int32 Line = 0;
    TOptional<FString> Context;
    TOptional<FString> Type;
    TArray<FString> Emotion;
    TOptional<int32> MaxWidth;
    TArray<FCliffEntry> Entries;
    TMap<FString, FString> Extensions;
};

Group metadata only allows the four keys context / type / emotion / max-width, and they must be written before the first entry.

FCliffEntry

struct CLIFFLOCALIZATIONSUITE_API FCliffEntry
{
    FString Id;                     // <id> 标记本身,不是字段
    int32 Line = 0;
    TOptional<FString> Source;      // 必需
    TOptional<FString> Target;
    TOptional<FString> Type;        // 必需(直接或从组继承)
    TArray<FString> Emotion;
    TOptional<FString> Status;      // 必需
    TOptional<FString> Context;
    TOptional<int32> MaxWidth;
    TArray<FString> Reference;
    TOptional<FString> Reviewer;
    TMap<FString, FString> Extensions;
};

Inheritance: type / emotion / context / max-width can be inherited from the group (the entry wins); source / target / status / reference / reviewer are never inherited.

FCliffValidationIssue and ECliffValidationCategory

enum class ECliffValidationCategory : uint8
{
    Syntax,      // 语法错误,无法完成解析
    Semantic,    // 结构合法但违反语义规则
    Vocabulary,  // 固定词表外的取值
    Icu,         // 花括号不平衡
    Id,          // 重复分组路径 / 重复条目 ID / 非法标识符
    Extension,   // x- 扩展字段(严格校验器警告并忽略)
    Warning      // 非致命提示(宽度、initial 带 target 等)
};

struct CLIFFLOCALIZATIONSUITE_API FCliffValidationIssue
{
    int32 Line = 0;
    ECliffValidationCategory Category = ECliffValidationCategory::Syntax;
    FString Message;
    FString Text;

    bool IsError() const;   // Category 既不是 Extension 也不是 Warning
};

FCliffSemanticMapping

struct CLIFFLOCALIZATIONSUITE_API FCliffSemanticMapping
{
    TMap<FString, FString> ClanToNamespace;
    TMap<FString, FString> NamespaceToType;
    TMap<FString, FString> NamespaceToEmotion;

    static FCliffSemanticMapping CreateDefault();

    FString ResolveNamespace(const FString& InClan, bool* OutWasExplicit = nullptr) const;
    FString ResolveClanFromNamespace(const FString& InNamespace, bool* OutWasExplicit = nullptr) const;
    FString ResolveTypeForNamespace(const FString& InNamespace, bool* OutWasExplicit = nullptr) const;
    FString ResolveEmotionForNamespace(const FString& InNamespace, bool* OutWasExplicit = nullptr) const;
};
MethodBehaviour on a miss
ResolveNamespaceReturns the clan passed in unchanged, with OutWasExplicit = false (in that case the importer uses the header namespace instead)
ResolveClanFromNamespaceReturns the kebab-slug of the UE Namespace; when the slug is empty it returns ue
ResolveTypeForNamespaceReturns an empty string
ResolveEmotionForNamespaceReturns an empty string

FCliffSerializer

struct CLIFFLOCALIZATIONSUITE_API FCliffSerializer
{
    static FString Serialize(const FCliffDocument& InDocument);
};

Output canonical form: the first line is CLIFF 1.0, then the header in the order namespaceclansource-languagetarget-language → optional fields, with groups and entries kept in authoring order, one space after the key: value colon, and control characters < 0x20 replaced with a space.

UCliffLocalizationSuiteSettings

UCLASS(Config = Editor, defaultconfig, meta = (DisplayName = "CLIFF Localization Suite"))
class CLIFFLOCALIZATIONSUITE_API UCliffLocalizationSuiteSettings : public UDeveloperSettings
{
public:
    // Identity
    FString CliffNamespace = TEXT("p-cliff-l10n-suite");

    // Semantic Mapping
    TMap<FString, FString> ClanToNamespace;
    TMap<FString, FString> NamespaceToType;
    TMap<FString, FString> NamespaceToEmotion;

    // Workflow
    FString DefaultImportDirectory;
    FString DefaultExportDirectory;

    // AI Translation
    FString DeepSeekApiKey;
    FString DeepSeekBaseUrl = TEXT("https://api.deepseek.com");
    FString DeepSeekModel = TEXT("deepseek-flash");
    ECliffDeepSeekThinkingLevel ThinkingLevel = ECliffDeepSeekThinkingLevel::Off;
    float RequestTimeoutSeconds = 60.0f;
    int32 MaxRetries = 3;
    int32 BatchSize = 20;
    int32 MaxConcurrentCultures = 4;
    FString TranslationStandard;
    FString SystemPrompt;
    bool bAutoRepairCliff = true;
    int32 MaxRepairAttempts = 2;
    bool bIncludeContext = true;
    bool bRetranslateSelfTranslations = true;

    virtual FName GetCategoryName() const override;   // "Plugins"
    virtual FName GetSectionName() const override;    // "CliffLocalizationSuite"

    FCliffSemanticMapping GetSemanticMapping() const;
    FString ResolveApiKey() const;
    static FString GetDefaultSystemPrompt();
    FString BuildSystemPrompt() const;
};

How to obtain it: GetDefault<UCliffLocalizationSuiteSettings>(). Field descriptions are in the settings reference.