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)
| Step | Actor | Detail |
|---|---|---|
| 1 | Editor entry point | Writes <Target>_CliffImport.ini: CommonSettings + GatherTextStep0 (CommandletClass=CliffImport) + GatherTextStep1 (CommandletClass=GenerateTextLocalizationResource) |
| 2 | Official executor | The GUI drives the official subprocess behind the official-style Slate window; headless runs go through the embedded UGatherTextCommandlet::Execute |
| 3 | UCliffImportCommandlet | Reads SourcePath / ManifestName / ArchiveName / NativeCulture / CulturesToGenerate plus CliffFilePath (repeatable) or CliffDirectory + CliffImportCulture |
| 4 | Parsing | Every .cliff goes through FCliffDocument::ParseAndValidate; any error-level issue fails the run and prints the offending line numbers |
| 5 | Assembly | Builds FLocTextHelper(TargetPath, ManifestName, ArchiveName, NativeCulture, Cultures) and calls LoadAll |
| 6 | Manifest write | Each entry calls AddSourceText(namespace, key, source, context) with cliff.* metadata attached to the context |
| 7 | Archive write | status: initial writes the manifest only; every other status writes the translation into that culture’s archive (empty targets and self-translations are rejected) |
| 8 | Save | SaveManifest / SaveArchive for each culture |
| 9 | Compile | The official GenerateTextLocalizationResource step produces .locmeta and a .locres for every culture — including the native one — with EGenerateLocResFlags::None |
| 10 | Refresh | FTextLocalizationManager::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
NativeTextsemantics) 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 officialGenerateLocReswould resolve conflicting translations for one key.
Chain 2: export (UE → CLIFF)
| Step | Actor | Detail |
|---|---|---|
| 1 | Editor entry point | Writes <Target>_CliffExport.ini: CommonSettings + GatherTextStep0 (CommandletClass=CliffExport with DestinationPath and CliffNamespace) |
| 2 | UCliffExportCommandlet | Reads the config and calls FCliffExporter::ExportTarget |
| 3 | Reading data | Main 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 |
| 4 | Clan grouping | One FCliffDocument per clan. Clan resolution order: cliff.clan metadata → reverse Namespace mapping table → kebab-slug fallback (with a warning) |
| 5 | ID generation | Group paths and entry IDs are stable kebab-case derived from SourceLocation / key fragments; collisions get an 8-digit `CRC32(Namespace |
| 6 | Semantic fallback | type / emotion / status / context / reference prefer cliff.* metadata, then the mapping tables; when nothing is known they fall back to sentence with a warning |
| 7 | Self-check | The serialized output is validated with ParseAndValidate before anything is delivered |
| 8 | Write | <OutputDirectory>/<Culture>/<Clan>.cliff (UTF-8 without BOM, LF) |
| 9 | Sidecar | <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
.clifffiles are copied back intoContent/Localization/<Target>/as reviewable, committable intermediates.
Official data model cheat sheet
| Official struct | Key fields | How the plugin maps it |
|---|---|---|
FManifestEntry | Namespace + Source + Contexts[] | CLIFF clan → Namespace, source → Source |
FManifestContext | Key / SourceLocation / DevNotes / KeyMetadataObj | Where cliff.* metadata lives |
FArchiveEntry | Namespace / Key / Source / Translation | target → Translation; foreign entries record the effective source |
FTextLocalizationResource | LocalizedString / SourceStringHash / Priority | Produced by the official generator; the plugin only reads it back for verification |
FTextLocalizationMetaDataResource | NativeCulture / CompiledCultures | Used to discover the native culture during export |
NOTE
.locreskeeps no source text and no metadata (only aSourceStringHashand 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.