int32 UGenerateGatherArchiveCommandlet::Main( const FString& Params ) { FInternationalization& I18N = FInternationalization::Get(); // Parse command line - we're interested in the param vals TArray<FString> Tokens; TArray<FString> Switches; TMap<FString, FString> 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(LogGenerateArchiveCommandlet, 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(LogGenerateArchiveCommandlet, Error, TEXT("No config section specified.")); return -1; } // Get manifest name. FString ManifestName; if( !GetConfigString( *SectionName, TEXT("ManifestName"), ManifestName, GatherTextConfigPath ) ) { UE_LOG( LogGenerateArchiveCommandlet, Error, TEXT("No manifest name specified.") ); return -1; } // Get source culture. FString SourceCulture; if( GetConfigString( *SectionName, TEXT("SourceCulture"), SourceCulture, GatherTextConfigPath ) ) { if( I18N.GetCulture( SourceCulture ).IsValid() ) { UE_LOG(LogGenerateArchiveCommandlet, Verbose, TEXT("Specified culture is not a valid runtime culture, but may be a valid base language: %s"), *(SourceCulture) ); } } // Get cultures to generate. TArray<FString> CulturesToGenerate; GetConfigArray(*SectionName, TEXT("CulturesToGenerate"), CulturesToGenerate, GatherTextConfigPath); if( CulturesToGenerate.Num() == 0 ) { UE_LOG(LogGenerateArchiveCommandlet, Error, TEXT("No cultures specified for generation.")); return -1; } for(int32 i = 0; i < CulturesToGenerate.Num(); ++i) { if( I18N.GetCulture( CulturesToGenerate[i] ).IsValid() ) { UE_LOG(LogGenerateArchiveCommandlet, 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( !GetConfigString( *SectionName, TEXT("DestinationPath"), DestinationPath, GatherTextConfigPath ) ) { UE_LOG( LogGenerateArchiveCommandlet, Error, TEXT("No destination path specified.") ); return -1; } if (FPaths::IsRelative(DestinationPath)) { if (!FPaths::GameDir().IsEmpty()) { DestinationPath = FPaths::Combine( *( FPaths::GameDir() ), *DestinationPath ); } else { DestinationPath = FPaths::Combine( *( FPaths::EngineDir() ), *DestinationPath ); } } // Get archive name. FString ArchiveName; if( !( GetConfigString(* SectionName, TEXT("ArchiveName"), ArchiveName, GatherTextConfigPath ) ) ) { UE_LOG(LogGenerateArchiveCommandlet, Error, TEXT("No archive name specified.")); return -1; } // Get bPurgeOldEmptyEntries option. bool ShouldPurgeOldEmptyEntries; if ( !GetConfigBool( *SectionName, TEXT("bPurgeOldEmptyEntries"), ShouldPurgeOldEmptyEntries, GatherTextConfigPath) ) { ShouldPurgeOldEmptyEntries = false; } FString ManifestFilePath = DestinationPath / ManifestName; TSharedPtr<FJsonObject> ManifestJsonObject = ReadJSONTextFile( ManifestFilePath ); if( !ManifestJsonObject.IsValid() ) { UE_LOG(LogGenerateArchiveCommandlet, Error, TEXT("Could not read manifest file %s."), *ManifestFilePath); return -1; } FJsonInternationalizationManifestSerializer ManifestSerializer; TSharedRef< FInternationalizationManifest > InternationalizationManifest = MakeShareable( new FInternationalizationManifest ); ManifestSerializer.DeserializeManifest( ManifestJsonObject.ToSharedRef(), InternationalizationManifest ); for(int32 Culture = 0; Culture < CulturesToGenerate.Num(); Culture++) { TSharedRef< FInternationalizationArchive > InternationalizationArchive = MakeShareable( new FInternationalizationArchive ); BuildArchiveFromManifest( InternationalizationManifest, InternationalizationArchive, SourceCulture, CulturesToGenerate[Culture] ); const FString CulturePath = DestinationPath / CulturesToGenerate[Culture]; FJsonInternationalizationArchiveSerializer ArchiveSerializer; TSharedRef< FInternationalizationArchive > OutputInternationalizationArchive = MakeShareable( new FInternationalizationArchive ); // Read in any existing archive for this culture. FString ExistingArchiveFileName = CulturePath / ArchiveName; TSharedPtr< FJsonObject > ExistingArchiveJsonObject = NULL; if( FPaths::FileExists(ExistingArchiveFileName) ) { ExistingArchiveJsonObject = ReadJSONTextFile( ExistingArchiveFileName ); // Some of the existing archives were saved out with an "Unnamed" namespace for the root instead of the empty string. We try to fix that here. if( ExistingArchiveJsonObject->HasField( FJsonInternationalizationArchiveSerializer::TAG_NAMESPACE ) ) { FString RootNamespace = ExistingArchiveJsonObject->GetStringField( FJsonInternationalizationArchiveSerializer::TAG_NAMESPACE ); if( RootNamespace == TEXT("Unnamed") ) { ExistingArchiveJsonObject->RemoveField( FJsonInternationalizationArchiveSerializer::TAG_NAMESPACE ); ExistingArchiveJsonObject->SetStringField( FJsonInternationalizationArchiveSerializer::TAG_NAMESPACE, TEXT("") ); } } struct Local { // Purges this JSONObject of an entries with no translated text and purges empty namespaces. // Returns true if the object was modified, false if not. static bool PurgeNamespaceOfEmptyEntries(const TSharedPtr<FJsonObject>& JSONObject) { bool ModifiedChildrenArray = false; if( JSONObject->HasField( FJsonInternationalizationArchiveSerializer::TAG_CHILDREN ) ) { TArray<TSharedPtr<FJsonValue>> ChildrenArray = JSONObject->GetArrayField(FJsonInternationalizationArchiveSerializer::TAG_CHILDREN); for( int32 ChildIndex = ChildrenArray.Num() - 1; ChildIndex >= 0; --ChildIndex ) { TSharedPtr<FJsonObject> Child = ChildrenArray[ ChildIndex ]->AsObject(); TSharedPtr<FJsonObject> TranslationObject = Child->GetObjectField(FJsonInternationalizationArchiveSerializer::TAG_TRANSLATION); const FString& TranslatedText = TranslationObject->GetStringField(FJsonInternationalizationArchiveSerializer::TAG_TRANSLATION_TEXT); if(TranslatedText.IsEmpty()) { ChildrenArray.RemoveAt( ChildIndex ); Child = NULL; ModifiedChildrenArray = true; } } if(ModifiedChildrenArray) { JSONObject->RemoveField(FJsonInternationalizationArchiveSerializer::TAG_CHILDREN); if(ChildrenArray.Num()) { JSONObject->SetArrayField(FJsonInternationalizationArchiveSerializer::TAG_CHILDREN, ChildrenArray); } } } bool ModifiedSubnamespaceArray = false; if( JSONObject->HasField( FJsonInternationalizationArchiveSerializer::TAG_SUBNAMESPACES ) ) { TArray<TSharedPtr<FJsonValue>> SubnamespaceArray = JSONObject->GetArrayField(FJsonInternationalizationArchiveSerializer::TAG_SUBNAMESPACES); for( int32 Index = SubnamespaceArray.Num() - 1; Index >= 0; --Index ) { TSharedPtr<FJsonObject> Subnamespace = SubnamespaceArray[ Index ]->AsObject(); ModifiedSubnamespaceArray = PurgeNamespaceOfEmptyEntries(Subnamespace); bool HasChildren = Subnamespace->HasField( FJsonInternationalizationArchiveSerializer::TAG_CHILDREN ); bool HasSubnamespaces = Subnamespace->HasField( FJsonInternationalizationArchiveSerializer::TAG_SUBNAMESPACES ); if(!HasChildren && !HasSubnamespaces) { SubnamespaceArray.RemoveAt( Index ); Subnamespace = NULL; ModifiedSubnamespaceArray = true; } } if(ModifiedSubnamespaceArray) { JSONObject->RemoveField(FJsonInternationalizationArchiveSerializer::TAG_SUBNAMESPACES); if(SubnamespaceArray.Num()) { JSONObject->SetArrayField(FJsonInternationalizationArchiveSerializer::TAG_SUBNAMESPACES, SubnamespaceArray); } } } return ModifiedChildrenArray || ModifiedSubnamespaceArray; } }; if(ShouldPurgeOldEmptyEntries) { // Remove entries lacking translations from pre-existing archive. // If they are absent in the source manifest, we save on not translating non-existent text. // If they are present in the source manifest, then the newly generated entries will contain the empty text again. Local::PurgeNamespaceOfEmptyEntries(ExistingArchiveJsonObject); } ArchiveSerializer.DeserializeArchive( ExistingArchiveJsonObject.ToSharedRef(), OutputInternationalizationArchive ); } if (InternationalizationArchive->GetFormatVersion() < FInternationalizationArchive::EFormatVersion::Latest) { UE_LOG( LogGenerateArchiveCommandlet, Error,TEXT("Archive version is out of date. Repair the archives or manually set the version to %d."), static_cast<int32>(FInternationalizationArchive::EFormatVersion::Latest)); return -1; } // Combine the generated gather archive with the contents of the archive structure we will write out. AppendArchiveData( InternationalizationArchive, OutputInternationalizationArchive ); InternationalizationArchive->SetFormatVersion(FInternationalizationArchive::EFormatVersion::Latest); TSharedRef< FJsonObject > OutputArchiveJsonObj = MakeShareable( new FJsonObject ); ArchiveSerializer.SerializeArchive( OutputInternationalizationArchive, OutputArchiveJsonObj ); if( !WriteArchiveToFile( OutputArchiveJsonObj, DestinationPath, *CulturesToGenerate[Culture], *ArchiveName ) ) { UE_LOG( LogGenerateArchiveCommandlet, Error,TEXT("Failed to write archive to %s."), *DestinationPath ); return -1; } } return 0; }
bool UInternationalizationExportCommandlet::DoImport(const FString& SourcePath, const FString& DestinationPath, const FString& Filename) { // Get manifest name. FString ManifestName; if( !GetStringFromConfig( *SectionName, TEXT("ManifestName"), ManifestName, ConfigPath ) ) { UE_LOG( LogInternationalizationExportCommandlet, Error, TEXT("No manifest name specified.") ); return false; } // Get archive name. FString ArchiveName; if( !( GetStringFromConfig(* SectionName, TEXT("ArchiveName"), ArchiveName, ConfigPath ) ) ) { UE_LOG(LogInternationalizationExportCommandlet, Error, TEXT("No archive name specified.")); return false; } // Get culture directory setting, default to true if not specified (used to allow picking of import directory with file open dialog from Translation Editor) bool bUseCultureDirectory = true; if (!(GetBoolFromConfig(*SectionName, TEXT("bUseCultureDirectory"), bUseCultureDirectory, ConfigPath))) { bUseCultureDirectory = true; } // Process the desired cultures for(int32 Culture = 0; Culture < CulturesToGenerate.Num(); Culture++) { // Load the Portable Object file if found const FString CultureName = CulturesToGenerate[Culture]; FString POFilePath = ""; if (bUseCultureDirectory) { POFilePath = SourcePath / CultureName / Filename; } else { POFilePath = SourcePath / Filename; } if( !FPaths::FileExists(POFilePath) ) { UE_LOG( LogInternationalizationExportCommandlet, Warning, TEXT("Could not find file %s"), *POFilePath ); continue; } FString POFileContents; if ( !FFileHelper::LoadFileToString( POFileContents, *POFilePath ) ) { UE_LOG( LogInternationalizationExportCommandlet, Error, TEXT("Failed to load file %s."), *POFilePath); continue; } FPortableObjectFormatDOM PortableObject; if( !PortableObject.FromString( POFileContents ) ) { UE_LOG( LogInternationalizationExportCommandlet, Error, TEXT("Failed to parse Portable Object file %s."), *POFilePath); continue; } if( PortableObject.GetProjectName() != ManifestName.Replace(TEXT(".manifest"), TEXT("")) ) { UE_LOG( LogInternationalizationExportCommandlet, Warning, TEXT("The project name (%s) in the file (%s) did not match the target manifest project (%s)."), *POFilePath, *PortableObject.GetProjectName(), *ManifestName.Replace(TEXT(".manifest"), TEXT(""))); } const FString DestinationCulturePath = DestinationPath / CultureName; FString ArchiveFileName = DestinationCulturePath / ArchiveName; if( !FPaths::FileExists(ArchiveFileName) ) { UE_LOG( LogInternationalizationExportCommandlet, Error, TEXT("Failed to find destination archive %s."), *ArchiveFileName); continue; } TSharedPtr< FJsonObject > ArchiveJsonObject = NULL; ArchiveJsonObject = ReadJSONTextFile( ArchiveFileName ); FJsonInternationalizationArchiveSerializer ArchiveSerializer; TSharedRef< FInternationalizationArchive > InternationalizationArchive = MakeShareable( new FInternationalizationArchive ); ArchiveSerializer.DeserializeArchive( ArchiveJsonObject.ToSharedRef(), InternationalizationArchive ); bool bModifiedArchive = false; { for( auto EntryIter = PortableObject.GetEntriesIterator(); EntryIter; ++EntryIter ) { auto POEntry = *EntryIter; if( POEntry->MsgId.IsEmpty() || POEntry->MsgStr.Num() == 0 || POEntry->MsgStr[0].Trim().IsEmpty() ) { // We ignore the header entry or entries with no translation. continue; } // Some warning messages for data we don't process at the moment if( !POEntry->MsgIdPlural.IsEmpty() || POEntry->MsgStr.Num() > 1 ) { UE_LOG( LogInternationalizationExportCommandlet, Error, TEXT("Portable Object entry has plural form we did not process. File: %s MsgCtxt: %s MsgId: %s"), *POFilePath, *POEntry->MsgCtxt, *POEntry->MsgId ); } const FString& Namespace = POEntry->MsgCtxt; const FString& SourceText = ConditionPoStringForArchive(POEntry->MsgId); const FString& Translation = ConditionPoStringForArchive(POEntry->MsgStr[0]); //@TODO: Take into account optional entries and entries that differ by keymetadata. Ex. Each optional entry needs a unique msgCtxt TSharedPtr< FArchiveEntry > FoundEntry = InternationalizationArchive->FindEntryBySource( Namespace, SourceText, NULL ); if( !FoundEntry.IsValid() ) { UE_LOG(LogInternationalizationExportCommandlet, Warning, TEXT("Could not find corresponding archive entry for PO entry. File: %s MsgCtxt: %s MsgId: %s"), *POFilePath, *POEntry->MsgCtxt, *POEntry->MsgId ); continue; } if( FoundEntry->Translation != Translation ) { FoundEntry->Translation = Translation; bModifiedArchive = true; } } } if( bModifiedArchive ) { TSharedRef<FJsonObject> FinalArchiveJsonObj = MakeShareable( new FJsonObject ); ArchiveSerializer.SerializeArchive( InternationalizationArchive, FinalArchiveJsonObj ); if( !WriteJSONToTextFile(FinalArchiveJsonObj, ArchiveFileName, SourceControlInfo ) ) { UE_LOG( LogInternationalizationExportCommandlet, Error, TEXT("Failed to write archive to %s."), *ArchiveFileName ); return false; } } } return true; }
bool UInternationalizationExportCommandlet::DoImport(const FString& SourcePath, const FString& DestinationPath, const FString& Filename) { // Get manifest name. FString ManifestName; if( !GetStringFromConfig( *SectionName, TEXT("ManifestName"), ManifestName, ConfigPath ) ) { UE_LOG( LogInternationalizationExportCommandlet, Error, TEXT("No manifest name specified.") ); return false; } // Get archive name. FString ArchiveName; if( !( GetStringFromConfig(* SectionName, TEXT("ArchiveName"), ArchiveName, ConfigPath ) ) ) { UE_LOG(LogInternationalizationExportCommandlet, Error, TEXT("No archive name specified.")); return false; } // Get culture directory setting, default to true if not specified (used to allow picking of import directory with file open dialog from Translation Editor) bool bUseCultureDirectory = true; if (!(GetBoolFromConfig(*SectionName, TEXT("bUseCultureDirectory"), bUseCultureDirectory, ConfigPath))) { bUseCultureDirectory = true; } // Process the desired cultures for(int32 Culture = 0; Culture < CulturesToGenerate.Num(); Culture++) { // Load the Portable Object file if found const FString CultureName = CulturesToGenerate[Culture]; FString POFilePath = ""; if (bUseCultureDirectory) { POFilePath = SourcePath / CultureName / Filename; } else { POFilePath = SourcePath / Filename; } FPortableObjectFormatDOM PortableObject; const bool HasLoadedPOFile = LoadPOFile(POFilePath, PortableObject); if (!HasLoadedPOFile) { continue; } if (ShouldPersistComments) { PreserveExtractedCommentsForPersistence(PortableObject); } if (PortableObject.GetProjectName() != ManifestName.Replace(TEXT(".manifest"), TEXT(""))) { UE_LOG(LogInternationalizationExportCommandlet, Log, TEXT("The project name (%s) in the file (%s) did not match the target manifest project (%s)."), *POFilePath, *PortableObject.GetProjectName(), *ManifestName.Replace(TEXT(".manifest"), TEXT(""))); } const FString ManifestFileName = DestinationPath / ManifestName; TSharedPtr< FJsonObject > ManifestJsonObject = NULL; ManifestJsonObject = ReadJSONTextFile( ManifestFileName ); FJsonInternationalizationManifestSerializer ManifestSerializer; TSharedRef< FInternationalizationManifest > InternationalizationManifest = MakeShareable( new FInternationalizationManifest ); ManifestSerializer.DeserializeManifest( ManifestJsonObject.ToSharedRef(), InternationalizationManifest ); if( !FPaths::FileExists(ManifestFileName) ) { UE_LOG( LogInternationalizationExportCommandlet, Error, TEXT("Failed to find manifest %s."), *ManifestFileName); continue; } const FString DestinationCulturePath = DestinationPath / CultureName; const FString ArchiveFileName = DestinationCulturePath / ArchiveName; if( !FPaths::FileExists(ArchiveFileName) ) { UE_LOG( LogInternationalizationExportCommandlet, Error, TEXT("Failed to find destination archive %s."), *ArchiveFileName); continue; } TSharedPtr< FJsonObject > ArchiveJsonObject = NULL; ArchiveJsonObject = ReadJSONTextFile( ArchiveFileName ); FJsonInternationalizationArchiveSerializer ArchiveSerializer; TSharedRef< FInternationalizationArchive > InternationalizationArchive = MakeShareable( new FInternationalizationArchive ); ArchiveSerializer.DeserializeArchive( ArchiveJsonObject.ToSharedRef(), InternationalizationArchive ); bool bModifiedArchive = false; { for( auto EntryIter = PortableObject.GetEntriesIterator(); EntryIter; ++EntryIter ) { auto POEntry = *EntryIter; if( POEntry->MsgId.IsEmpty() || POEntry->MsgStr.Num() == 0 || POEntry->MsgStr[0].Trim().IsEmpty() ) { // We ignore the header entry or entries with no translation. continue; } // Some warning messages for data we don't process at the moment if( !POEntry->MsgIdPlural.IsEmpty() || POEntry->MsgStr.Num() > 1 ) { UE_LOG( LogInternationalizationExportCommandlet, Error, TEXT("Portable Object entry has plural form we did not process. File: %s MsgCtxt: %s MsgId: %s"), *POFilePath, *POEntry->MsgCtxt, *POEntry->MsgId ); } FString Key; FString Namespace; ParsePOMsgCtxtForIdentity(POEntry->MsgCtxt, Namespace, Key); const FString& SourceText = ConditionPoStringForArchive(POEntry->MsgId); const FString& Translation = ConditionPoStringForArchive(POEntry->MsgStr[0]); TSharedPtr<FLocMetadataObject> KeyMetaDataObject; // Get key metadata from the manifest, using the namespace and key. if (!Key.IsEmpty()) { // Find manifest entry by namespace for (auto ManifestEntryIterator = InternationalizationManifest->GetEntriesByContextIdIterator(); ManifestEntryIterator; ++ManifestEntryIterator) { const FString& ManifestEntryNamespace = ManifestEntryIterator->Key; const TSharedRef<FManifestEntry>& ManifestEntry = ManifestEntryIterator->Value; if (ManifestEntry->Namespace == Namespace) { FContext* const MatchingContext = ManifestEntry->Contexts.FindByPredicate([&](FContext& Context) -> bool { return Context.Key == Key; }); if (MatchingContext) { KeyMetaDataObject = MatchingContext->KeyMetadataObj; } } } } //@TODO: Take into account optional entries and entries that differ by keymetadata. Ex. Each optional entry needs a unique msgCtxt const TSharedPtr< FArchiveEntry > FoundEntry = InternationalizationArchive->FindEntryBySource( Namespace, SourceText, KeyMetaDataObject ); if( !FoundEntry.IsValid() ) { UE_LOG(LogInternationalizationExportCommandlet, Warning, TEXT("Could not find corresponding archive entry for PO entry. File: %s MsgCtxt: %s MsgId: %s"), *POFilePath, *POEntry->MsgCtxt, *POEntry->MsgId ); continue; } if( FoundEntry->Translation != Translation ) { FoundEntry->Translation = Translation; bModifiedArchive = true; } } } if( bModifiedArchive ) { TSharedRef<FJsonObject> FinalArchiveJsonObj = MakeShareable( new FJsonObject ); ArchiveSerializer.SerializeArchive( InternationalizationArchive, FinalArchiveJsonObj ); if( !WriteJSONToTextFile(FinalArchiveJsonObj, ArchiveFileName, SourceControlInfo ) ) { UE_LOG( LogInternationalizationExportCommandlet, Error, TEXT("Failed to write archive to %s."), *ArchiveFileName ); return false; } } } return true; }
int32 URepairLocalizationDataCommandlet::Main(const FString& Params) { FInternationalization& I18N = FInternationalization::Get(); // Parse command line. TArray<FString> Tokens; TArray<FString> Switches; TMap<FString, FString> 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(LogRepairLocalizationDataCommandlet, 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(LogRepairLocalizationDataCommandlet, Error, TEXT("No config section specified.")); return -1; } // Get destination path. FString DestinationPath; if( !GetPathFromConfig( *SectionName, TEXT("DestinationPath"), DestinationPath, GatherTextConfigPath ) ) { UE_LOG( LogRepairLocalizationDataCommandlet, Error, TEXT("No destination path specified.") ); return -1; } // Get manifest name. FString ManifestName; if( !GetStringFromConfig( *SectionName, TEXT("ManifestName"), ManifestName, GatherTextConfigPath ) ) { UE_LOG( LogRepairLocalizationDataCommandlet, Error, TEXT("No manifest name specified.") ); return -1; } // Get archive name. FString ArchiveName; if( !( GetStringFromConfig(* SectionName, TEXT("ArchiveName"), ArchiveName, GatherTextConfigPath ) ) ) { UE_LOG(LogRepairLocalizationDataCommandlet, Error, TEXT("No archive name specified.")); return -1; } // Get cultures to generate. TArray<FString> CulturesToGenerate; GetStringArrayFromConfig(*SectionName, TEXT("CulturesToGenerate"), CulturesToGenerate, GatherTextConfigPath); if( CulturesToGenerate.Num() == 0 ) { UE_LOG(LogRepairLocalizationDataCommandlet, Error, TEXT("No cultures specified for generation.")); return -1; } for(int32 i = 0; i < CulturesToGenerate.Num(); ++i) { if( I18N.GetCulture( CulturesToGenerate[i] ).IsValid() ) { UE_LOG(LogRepairLocalizationDataCommandlet, Verbose, TEXT("Specified culture is not a valid runtime culture, but may be a valid base language: %s"), *(CulturesToGenerate[i]) ); } } ////////////////////////////////////////////////////////////////////////// // Read the damaged manifest. const FString ManifestFilePath = DestinationPath / ManifestName; const TSharedPtr<FJsonObject> ManifestJsonObject = ReadJSONTextFile( ManifestFilePath ); if( !ManifestJsonObject.IsValid() ) { UE_LOG(LogRepairLocalizationDataCommandlet, Error, TEXT("Could not read manifest file %s."), *ManifestFilePath); return -1; } FJsonInternationalizationManifestSerializer ManifestSerializer; const TSharedRef< FInternationalizationManifest > InternationalizationManifest = MakeShareable( new FInternationalizationManifest ); ManifestSerializer.DeserializeManifest( ManifestJsonObject.ToSharedRef(), InternationalizationManifest ); // Read the damaged archives. TArray< TSharedRef<FInternationalizationArchive> > InternationalizationArchives; TArray<FString> ArchiveCultures; for(int32 Culture = 0; Culture < CulturesToGenerate.Num(); Culture++) { // Read in any existing archive for this culture. const FString CulturePath = DestinationPath / CulturesToGenerate[Culture]; const FString ArchiveFilePath = CulturePath / ArchiveName; if( FPaths::FileExists(ArchiveFilePath) ) { TSharedPtr< FJsonObject > ArchiveJsonObject = ReadJSONTextFile( ArchiveFilePath ); if( !ArchiveJsonObject.IsValid() ) { UE_LOG(LogRepairLocalizationDataCommandlet, Error, TEXT("Could not read archive file %s."), *ArchiveFilePath); return -1; } FJsonInternationalizationArchiveSerializer ArchiveSerializer; const TSharedRef< FInternationalizationArchive > InternationalizationArchive = MakeShareable( new FInternationalizationArchive ); ArchiveSerializer.DeserializeArchive( ArchiveJsonObject.ToSharedRef(), InternationalizationArchive ); InternationalizationArchives.Add(InternationalizationArchive); ArchiveCultures.Add(CulturesToGenerate[Culture]); } } // Repair. RepairManifestAndArchives(InternationalizationManifest, InternationalizationArchives); // Write the repaired manifests. TSharedRef< FJsonObject > OutputManifestJsonObj = MakeShareable( new FJsonObject ); ManifestSerializer.SerializeManifest( InternationalizationManifest, OutputManifestJsonObj ); if (!WriteJSONToTextFile( OutputManifestJsonObj, DestinationPath / ManifestName, SourceControlInfo )) { UE_LOG( LogRepairLocalizationDataCommandlet, Error,TEXT("Failed to write manifest to %s."), *DestinationPath ); return -1; } // Write the repaired archives. { int Index = 0; for (const TSharedRef<FInternationalizationArchive>& InternationalizationArchive : InternationalizationArchives) { TSharedRef< FJsonObject > OutputArchiveJsonObj = MakeShareable( new FJsonObject ); FJsonInternationalizationArchiveSerializer ArchiveSerializer; ArchiveSerializer.SerializeArchive( InternationalizationArchive, OutputArchiveJsonObj ); int32 Culture = 0; if (!WriteJSONToTextFile( OutputArchiveJsonObj, (DestinationPath / *ArchiveCultures[Index] / ArchiveName), SourceControlInfo )) { UE_LOG( LogRepairLocalizationDataCommandlet, Error,TEXT("Failed to write archive to %s."), *DestinationPath ); return -1; } ++Index; } } return 0; }