Data Flow

All three chains share one skeleton: an editor entry point generates an official GatherText configuration → the official executor schedules the steps → plugin commandlets read and write CLIFF → official generators compile the binaries.

              ┌──────────── editor entry points ───────────┐
              │ Dashboard buttons / console / CI           │
              └───────────────┬────────────────────────────┘

              GatherText ini (CommonSettings + GatherTextStep{N})

        UGatherTextCommandlet reflects CommandletClass → runs the plugin commandlet

   ┌──────────────┬───────────────────────┬──────────────────────┐
   │ CliffImport   │ CliffExport            │ CliffAITranslate      │
   └──────┬───────┴───────────┬───────────┴──────────┬───────────┘
          ▼                   ▼                      ▼
   manifest/archive      <culture>/<clan>.cliff   DeepSeek writes .cliff back
          ▼                   ▼                      ▼
   .locmeta/.locres      .cliffmap.json sidecar  then runs CliffImport again

Chain 1: import (CLIFF → UE)

StepActorDetail
1Editor entry pointWrites <Target>_CliffImport.ini: CommonSettings + GatherTextStep0 (CommandletClass=CliffImport) + GatherTextStep1 (CommandletClass=GenerateTextLocalizationResource)
2Official executorThe GUI drives the official subprocess behind the official-style Slate window; headless runs go through the embedded UGatherTextCommandlet::Execute
3UCliffImportCommandletReads SourcePath / ManifestName / ArchiveName / NativeCulture / CulturesToGenerate plus CliffFilePath (repeatable) or CliffDirectory + CliffImportCulture
4ParsingEvery .cliff goes through FCliffDocument::ParseAndValidate; any error-level issue fails the run and prints the offending line numbers
5AssemblyBuilds FLocTextHelper(TargetPath, ManifestName, ArchiveName, NativeCulture, Cultures) and calls LoadAll
6Manifest writeEach entry calls AddSourceText(namespace, key, source, context) with cliff.* metadata attached to the context
7Archive writestatus: initial writes the manifest only; every other status writes the translation into that culture’s archive (empty targets and self-translations are rejected)
8SaveSaveManifest / SaveArchive for each culture
9CompileThe official GenerateTextLocalizationResource step produces .locmeta and a .locres for every culture — including the native one — with EGenerateLocResFlags::None
10RefreshFTextLocalizationManager::UpdateFromLocalizationResource refreshes live text; the toolbox broadcasts LocalizationDelegates::OnLocalizationTargetDataUpdated once the tasks finish

Two conventions make import deterministic:

  • Native culture first: files inside one task are sorted native-first and foreign archive entries get their effective source (the official NativeText semantics) synchronised, so import order cannot affect the result.
  • Metadata attached in place: an identical identity (Namespace + Key + Source) never adds a duplicate manifest context; the cliff.* metadata is attached to the existing context. Otherwise the official GenerateLocRes would resolve conflicting translations for one key.

Chain 2: export (UE → CLIFF)

StepActorDetail
1Editor entry pointWrites <Target>_CliffExport.ini: CommonSettings + GatherTextStep0 (CommandletClass=CliffExport with DestinationPath and CliffNamespace)
2UCliffExportCommandletReads the config and calls FCliffExporter::ExportTarget
3Reading dataMain path: FLocTextHelper::LoadAll over the manifest and every culture archive. Secondary path: ExportFromLocResFile reads a compiled .locres, recovering source text from the manifest when possible and warning per entry when it cannot
4Clan groupingOne FCliffDocument per clan. Clan resolution order: cliff.clan metadata → reverse Namespace mapping table → kebab-slug fallback (with a warning)
5ID generationGroup paths and entry IDs are stable kebab-case derived from SourceLocation / key fragments; collisions get an 8-digit `CRC32(Namespace
6Semantic fallbacktype / emotion / status / context / reference prefer cliff.* metadata, then the mapping tables; when nothing is known they fall back to sentence with a warning
7Self-checkThe serialized output is validated with ParseAndValidate before anything is delivered
8Write<OutputDirectory>/<Culture>/<Clan>.cliff (UTF-8 without BOM, LF)
9Sidecar<OutputDirectory>/<manifest stem>.cliffmap.json records canonical id ↔ (namespace, key)

Chain 3: AI translation

Saved/CliffAITranslate/<Target>/    ← wiped and recreated on every run
① CliffExport      → <Culture>/<Clan>.cliff (source plus whatever targets already exist)
② CliffAITranslate → per culture: scan .cliff, translate only entries without a target, write back in place
③ CliffImport      → manifest / archive + GenerateTextLocalizationResource
④ Generate Reports→ the official word-count report
  • Each target language owns an independent conversation and languages run concurrently (capped by Max Concurrent Cultures).
  • Every AI request happens inside the commandlet (an editor subprocess) on async HTTP with a unified event loop, so the main editor UI never blocks.
  • When the pipeline finishes the translated .cliff files are copied back into Content/Localization/<Target>/ as reviewable, committable intermediates.

Official data model cheat sheet

Official structKey fieldsHow the plugin maps it
FManifestEntryNamespace + Source + Contexts[]CLIFF clan → Namespace, source → Source
FManifestContextKey / SourceLocation / DevNotes / KeyMetadataObjWhere cliff.* metadata lives
FArchiveEntryNamespace / Key / Source / Translationtarget → Translation; foreign entries record the effective source
FTextLocalizationResourceLocalizedString / SourceStringHash / PriorityProduced by the official generator; the plugin only reads it back for verification
FTextLocalizationMetaDataResourceNativeCulture / CompiledCulturesUsed to discover the native culture during export

NOTE

.locres keeps no source text and no metadata (only a SourceStringHash and the translation), so a binary-only export necessarily loses semantics. That is precisely why the sidecar is part of the main export path — see the export pipeline.