AI 翻译与命令let
FCliffAITranslator
struct CLIFFLOCALIZATIONSUITEEDITOR_API FCliffAITranslationStats
{
int32 CandidateCount = 0; // 进入请求的候选条目累计(含重跑轮次)
int32 TranslatedCount = 0; // 成功写回并置 translated
int32 SkippedCount = 0; // 已有 target 且非重译候选
int32 FailedCount = 0; // 无回包/空译文/占位符不符/自翻译/修复后仍非法/写盘失败
int32 RepairCount = 0; // 成功修复次数
int32 BatchCount = 0; // 批次数
TArray<FString> FailedFiles;
};
class CLIFFLOCALIZATIONSUITEEDITOR_API FCliffAITranslator
{
public:
static bool TranslateCliffDirectory(const FString& InCliffDirectory,
const TArray<FString>& InCultures,
const UCliffLocalizationSuiteSettings* InSettings,
FCliffAITranslationStats& OutStats,
FText& OutError,
int32 InRetryDepth = 0);
static bool TestConnection(const UCliffLocalizationSuiteSettings* InSettings,
FString& OutMessage, FText& OutError);
static bool IsSelfTranslation(const FString& InSource, const FString& InTarget, const FString& InTargetLanguage);
static bool IsRetranslationCandidate(const FString& InSource, const FString& InTarget, const FString& InStatus,
const FString& InTargetLanguage, const bool bEnabled);
};
示例
const UCliffLocalizationSuiteSettings* Settings = GetDefault<UCliffLocalizationSuiteSettings>();
// 1) 连接自检
FString Message; FText Error;
if (!FCliffAITranslator::TestConnection(Settings, Message, Error))
{
UE_LOG(LogTemp, Error, TEXT("DeepSeek: %s"), *Error.ToString());
}
// 2) 翻译一个目录下的全部 .cliff(按 culture 筛选)
FCliffAITranslationStats Stats;
if (FCliffAITranslator::TranslateCliffDirectory(
FPaths::ProjectSavedDir() / TEXT("CliffAITranslate/Game"),
{ TEXT("ja"), TEXT("ko") }, Settings, Stats, Error))
{
UE_LOG(LogTemp, Log, TEXT("translated %d / candidates %d, failed %d, repairs %d"),
Stats.TranslatedCount, Stats.CandidateCount, Stats.FailedCount, Stats.RepairCount);
}
// 3) 判定辅助函数
const bool bSelf = FCliffAITranslator::IsSelfTranslation(
TEXT("DeepSeek response has no message content."), TEXT("DeepSeek response has no message content."), TEXT("zh-Hans")); // true
const bool bCandidate = FCliffAITranslator::IsRetranslationCandidate(
TEXT("Play"), TEXT("Play"), TEXT("translated"), TEXT("zh-Hans"), /*bEnabled=*/true); // true
行为契约
| 项 | 规则 |
|---|
| 候选筛选 | 有 source 且无 target;或 status == "translated" 且判定为自翻译且开关开启 |
| 永不触碰 | reviewed / final |
| 空译文 | 源文属目标语言 → 本地填源文;否则拒绝写回 |
| 自翻译 | 拒绝写回并计入失败 |
| 占位符 | 数量相等且源文每个 {…} 都出现在译文中,否则拒绝写回 |
| 重试 | 单批重试最多 MaxRetries 次(间隔 0.5s 递增);整轮后仍有失败则再跑(最多 MaxRetries 轮) |
| 修复 | 写回后 ParseAndValidate 失败且 bAutoRepairCliff → 最多 MaxRepairAttempts 轮;仍失败则不写盘并记入 FailedFiles |
| 整体失败 | 仅当「候选数 > 0 且译文数为 0」;部分成功只告警 |
| 并发 | 批次按 MaxConcurrentCultures 分块并发发出后统一等待 |
| Key 解析 | 环境变量 DEEPSEEK_API_KEY 优先,其次设置字段;日志与配置中不出现 Key |
| 脚本判定 | 拉丁 / CJK / 阿拉伯 / 西里尔四个族,按目标语言前缀(zh/ja/ko、ar/fa/he/ur、ru/uk/bg/sr,其余视为拉丁)判定是否「已属目标语言」 |
GatherText 命令let
UCLASS()
class CLIFFLOCALIZATIONSUITEEDITOR_API UCliffImportCommandlet : public UGatherTextCommandletBase
{
virtual int32 Main(const FString& Params) override;
virtual EGatherTextCommandletPhase GetPhase() const override { return EGatherTextCommandletPhase::Import; }
};
UCLASS()
class CLIFFLOCALIZATIONSUITEEDITOR_API UCliffExportCommandlet : public UGatherTextCommandletBase
{
virtual int32 Main(const FString& Params) override;
virtual EGatherTextCommandletPhase GetPhase() const override { return EGatherTextCommandletPhase::Export; }
};
UCLASS()
class CLIFFLOCALIZATIONSUITEEDITOR_API UCliffAITranslateCommandlet : public UGatherTextCommandletBase
{
virtual int32 Main(const FString& Params) override;
virtual EGatherTextCommandletPhase GetPhase() const override { return EGatherTextCommandletPhase::Import; }
};
| 命令let | CommandletClass | 必需配置键 | 返回 |
|---|
UCliffImportCommandlet | CliffImport | SourcePath / ManifestName / ArchiveName / NativeCulture,以及 CliffFilePath[] 或 CliffDirectory | 0 成功,-1 失败 |
UCliffExportCommandlet | CliffExport | SourcePath / DestinationPath / ManifestName / ArchiveName / NativeCulture / CliffNamespace | 0 / -1 |
UCliffAITranslateCommandlet | CliffAITranslate | SourcePath / DestinationPath / CulturesToGenerate[] | 0 / -1 |
它们由官方执行器以 -Config="<ini>" -Section="GatherTextStep{N}" 调用;缺任一参数即报 No config or section specified. 并返回 -1。键的完整清单见命令与命令let。
工具箱入口(Editor-only)
namespace FCliffEditorToolbox
{
bool BuildImportSettings(const ULocalizationTarget*, FCliffImportSettings&, FText&);
bool BuildExportSettings(const ULocalizationTarget*, FCliffExportSettings&, FText&);
bool ImportCliffFile(const ULocalizationTarget*, const FString& InCliffFilePath, FCliffImportStats&, TArray<FCliffValidationIssue>&, FText&);
bool ImportCliffFileOfficial(const ULocalizationTarget*, const FString& InCliffFilePath, FText&);
bool ImportCliffFilesOfficial(const ULocalizationTarget*, const TArray<FString>&, FText&);
bool ExportTargetToDirectory(const ULocalizationTarget*, const FString& InOutputDirectory, TArray<FCliffExportDocument>&, FCliffExportStats&, FText&);
bool ExportTargetToDirectoryOfficial(const ULocalizationTarget*, const FString& InOutputDirectory, FText&);
bool AITranslateTargetsOfficial(const ULocalizationTarget*, const TArray<FString>& InCultures, const FString& InSourceLanguage, FText&);
bool AITranslateTargetsOfficial(const TArray<ULocalizationTarget*>&, const TArray<FString>&, const FString&, FText&);
bool ExportTargetsToDirectoryOfficial(const TArray<ULocalizationTarget*>&, const FString&, FText&);
bool ImportCliffDirectoryOfficialForTargets(const TArray<ULocalizationTarget*>&, const FString&, FText&);
void ImportFromDialog(const TWeakObjectPtr<ULocalizationTarget>&);
void ImportFromDialog(const TWeakObjectPtr<ULocalizationTargetSet>&);
void ExportToDialog(const TWeakObjectPtr<ULocalizationTarget>&);
void ExportToDialog(const TWeakObjectPtr<ULocalizationTargetSet>&);
void AITranslateFromDialog(const TWeakObjectPtr<ULocalizationTarget>&);
void AITranslateFromDialog(const TWeakObjectPtr<ULocalizationTargetSet>&);
void NotifyTargetSetAction(const FText& InActionName);
}
| 命名 | 说明 |
|---|
…Official 后缀 | 走官方 GatherText 管线(生成配置、起子进程、跑报告、刷 Dashboard),GUI 与 CLI 用的就是这些 |
无后缀(ImportCliffFile / ExportTargetToDirectory) | 直接调用导入器/导出器,不起子进程,适合工具与测试 |
*FromDialog | Dashboard 按钮的实际入口:弹目录或 Culture 选择窗口,然后调度上面的官方入口 |
CliffLocalizationServiceProvider
class FCliffLocalizationServiceProvider : public ILocalizationServiceProvider
{
const FName& GetName() const override; // "CliffLocalizationSuite"
const FText GetDisplayName() const override; // "CLIFF Localization Suite"
FText GetStatusText() const override; // "CLIFF import/export is ready."
bool IsEnabled() const override; // true
bool IsAvailable() const override; // true
void CustomizeTargetToolbar(TSharedRef<FExtender>&, TWeakObjectPtr<ULocalizationTarget>) const override;
void CustomizeTargetSetToolbar(TSharedRef<FExtender>&, TWeakObjectPtr<ULocalizationTargetSet>) const override;
};
模块启动时注册为 "LocalizationService" modular feature 并设为当前 Provider(否则 Dashboard 会快照默认 Provider,按钮不会出现)。
相关阅读