// Copyright Epic Games, Inc. All Rights Reserved. #include "Commandlets/GenerateTextLocalizationReportCommandlet.h" #include "Commandlets/Commandlet.h" #include "CoreGlobals.h" #include "Internationalization/CulturePointer.h" #include "Internationalization/Internationalization.h" #include "Internationalization/Text.h" #include "LocTextHelper.h" #include "LocalizationSourceControlUtil.h" #include "Logging/LogCategory.h" #include "Logging/LogMacros.h" #include "Misc/CString.h" #include "Misc/ConfigCacheIni.h" #include "Misc/DateTime.h" #include "Templates/SharedPointer.h" #include "Trace/Detail/Channel.h" #include "ProfilingDebugging/ScopedTimers.h" DEFINE_LOG_CATEGORY_STATIC(LogGenerateTextLocalizationReportCommandlet, Log, All); UGenerateTextLocalizationReportCommandlet::UGenerateTextLocalizationReportCommandlet(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) { } int32 UGenerateTextLocalizationReportCommandlet::Main(const FString& Params) { UE_SCOPED_TIMER(TEXT("UGenerateTextLocalizationReportCommandlet::Main"), LogGenerateTextLocalizationReportCommandlet, Display); // 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"))); if ( ParamVal ) { GatherTextConfigPath = *ParamVal; } else { UE_LOG(LogGenerateTextLocalizationReportCommandlet, Error, TEXT("No config specified.")); } // Set config section. ParamVal = ParamVals.Find(FString(TEXT("Section"))); if ( ParamVal ) { SectionName = *ParamVal; } else { UE_LOG(LogGenerateTextLocalizationReportCommandlet, Error, TEXT("No config section specified.")); return -1; } // Common settings FString SourcePath; FString DestinationPath; // Settings for generating/appending to word count report file bool bWordCountReport = false; // Settings for generating loc conflict report file bool bConflictReport = false; // Get source path. if( !( GetPathFromConfig( *SectionName, TEXT("SourcePath"), SourcePath, GatherTextConfigPath ) ) ) { UE_LOG(LogGenerateTextLocalizationReportCommandlet, Error, TEXT("No source path specified.")); return -1; } // Get destination path. if( !( GetPathFromConfig( *SectionName, TEXT("DestinationPath"), DestinationPath, GatherTextConfigPath ) ) ) { UE_LOG(LogGenerateTextLocalizationReportCommandlet, Error, TEXT("No destination path specified.")); return -1; } // Get the timestamp from the commandline, if not provided we will use the current time. const FString* TimeStampParamVal = ParamVals.Find(FString(TEXT("TimeStamp"))); if ( TimeStampParamVal && !TimeStampParamVal->IsEmpty() ) { CmdlineTimeStamp = *TimeStampParamVal; } GetBoolFromConfig( *SectionName, TEXT("bWordCountReport"), bWordCountReport, GatherTextConfigPath ); GetBoolFromConfig( *SectionName, TEXT("bConflictReport"), bConflictReport, GatherTextConfigPath ); if( bWordCountReport ) { if( !ProcessWordCountReport( SourcePath, DestinationPath ) ) { UE_LOG(LogGenerateTextLocalizationReportCommandlet, Error, TEXT("Failed to generate word count report.")); return -1; } } if( bConflictReport ) { if( !ProcessConflictReport( DestinationPath) ) { UE_LOG(LogGenerateTextLocalizationReportCommandlet, Error, TEXT("Failed to generate localization conflict report.")); return -1; } } return 0; } bool UGenerateTextLocalizationReportCommandlet::ProcessWordCountReport(const FString& SourcePath, const FString& DestinationPath) { FDateTime Timestamp = FDateTime::Now(); if (!CmdlineTimeStamp.IsEmpty()) { FDateTime::Parse(*CmdlineTimeStamp, Timestamp); } // Get manifest name. FString ManifestName; if( !( GetStringFromConfig( *SectionName, TEXT("ManifestName"), ManifestName, GatherTextConfigPath ) ) ) { UE_LOG(LogGenerateTextLocalizationReportCommandlet, Error, TEXT("No manifest name specified.")); return false; } // Get archive name. FString ArchiveName; if( !( GetStringFromConfig( *SectionName, TEXT("ArchiveName"), ArchiveName, GatherTextConfigPath ) ) ) { UE_LOG(LogGenerateTextLocalizationReportCommandlet, Error, TEXT("No archive name specified.")); return false; } // Get report name. FString WordCountReportName; if( !( GetStringFromConfig( *SectionName, TEXT("WordCountReportName"), WordCountReportName, GatherTextConfigPath ) ) ) { UE_LOG(LogGenerateTextLocalizationReportCommandlet, Error, TEXT("No word count report name specified.")); return false; } // Get cultures to generate. TArray CulturesToGenerate; GetStringArrayFromConfig( *SectionName, TEXT("CulturesToGenerate"), CulturesToGenerate, GatherTextConfigPath ); for(int32 i = 0; i < CulturesToGenerate.Num(); ++i) { if( FInternationalization::Get().GetCulture( CulturesToGenerate[i] ).IsValid() ) { UE_LOG(LogGenerateTextLocalizationReportCommandlet, Verbose, TEXT("Specified culture is not a valid runtime culture, but may be a valid base language: %s"), *(CulturesToGenerate[i])); } } // Load the manifest and all archives FLocTextHelper LocTextHelper(SourcePath, ManifestName, ArchiveName, FString(), CulturesToGenerate, GatherManifestHelper->GetLocFileNotifies(), GatherManifestHelper->GetPlatformSplitMode()); LocTextHelper.SetCopyrightNotice(GatherManifestHelper->GetCopyrightNotice()); { FText LoadError; if (!LocTextHelper.LoadAll(ELocTextHelperLoadFlags::LoadOrCreate, &LoadError)) { UE_LOG(LogGenerateTextLocalizationReportCommandlet, Error, TEXT("%s"), *LoadError.ToString()); return false; } } const FString ReportFilePath = (DestinationPath / WordCountReportName); FText ReportSaveError; if (!LocTextHelper.SaveWordCountReport(Timestamp, ReportFilePath, &ReportSaveError)) { UE_LOG(LogGenerateTextLocalizationReportCommandlet, Error, TEXT("%s"), *ReportSaveError.ToString()); return false; } return true; } bool UGenerateTextLocalizationReportCommandlet::ProcessConflictReport(const FString& DestinationPath) { // Get report name. FString ConflictReportName; if (!GetStringFromConfig(*SectionName, TEXT("ConflictReportName"), ConflictReportName, GatherTextConfigPath)) { UE_LOG(LogGenerateTextLocalizationReportCommandlet, Error, TEXT("No conflict report name specified.")); return false; } EConflictReportFormat ConflictReportFormat = EConflictReportFormat::None; static const FString TxtExtension = TEXT(".txt"); static const FString CSVExtension = TEXT(".csv"); FString FileExtension = FPaths::GetExtension(ConflictReportName, true); if (FileExtension == TxtExtension) { ConflictReportFormat = EConflictReportFormat::Txt; } else if (FileExtension == CSVExtension) { ConflictReportFormat = EConflictReportFormat::CSV; } // This is an unsupported extension or an empty extension else { // We default to csv ConflictReportFormat = EConflictReportFormat::CSV; // We found a file extension somewhere in the specified name. if (!FileExtension.IsEmpty()) { UE_LOG(LogGenerateTextLocalizationReportCommandlet, Warning, TEXT("The conflict report filename %s has an unsupported extension. Only .txt and .csv is supported at this time."), *ConflictReportName); } else { UE_LOG(LogGenerateTextLocalizationReportCommandlet, Warning, TEXT("The conflict report filename %s has no extension. Defaulting the report to be generated as a .csv file."), *ConflictReportName); } ConflictReportName = FPaths::SetExtension(ConflictReportName, CSVExtension); } const FString ReportFilePath = (DestinationPath / ConflictReportName); FText ReportSaveError; if (!GatherManifestHelper->SaveConflictReport(ReportFilePath, ConflictReportFormat , &ReportSaveError)) { UE_LOG(LogGenerateTextLocalizationReportCommandlet, Error, TEXT("%s"), *ReportSaveError.ToString()); return false; } return true; }