// Copyright Epic Games, Inc. All Rights Reserved. #include "Commandlets/GenerateTextLocalizationResourceCommandlet.h" #include "HAL/FileManager.h" #include "Misc/Paths.h" #include "Internationalization/TextLocalizationResource.h" #include "TextLocalizationResourceGenerator.h" #include "Templates/UniquePtr.h" DEFINE_LOG_CATEGORY_STATIC(LogGenerateTextLocalizationResourceCommandlet, Log, All); UGenerateTextLocalizationResourceCommandlet::UGenerateTextLocalizationResourceCommandlet(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) { } int32 UGenerateTextLocalizationResourceCommandlet::Main(const FString& Params) { // Parse command line - we're interested in the param vals TArray Tokens; TArray Switches; TMap ParamVals; UCommandlet::ParseCommandLine(*Params, Tokens, Switches, ParamVals); // Set config file. const FString* ParamVal = ParamVals.Find(FString(TEXT("Config"))); FString GatherTextConfigPath; if (ParamVal) { GatherTextConfigPath = *ParamVal; } else { UE_LOG(LogGenerateTextLocalizationResourceCommandlet, Error, TEXT("No config specified.")); return -1; } // Set config section. ParamVal = ParamVals.Find(FString(TEXT("Section"))); FString SectionName; if (ParamVal) { SectionName = *ParamVal; } else { UE_LOG(LogGenerateTextLocalizationResourceCommandlet, Error, TEXT("No config section specified.")); return -1; } // Get source path. FString SourcePath; if (!(GetPathFromConfig(*SectionName, TEXT("SourcePath"), SourcePath, GatherTextConfigPath))) { UE_LOG(LogGenerateTextLocalizationResourceCommandlet, Error, TEXT("No source path specified.")); return -1; } // Get manifest name. FString ManifestName; if (!(GetStringFromConfig(*SectionName, TEXT("ManifestName"), ManifestName, GatherTextConfigPath))) { UE_LOG(LogGenerateTextLocalizationResourceCommandlet, Error, TEXT("No manifest name specified.")); return -1; } // Get archive name. FString ArchiveName; if (!GetStringFromConfig(*SectionName, TEXT("ArchiveName"), ArchiveName, GatherTextConfigPath)) { UE_LOG(LogGenerateTextLocalizationResourceCommandlet, Error, TEXT("No archive name specified.")); return -1; } // Get cultures to generate. FString NativeCultureName; if (!(GetStringFromConfig(*SectionName, TEXT("NativeCulture"), NativeCultureName, GatherTextConfigPath))) { UE_LOG(LogGenerateTextLocalizationResourceCommandlet, Error, TEXT("No native culture specified.")); return -1; } // Get cultures to generate. TArray CulturesToGenerate; GetStringArrayFromConfig(*SectionName, TEXT("CulturesToGenerate"), CulturesToGenerate, GatherTextConfigPath); if (CulturesToGenerate.Num() == 0) { UE_LOG(LogGenerateTextLocalizationResourceCommandlet, Error, TEXT("No cultures specified for generation.")); return -1; } for (int32 i = 0; i < CulturesToGenerate.Num(); ++i) { if (FInternationalization::Get().GetCulture(CulturesToGenerate[i]).IsValid()) { UE_LOG(LogGenerateTextLocalizationResourceCommandlet, Verbose, TEXT("Specified culture is not a valid runtime culture, but may be a valid base language: %s"), *(CulturesToGenerate[i])); } } // Get destination path. FString DestinationPath; if (!(GetPathFromConfig(*SectionName, TEXT("DestinationPath"), DestinationPath, GatherTextConfigPath))) { UE_LOG(LogGenerateTextLocalizationResourceCommandlet, Error, TEXT("No destination path specified.")); return -1; } // Get resource name. FString ResourceName; if (!(GetStringFromConfig(*SectionName, TEXT("ResourceName"), ResourceName, GatherTextConfigPath))) { UE_LOG(LogGenerateTextLocalizationResourceCommandlet, Error, TEXT("No resource name specified.")); return -1; } EGenerateLocResFlags GenerateFlags = EGenerateLocResFlags::None; // Get whether to skip the source check. { bool bSkipSourceCheck = false; GetBoolFromConfig(*SectionName, TEXT("bSkipSourceCheck"), bSkipSourceCheck, GatherTextConfigPath); GenerateFlags |= (bSkipSourceCheck ? EGenerateLocResFlags::AllowStaleTranslations : EGenerateLocResFlags::None); } // Get whether to validate format patterns. { bool bValidateFormatPatterns = false; GetBoolFromConfig(*SectionName, TEXT("bValidateFormatPatterns"), bValidateFormatPatterns, GatherTextConfigPath); GenerateFlags |= (bValidateFormatPatterns ? EGenerateLocResFlags::ValidateFormatPatterns : EGenerateLocResFlags::None); } // Get whether to validate whitespace. { bool bValidateSafeWhitespace = false; GetBoolFromConfig(*SectionName, TEXT("bValidateSafeWhitespace"), bValidateSafeWhitespace, GatherTextConfigPath); GenerateFlags |= (bValidateSafeWhitespace ? EGenerateLocResFlags::ValidateSafeWhitespace : EGenerateLocResFlags::None); } // Load the manifest and all archives FLocTextHelper LocTextHelper(SourcePath, ManifestName, ArchiveName, NativeCultureName, CulturesToGenerate, GatherManifestHelper->GetLocFileNotifies(), GatherManifestHelper->GetPlatformSplitMode()); { FText LoadError; if (!LocTextHelper.LoadAll(ELocTextHelperLoadFlags::LoadOrCreate, &LoadError)) { UE_LOG(LogGenerateTextLocalizationResourceCommandlet, Error, TEXT("%s"), *LoadError.ToString()); return -1; } } // Generate the LocMeta file for all cultures { const FString TextLocalizationMetaDataResourcePath = DestinationPath / FPaths::GetBaseFilename(ResourceName) + TEXT(".locmeta"); const bool bLocMetaFileSaved = FLocalizedAssetSCCUtil::SaveFileWithSCC(SourceControlInfo, TextLocalizationMetaDataResourcePath, [&LocTextHelper, &ResourceName](const FString& InSaveFileName) -> bool { FTextLocalizationMetaDataResource LocMeta; return FTextLocalizationResourceGenerator::GenerateLocMeta(LocTextHelper, ResourceName, LocMeta) && LocMeta.SaveToFile(InSaveFileName); }); if (!bLocMetaFileSaved) { UE_LOG(LogGenerateTextLocalizationResourceCommandlet, Error, TEXT("Could not write file %s"), *TextLocalizationMetaDataResourcePath); return -1; } } // Generate the LocRes file for each culture for (const FString& CultureName: CulturesToGenerate) { auto GenerateSingleLocRes = [this, &DestinationPath, &CultureName, &ResourceName](const FTextLocalizationResource& InLocRes, const FName InPlatformName) -> bool { FString TextLocalizationResourcePath; if (InPlatformName.IsNone()) { TextLocalizationResourcePath = DestinationPath / CultureName / ResourceName; } else { TextLocalizationResourcePath = DestinationPath / FPaths::GetPlatformLocalizationFolderName() / InPlatformName.ToString() / CultureName / ResourceName; } const bool bLocResFileSaved = FLocalizedAssetSCCUtil::SaveFileWithSCC(SourceControlInfo, TextLocalizationResourcePath, [&InLocRes](const FString& InSaveFileName) -> bool { return InLocRes.SaveToFile(InSaveFileName); }); if (!bLocResFileSaved) { UE_LOG(LogGenerateTextLocalizationResourceCommandlet, Error, TEXT("Could not write file %s"), *TextLocalizationResourcePath); return false; } return true; }; FTextLocalizationResource PlatformAgnosticLocRes; TMap> PerPlatformLocRes; const FTextKey LocResId = DestinationPath / CultureName / ResourceName; if (!FTextLocalizationResourceGenerator::GenerateLocRes(LocTextHelper, CultureName, GenerateFlags, LocResId, PlatformAgnosticLocRes, PerPlatformLocRes)) { UE_LOG(LogGenerateTextLocalizationResourceCommandlet, Error, TEXT("Failed to generate LocRes %s"), LocResId.GetChars()); return false; } bool bSuccess = GenerateSingleLocRes(PlatformAgnosticLocRes, FName()); for (const auto& PerPlatformLocResPair: PerPlatformLocRes) { bSuccess &= GenerateSingleLocRes(*PerPlatformLocResPair.Value, PerPlatformLocResPair.Key); } if (!bSuccess) { return -1; } } return 0; }