2014-12-07 19:09:38 -05:00
// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
2014-03-14 14:13:41 -04:00
# include "TranslationEditorPrivatePCH.h"
# include "TranslationEditor.h"
# include "WorkspaceMenuStructureModule.h"
2014-04-23 18:47:10 -04:00
# include "TranslationUnit.h"
2014-03-14 14:13:41 -04:00
# include "ISourceControlModule.h"
# include "MessageLog.h"
2014-04-23 17:49:26 -04:00
# include "TextLocalizationManager.h"
2014-09-25 18:03:04 -04:00
# include "JsonInternationalizationArchiveSerializer.h"
# include "JsonInternationalizationManifestSerializer.h"
2014-10-14 22:50:06 -04:00
# include "SNotificationList.h"
# include "NotificationManager.h"
2014-09-25 18:03:04 -04:00
2014-03-14 14:13:41 -04:00
DEFINE_LOG_CATEGORY_STATIC ( LogTranslationEditor , Log , All ) ;
# define LOCTEXT_NAMESPACE "TranslationDataManager"
2014-09-25 18:03:04 -04:00
2014-04-23 17:49:26 -04:00
FTranslationDataManager : : FTranslationDataManager ( const FString & InManifestFilePath , const FString & InArchiveFilePath )
: ManifestFilePath ( InManifestFilePath )
, ArchiveFilePath ( InArchiveFilePath )
2014-03-14 14:13:41 -04:00
{
GWarn - > BeginSlowTask ( LOCTEXT ( " LoadingTranslationData " , " Loading Translation Data... " ) , true ) ;
2014-04-23 18:48:34 -04:00
TArray < UTranslationUnit * > TranslationUnits ;
2014-03-14 14:13:41 -04:00
ManifestAtHeadRevisionPtr = ReadManifest ( ManifestFilePath ) ;
if ( ManifestAtHeadRevisionPtr . IsValid ( ) )
{
TSharedRef < FInternationalizationManifest > ManifestAtHeadRevision = ManifestAtHeadRevisionPtr . ToSharedRef ( ) ;
int32 ManifestEntriesCount = ManifestAtHeadRevision - > GetNumEntriesBySourceText ( ) ;
if ( ManifestEntriesCount < 1 )
{
FFormatNamedArguments Arguments ;
Arguments . Add ( TEXT ( " ManifestFilePath " ) , FText : : FromString ( ManifestFilePath ) ) ;
Arguments . Add ( TEXT ( " ManifestEntriesCount " ) , FText : : AsNumber ( ManifestEntriesCount ) ) ;
FMessageLog TranslationEditorMessageLog ( " TranslationEditor " ) ;
TranslationEditorMessageLog . Error ( FText : : Format ( LOCTEXT ( " CurrentManifestEmpty " , " Most current translation manifest ({ManifestFilePath}) has {ManifestEntriesCount} entries. " ) , Arguments ) ) ;
TranslationEditorMessageLog . Notify ( LOCTEXT ( " TranslationLoadError " , " Error Loading Translations! " ) ) ;
TranslationEditorMessageLog . Open ( EMessageSeverity : : Error ) ;
}
2014-04-23 19:14:07 -04:00
ArchivePtr = ReadArchive ( ) ;
2014-03-14 14:13:41 -04:00
if ( ArchivePtr . IsValid ( ) )
{
int32 NumManifestEntriesParsed = 0 ;
GWarn - > BeginSlowTask ( LOCTEXT ( " LoadingCurrentManifest " , " Loading Entries from Current Translation Manifest... " ) , true ) ;
// Get all manifest entries by source text (same source text in multiple contexts will only show up once)
for ( auto ManifestItr = ManifestAtHeadRevision - > GetEntriesBySourceTextIterator ( ) ; ManifestItr ; + + ManifestItr , + + NumManifestEntriesParsed )
{
GWarn - > StatusUpdate ( NumManifestEntriesParsed , ManifestEntriesCount , FText : : Format ( LOCTEXT ( " LoadingCurrentManifestEntries " , " Loading Entry {0} of {1} from Current Translation Manifest... " ) , FText : : AsNumber ( NumManifestEntriesParsed ) , FText : : AsNumber ( ManifestEntriesCount ) ) ) ;
const TSharedRef < FManifestEntry > ManifestEntry = ManifestItr . Value ( ) ;
2014-04-23 18:47:10 -04:00
UTranslationUnit * TranslationUnit = NewObject < UTranslationUnit > ( ) ;
2014-09-25 18:03:04 -04:00
check ( TranslationUnit ! = nullptr ) ;
2014-04-23 18:47:10 -04:00
// We want Undo/Redo support
TranslationUnit - > SetFlags ( RF_Transactional ) ;
TranslationUnit - > HasBeenReviewed = false ;
2014-09-17 16:37:28 -04:00
TranslationUnit - > Source = ManifestEntry - > Source . Text ;
2014-04-23 18:47:10 -04:00
TranslationUnit - > Namespace = ManifestEntry - > Namespace ;
2014-03-14 14:13:41 -04:00
for ( auto ContextIter ( ManifestEntry - > Contexts . CreateConstIterator ( ) ) ; ContextIter ; + + ContextIter )
{
FTranslationContextInfo ContextInfo ;
const FContext & AContext = * ContextIter ;
ContextInfo . Context = AContext . SourceLocation ;
ContextInfo . Key = AContext . Key ;
2014-04-23 18:47:10 -04:00
TranslationUnit - > Contexts . Add ( ContextInfo ) ;
2014-03-14 14:13:41 -04:00
}
2014-04-23 18:48:34 -04:00
TranslationUnits . Add ( TranslationUnit ) ;
2014-03-14 14:13:41 -04:00
}
GWarn - > EndSlowTask ( ) ;
2014-04-23 19:14:07 -04:00
LoadFromArchive ( TranslationUnits ) ;
2014-03-14 14:13:41 -04:00
}
else // ArchivePtr.IsValid() is false
{
FFormatNamedArguments Arguments ;
Arguments . Add ( TEXT ( " ArchiveFilePath " ) , FText : : FromString ( ArchiveFilePath ) ) ;
FMessageLog TranslationEditorMessageLog ( " TranslationEditor " ) ;
TranslationEditorMessageLog . Error ( FText : : Format ( LOCTEXT ( " FailedToLoadCurrentArchive " , " Failed to load most current translation archive ({ArchiveFilePath}), unable to load translations. " ) , Arguments ) ) ;
TranslationEditorMessageLog . Notify ( LOCTEXT ( " TranslationLoadError " , " Error Loading Translations! " ) ) ;
TranslationEditorMessageLog . Open ( EMessageSeverity : : Error ) ;
}
}
else // ManifestAtHeadRevisionPtr.IsValid() is false
{
FFormatNamedArguments Arguments ;
Arguments . Add ( TEXT ( " ManifestFilePath " ) , FText : : FromString ( ManifestFilePath ) ) ;
FMessageLog TranslationEditorMessageLog ( " TranslationEditor " ) ;
TranslationEditorMessageLog . Error ( FText : : Format ( LOCTEXT ( " FailedToLoadCurrentManifest " , " Failed to load most current translation manifest ({ManifestFilePath}), unable to load translations. " ) , Arguments ) ) ;
TranslationEditorMessageLog . Notify ( LOCTEXT ( " TranslationLoadError " , " Error Loading Translations! " ) ) ;
TranslationEditorMessageLog . Open ( EMessageSeverity : : Error ) ;
}
GWarn - > EndSlowTask ( ) ;
}
2014-05-01 23:21:37 -04:00
FTranslationDataManager : : ~ FTranslationDataManager ( )
{
RemoveTranslationUnitArrayfromRoot ( AllTranslations ) ; // Re-enable garbage collection for all current UTranslationDataObjects
}
2014-04-23 17:49:26 -04:00
TSharedPtr < FInternationalizationManifest > FTranslationDataManager : : ReadManifest ( const FString & ManifestFilePathToRead )
2014-03-14 14:13:41 -04:00
{
2014-04-23 17:49:26 -04:00
TSharedPtr < FJsonObject > ManifestJsonObject = ReadJSONTextFile ( ManifestFilePathToRead ) ;
2014-03-14 14:13:41 -04:00
if ( ! ManifestJsonObject . IsValid ( ) )
{
2014-04-23 17:49:26 -04:00
UE_LOG ( LogTranslationEditor , Error , TEXT ( " Could not read manifest file %s. " ) , * ManifestFilePathToRead ) ;
2014-03-14 14:13:41 -04:00
return TSharedPtr < FInternationalizationManifest > ( ) ;
}
TSharedRef < FInternationalizationManifest > InternationalizationManifest = MakeShareable ( new FInternationalizationManifest ) ;
ManifestSerializer . DeserializeManifest ( ManifestJsonObject . ToSharedRef ( ) , InternationalizationManifest ) ;
return InternationalizationManifest ;
}
2014-04-23 19:14:07 -04:00
TSharedPtr < FInternationalizationArchive > FTranslationDataManager : : ReadArchive ( )
2014-03-14 14:13:41 -04:00
{
// Read in any existing archive for this culture.
TSharedPtr < FJsonObject > ArchiveJsonObject = ReadJSONTextFile ( ArchiveFilePath ) ;
if ( ! ArchiveJsonObject . IsValid ( ) )
{
UE_LOG ( LogTranslationEditor , Error , TEXT ( " Could not read archive file %s. " ) , * ArchiveFilePath ) ;
2014-09-25 18:03:04 -04:00
return nullptr ;
2014-03-14 14:13:41 -04:00
}
TSharedRef < FInternationalizationArchive > InternationalizationArchive = MakeShareable ( new FInternationalizationArchive ) ;
ArchiveSerializer . DeserializeArchive ( ArchiveJsonObject . ToSharedRef ( ) , InternationalizationArchive ) ;
return InternationalizationArchive ;
}
TSharedPtr < FJsonObject > FTranslationDataManager : : ReadJSONTextFile ( const FString & InFilePath )
{
//read in file as string
FString FileContents ;
if ( ! FFileHelper : : LoadFileToString ( FileContents , * InFilePath ) )
{
UE_LOG ( LogTranslationEditor , Error , TEXT ( " Failed to load file %s. " ) , * InFilePath ) ;
2014-09-25 18:03:04 -04:00
return nullptr ;
2014-03-14 14:13:41 -04:00
}
//parse as JSON
TSharedPtr < FJsonObject > JSONObject ;
TSharedRef < TJsonReader < > > Reader = TJsonReaderFactory < > : : Create ( FileContents ) ;
if ( ! FJsonSerializer : : Deserialize ( Reader , JSONObject ) | | ! JSONObject . IsValid ( ) )
{
UE_LOG ( LogTranslationEditor , Error , TEXT ( " Invalid JSON in file %s. " ) , * InFilePath ) ;
2014-09-25 18:03:04 -04:00
return nullptr ;
2014-03-14 14:13:41 -04:00
}
return JSONObject ;
}
2014-04-23 19:14:07 -04:00
bool FTranslationDataManager : : WriteTranslationData ( bool bForceWrite /*= false*/ )
2014-03-14 14:13:41 -04:00
{
check ( ArchivePtr . IsValid ( ) ) ;
TSharedRef < FInternationalizationArchive > Archive = ArchivePtr . ToSharedRef ( ) ;
bool bNeedsWrite = false ;
2014-04-23 18:47:10 -04:00
for ( UTranslationUnit * TranslationUnit : Untranslated )
2014-03-14 14:13:41 -04:00
{
2014-09-25 18:03:04 -04:00
if ( TranslationUnit ! = nullptr )
2014-03-14 14:13:41 -04:00
{
2014-05-01 00:16:11 -04:00
const FLocItem SearchSource ( TranslationUnit - > Source ) ;
2014-09-25 18:03:04 -04:00
FString OldTranslation = Archive - > FindEntryBySource ( TranslationUnit - > Namespace , SearchSource , nullptr ) - > Translation . Text ;
2014-09-17 16:37:28 -04:00
FString TranslationToWrite = TranslationUnit - > Translation ;
2014-05-01 00:16:11 -04:00
if ( ! TranslationToWrite . Equals ( OldTranslation ) )
{
2014-09-25 18:03:04 -04:00
Archive - > SetTranslation ( TranslationUnit - > Namespace , TranslationUnit - > Source , TranslationToWrite , nullptr ) ;
2014-05-01 00:16:11 -04:00
bNeedsWrite = true ;
}
2014-03-14 14:13:41 -04:00
}
}
2014-04-23 18:47:10 -04:00
for ( UTranslationUnit * TranslationUnit : Review )
2014-03-14 14:13:41 -04:00
{
2014-09-25 18:03:04 -04:00
if ( TranslationUnit ! = nullptr )
2014-03-14 14:13:41 -04:00
{
2014-05-01 00:16:11 -04:00
const FLocItem SearchSource ( TranslationUnit - > Source ) ;
2014-09-25 18:03:04 -04:00
FString OldTranslation = Archive - > FindEntryBySource ( TranslationUnit - > Namespace , SearchSource , nullptr ) - > Translation . Text ;
2014-09-17 16:37:28 -04:00
FString TranslationToWrite = TranslationUnit - > Translation ;
2014-05-01 00:16:11 -04:00
if ( TranslationUnit - > HasBeenReviewed & & ! TranslationToWrite . Equals ( OldTranslation ) )
{
2014-09-25 18:03:04 -04:00
Archive - > SetTranslation ( TranslationUnit - > Namespace , TranslationUnit - > Source , TranslationToWrite , nullptr ) ;
2014-05-01 00:16:11 -04:00
bNeedsWrite = true ;
}
2014-03-14 14:13:41 -04:00
}
}
2014-04-23 18:47:10 -04:00
for ( UTranslationUnit * TranslationUnit : Complete )
2014-03-14 14:13:41 -04:00
{
2014-09-25 18:03:04 -04:00
if ( TranslationUnit ! = nullptr )
2014-03-14 14:13:41 -04:00
{
2014-05-01 00:16:11 -04:00
const FLocItem SearchSource ( TranslationUnit - > Source ) ;
2014-09-25 18:03:04 -04:00
FString OldTranslation = Archive - > FindEntryBySource ( TranslationUnit - > Namespace , SearchSource , nullptr ) - > Translation . Text ;
2014-09-17 16:37:28 -04:00
FString TranslationToWrite = TranslationUnit - > Translation ;
2014-05-01 00:16:11 -04:00
if ( ! TranslationToWrite . Equals ( OldTranslation ) )
{
2014-09-25 18:03:04 -04:00
Archive - > SetTranslation ( TranslationUnit - > Namespace , TranslationUnit - > Source , TranslationToWrite , nullptr ) ;
2014-05-01 00:16:11 -04:00
bNeedsWrite = true ;
}
2014-03-14 14:13:41 -04:00
}
}
2014-04-23 19:14:07 -04:00
bool bSuccess = true ;
if ( bForceWrite | | bNeedsWrite )
2014-03-14 14:13:41 -04:00
{
TSharedRef < FJsonObject > FinalArchiveJsonObj = MakeShareable ( new FJsonObject ) ;
ArchiveSerializer . SerializeArchive ( Archive , FinalArchiveJsonObj ) ;
2014-04-23 19:14:07 -04:00
bSuccess = WriteJSONToTextFile ( FinalArchiveJsonObj , ArchiveFilePath ) ;
2014-03-14 14:13:41 -04:00
}
2014-04-23 19:14:07 -04:00
return bSuccess ;
2014-03-14 14:13:41 -04:00
}
bool FTranslationDataManager : : WriteJSONToTextFile ( TSharedRef < FJsonObject > & Output , const FString & Filename )
{
bool CheckoutAndSaveWasSuccessful = true ;
bool bPreviouslyCheckedOut = false ;
// If the user specified a reference file - write the entries read from code to a ref file
if ( ! Filename . IsEmpty ( ) )
{
2015-03-18 06:03:00 -04:00
// If source control is enabled, try to check out the file. Otherwise just try to write it
if ( ISourceControlModule : : Get ( ) . IsEnabled ( ) )
2014-03-14 14:13:41 -04:00
{
2015-03-18 06:03:00 -04:00
// Already checked out?
if ( CheckedOutFiles . Contains ( Filename ) )
{
bPreviouslyCheckedOut = true ;
}
else if ( ! SourceControlHelpers : : CheckOutFile ( Filename ) )
{
FFormatNamedArguments Arguments ;
Arguments . Add ( TEXT ( " Filename " ) , FText : : FromString ( Filename ) ) ;
// Use Source Control Message Log here because there might be other useful information in that log for the user.
FMessageLog SourceControlMessageLog ( " SourceControl " ) ;
SourceControlMessageLog . Error ( FText : : Format ( LOCTEXT ( " CheckoutFailed " , " Check out of file '{Filename}' failed. " ) , Arguments ) ) ;
SourceControlMessageLog . Notify ( LOCTEXT ( " TranslationArchiveCheckoutFailed " , " Failed to Check Out Translation Archive! " ) ) ;
SourceControlMessageLog . Open ( EMessageSeverity : : Error ) ;
CheckoutAndSaveWasSuccessful = false ;
}
else
{
CheckedOutFiles . Add ( Filename ) ;
}
2014-03-14 14:13:41 -04:00
}
if ( CheckoutAndSaveWasSuccessful )
{
//Print the JSON data out to the ref file.
FString OutputString ;
TSharedRef < TJsonWriter < > > Writer = TJsonWriterFactory < > : : Create ( & OutputString ) ;
FJsonSerializer : : Serialize ( Output , Writer ) ;
if ( ! FFileHelper : : SaveStringToFile ( OutputString , * Filename , FFileHelper : : EEncodingOptions : : ForceUnicode ) )
{
// If we already checked out the file, but cannot write it, perhaps the user checked it in via perforce, so try to check it out again
if ( bPreviouslyCheckedOut )
{
bPreviouslyCheckedOut = false ;
if ( ! SourceControlHelpers : : CheckOutFile ( Filename ) )
{
FFormatNamedArguments Arguments ;
Arguments . Add ( TEXT ( " Filename " ) , FText : : FromString ( Filename ) ) ;
// Use Source Control Message Log here because there might be other useful information in that log for the user.
FMessageLog SourceControlMessageLog ( " SourceControl " ) ;
SourceControlMessageLog . Error ( FText : : Format ( LOCTEXT ( " CheckoutFailed " , " Check out of file '{Filename}' failed. " ) , Arguments ) ) ;
SourceControlMessageLog . Notify ( LOCTEXT ( " TranslationArchiveCheckoutFailed " , " Failed to Check Out Translation Archive! " ) ) ;
SourceControlMessageLog . Open ( EMessageSeverity : : Error ) ;
CheckoutAndSaveWasSuccessful = false ;
CheckedOutFiles . Remove ( Filename ) ;
}
}
FFormatNamedArguments Arguments ;
Arguments . Add ( TEXT ( " Filename " ) , FText : : FromString ( Filename ) ) ;
FMessageLog TranslationEditorMessageLog ( " TranslationEditor " ) ;
TranslationEditorMessageLog . Error ( FText : : Format ( LOCTEXT ( " WriteFileFailed " , " Failed to write localization entries to file '{Filename}'. " ) , Arguments ) ) ;
TranslationEditorMessageLog . Notify ( LOCTEXT ( " FileWriteFailed " , " Failed to Write Translations to File! " ) ) ;
TranslationEditorMessageLog . Open ( EMessageSeverity : : Error ) ;
CheckoutAndSaveWasSuccessful = false ;
}
}
}
else
{
CheckoutAndSaveWasSuccessful = false ;
}
// If this is the first time, let the user know the file was checked out
if ( ! bPreviouslyCheckedOut & & CheckoutAndSaveWasSuccessful )
{
struct Local
{
/**
* Called by our notification ' s hyperlink to open the Source Control message log
*/
static void OpenSourceControlMessageLog ( )
{
FMessageLog ( " SourceControl " ) . Open ( ) ;
}
} ;
FFormatNamedArguments Arguments ;
Arguments . Add ( TEXT ( " Filename " ) , FText : : FromString ( Filename ) ) ;
// Make a note in the Source Control log, including a note to check in the file later via source control application
FMessageLog TranslationEditorMessageLog ( " SourceControl " ) ;
TranslationEditorMessageLog . Info ( FText : : Format ( LOCTEXT ( " TranslationArchiveCheckedOut " , " Successfully checked out and saved translation archive '{Filename}'. Please check-in this file later via your source control application. " ) , Arguments ) ) ;
// Display notification that save was successful, along with a link to the Source Control log so the user can see the above message.
FNotificationInfo Info ( LOCTEXT ( " ArchiveCheckedOut " , " Translation Archive Successfully Checked Out and Saved. " ) ) ;
Info . ExpireDuration = 5 ;
Info . Hyperlink = FSimpleDelegate : : CreateStatic ( & Local : : OpenSourceControlMessageLog ) ;
2014-06-17 12:05:50 -04:00
Info . HyperlinkText = LOCTEXT ( " ShowMessageLogHyperlink " , " Show Message Log " ) ;
2014-03-14 14:13:41 -04:00
Info . bFireAndForget = true ;
Info . bUseSuccessFailIcons = true ;
Info . Image = FEditorStyle : : GetBrush ( TEXT ( " NotificationList.SuccessImage " ) ) ;
FSlateNotificationManager : : Get ( ) . AddNotification ( Info ) ;
}
return CheckoutAndSaveWasSuccessful ;
}
2014-11-18 03:20:09 -05:00
void FTranslationDataManager : : GetHistoryForTranslationUnits ( )
2014-03-14 14:13:41 -04:00
{
GWarn - > BeginSlowTask ( LOCTEXT ( " LoadingSourceControlHistory " , " Loading Translation History from Source Control... " ) , true ) ;
2014-11-18 03:20:09 -05:00
TArray < UTranslationUnit * > & TranslationUnits = AllTranslations ;
const FString & InManifestFilePath = ManifestFilePath ;
// Unload any previous history information, going to retrieve it all again.
UnloadHistoryInformation ( ) ;
2014-04-23 17:47:40 -04:00
// Force history update
2014-03-14 14:13:41 -04:00
ISourceControlProvider & SourceControlProvider = ISourceControlModule : : Get ( ) . GetProvider ( ) ;
TSharedRef < FUpdateStatus , ESPMode : : ThreadSafe > UpdateStatusOperation = ISourceControlOperation : : Create < FUpdateStatus > ( ) ;
UpdateStatusOperation - > SetUpdateHistory ( true ) ;
2014-05-29 17:14:20 -04:00
ECommandResult : : Type Result = SourceControlProvider . Execute ( UpdateStatusOperation , InManifestFilePath ) ;
2014-04-23 17:47:40 -04:00
bool bGetHistoryFromSourceControlSucceeded = Result = = ECommandResult : : Succeeded ;
// Now we can get information about the file's history from the source control state, retrieve that
TArray < FString > Files ;
TArray < TSharedRef < ISourceControlState , ESPMode : : ThreadSafe > > States ;
2014-05-29 17:14:20 -04:00
Files . Add ( InManifestFilePath ) ;
2014-04-23 17:47:40 -04:00
Result = SourceControlProvider . GetState ( Files , States , EStateCacheUsage : : ForceUpdate ) ;
bGetHistoryFromSourceControlSucceeded = bGetHistoryFromSourceControlSucceeded & & ( Result = = ECommandResult : : Succeeded ) ;
2014-04-23 17:49:15 -04:00
FSourceControlStatePtr SourceControlState ;
if ( States . Num ( ) = = 1 )
{
SourceControlState = States [ 0 ] ;
}
2014-04-23 17:47:40 -04:00
// If all the source control operations went ok, continue
if ( bGetHistoryFromSourceControlSucceeded & & SourceControlState . IsValid ( ) )
2014-03-14 14:13:41 -04:00
{
int32 HistorySize = SourceControlState - > GetHistorySize ( ) ;
2014-04-02 18:09:23 -04:00
for ( int HistoryItemIndex = HistorySize - 1 ; HistoryItemIndex > = 0 ; - - HistoryItemIndex )
2014-03-14 14:13:41 -04:00
{
2014-04-02 18:09:23 -04:00
GWarn - > StatusUpdate ( HistorySize - HistoryItemIndex , HistorySize , FText : : Format ( LOCTEXT ( " LoadingOldManifestRevisionNumber " , " Loading Translation History from Manifest Revision {0} of {1} from Source Control... " ) , FText : : AsNumber ( HistorySize - HistoryItemIndex ) , FText : : AsNumber ( HistorySize ) ) ) ;
2014-03-14 14:13:41 -04:00
TSharedPtr < ISourceControlRevision , ESPMode : : ThreadSafe > Revision = SourceControlState - > GetHistoryItem ( HistoryItemIndex ) ;
if ( Revision . IsValid ( ) )
{
2014-05-29 17:14:20 -04:00
FString ManifestFullPath = FPaths : : ConvertRelativePathToFull ( InManifestFilePath ) ;
2014-04-02 18:09:23 -04:00
FString EngineFullPath = FPaths : : ConvertRelativePathToFull ( FPaths : : EngineContentDir ( ) ) ;
bool IsEngineManifest = false ;
if ( ManifestFullPath . StartsWith ( EngineFullPath ) )
2014-03-14 14:13:41 -04:00
{
2014-04-02 18:09:23 -04:00
IsEngineManifest = true ;
}
FString ProjectName ;
FString SavedDir ; // Store these cached translation history files in the saved directory
if ( IsEngineManifest )
{
ProjectName = " Engine " ;
SavedDir = FPaths : : EngineSavedDir ( ) ;
}
else
{
2014-10-14 10:31:43 -04:00
ProjectName = FApp : : GetGameName ( ) ;
2014-04-02 18:09:23 -04:00
SavedDir = FPaths : : GameSavedDir ( ) ;
}
2014-05-29 17:14:20 -04:00
FString TempFileName = SavedDir / " CachedTranslationHistory " / " UE4-Manifest- " + ProjectName + " - " + FPaths : : GetBaseFilename ( InManifestFilePath ) + " -Rev- " + FString : : FromInt ( Revision - > GetRevisionNumber ( ) ) ;
2014-04-02 18:09:23 -04:00
if ( ! FPaths : : FileExists ( TempFileName ) ) // Don't bother syncing again if we already have this manifest version cached locally
{
Revision - > Get ( TempFileName ) ;
}
TSharedPtr < FInternationalizationManifest > OldManifestPtr = ReadManifest ( TempFileName ) ;
if ( OldManifestPtr . IsValid ( ) ) // There may be corrupt manifests in the history, so ignore them.
{
TSharedRef < FInternationalizationManifest > OldManifest = OldManifestPtr . ToSharedRef ( ) ;
2014-04-23 18:47:10 -04:00
for ( UTranslationUnit * TranslationUnit : TranslationUnits )
2014-03-14 14:13:41 -04:00
{
2014-09-25 18:03:04 -04:00
if ( TranslationUnit ! = nullptr & & TranslationUnit - > Contexts . Num ( ) > 0 )
2014-03-14 14:13:41 -04:00
{
2014-04-23 18:47:10 -04:00
for ( FTranslationContextInfo & ContextInfo : TranslationUnit - > Contexts )
2014-03-14 14:13:41 -04:00
{
2014-04-02 18:09:23 -04:00
FString PreviousSourceText = " " ;
// If we already have history, then compare against the newest history so far
if ( ContextInfo . Changes . Num ( ) > 0 )
2014-03-14 14:13:41 -04:00
{
2014-04-02 18:09:23 -04:00
PreviousSourceText = ContextInfo . Changes [ 0 ] . Source ;
}
2014-03-14 14:13:41 -04:00
2014-04-02 18:09:23 -04:00
FContext SearchContext ;
SearchContext . Key = ContextInfo . Key ;
2014-04-23 18:47:10 -04:00
TSharedPtr < FManifestEntry > OldManifestEntryPtr = OldManifest - > FindEntryByContext ( TranslationUnit - > Namespace , SearchContext ) ;
2014-04-02 18:09:23 -04:00
if ( ! OldManifestEntryPtr . IsValid ( ) )
{
// If this version of the manifest didn't know anything about this string, move onto the next
continue ;
}
2014-03-14 14:13:41 -04:00
2014-04-02 18:09:23 -04:00
// Always add first instance of this string, and then add any versions that changed since
2014-09-17 16:37:28 -04:00
if ( ContextInfo . Changes . Num ( ) = = 0 | | ! OldManifestEntryPtr - > Source . Text . Equals ( PreviousSourceText ) )
2014-04-02 18:09:23 -04:00
{
2014-09-25 18:03:04 -04:00
TSharedPtr < FArchiveEntry > OldArchiveEntry = ArchivePtr - > FindEntryBySource ( OldManifestEntryPtr - > Namespace , OldManifestEntryPtr - > Source , nullptr ) ;
2014-04-02 18:09:23 -04:00
if ( OldArchiveEntry . IsValid ( ) )
2014-03-14 14:13:41 -04:00
{
2014-04-02 18:09:23 -04:00
FTranslationChange Change ;
2014-09-17 16:37:28 -04:00
Change . Source = OldManifestEntryPtr - > Source . Text ;
Change . Translation = OldArchiveEntry - > Translation . Text ;
2014-04-02 18:09:23 -04:00
Change . DateAndTime = Revision - > GetDate ( ) ;
Change . Version = FString : : FromInt ( Revision - > GetRevisionNumber ( ) ) ;
ContextInfo . Changes . Insert ( Change , 0 ) ;
2014-03-14 14:13:41 -04:00
}
}
}
}
}
2014-04-02 18:09:23 -04:00
}
else // OldManifestPtr.IsValid() is false
{
FFormatNamedArguments Arguments ;
2014-05-29 17:14:20 -04:00
Arguments . Add ( TEXT ( " ManifestFilePath " ) , FText : : FromString ( InManifestFilePath ) ) ;
2014-04-02 18:09:23 -04:00
Arguments . Add ( TEXT ( " ManifestRevisionNumber " ) , FText : : AsNumber ( Revision - > GetRevisionNumber ( ) ) ) ;
FMessageLog TranslationEditorMessageLog ( " TranslationEditor " ) ;
TranslationEditorMessageLog . Warning ( FText : : Format ( LOCTEXT ( " PreviousManifestCorrupt " , " Previous revision {ManifestRevisionNumber} of {ManifestFilePath} failed to load correctly. Ignoring. " ) , Arguments ) ) ;
2014-03-14 14:13:41 -04:00
}
}
}
}
2014-04-23 17:47:40 -04:00
// If source control operations failed, display error message
else // (bGetHistoryFromSourceControlSucceeded && SourceControlState.IsValid()) is false
2014-03-14 14:13:41 -04:00
{
FFormatNamedArguments Arguments ;
2014-05-29 17:14:20 -04:00
Arguments . Add ( TEXT ( " ManifestFilePath " ) , FText : : FromString ( InManifestFilePath ) ) ;
2014-03-14 14:13:41 -04:00
FMessageLog TranslationEditorMessageLog ( " SourceControl " ) ;
TranslationEditorMessageLog . Warning ( FText : : Format ( LOCTEXT ( " SourceControlStateQueryFailed " , " Failed to query source control state of file {ManifestFilePath}. " ) , Arguments ) ) ;
TranslationEditorMessageLog . Notify ( LOCTEXT ( " RetrieveTranslationHistoryFailed " , " Unable to Retrieve Translation History from Source Control! " ) ) ;
}
2014-11-18 03:20:09 -05:00
// Go though all translation units
for ( int32 CurrentTranslationUnitIndex = 0 ; CurrentTranslationUnitIndex < TranslationUnits . Num ( ) ; + + CurrentTranslationUnitIndex )
{
UTranslationUnit * TranslationUnit = TranslationUnits [ CurrentTranslationUnitIndex ] ;
if ( TranslationUnit ! = nullptr )
{
if ( TranslationUnit - > Translation . IsEmpty ( ) )
{
bool bHasTranslationHistory = false ;
int32 MostRecentNonNullTranslationIndex = - 1 ;
int32 ContextForRecentTranslation = - 1 ;
// Check all contexts for history
for ( int32 ContextIndex = 0 ; ContextIndex < TranslationUnit - > Contexts . Num ( ) ; + + ContextIndex )
{
for ( int32 ChangeIndex = 0 ; ChangeIndex < TranslationUnit - > Contexts [ ContextIndex ] . Changes . Num ( ) ; + + ChangeIndex )
{
if ( ! ( TranslationUnit - > Contexts [ ContextIndex ] . Changes [ ChangeIndex ] . Translation . IsEmpty ( ) ) )
{
bHasTranslationHistory = true ;
MostRecentNonNullTranslationIndex = ChangeIndex ;
ContextForRecentTranslation = ContextIndex ;
break ;
}
}
if ( bHasTranslationHistory )
{
break ;
}
}
// If we have history, but current translation is empty, this goes in the Needs Review tab
if ( bHasTranslationHistory )
{
// Offer the most recent translation (for the first context in the list) as a suggestion or starting point (not saved unless user checks "Has Been Reviewed")
TranslationUnit - > Translation = TranslationUnit - > Contexts [ ContextForRecentTranslation ] . Changes [ MostRecentNonNullTranslationIndex ] . Translation ;
TranslationUnit - > HasBeenReviewed = false ;
// Move from Untranslated to review
if ( Untranslated . Contains ( TranslationUnit ) )
{
Untranslated . Remove ( TranslationUnit ) ;
}
if ( ! Review . Contains ( TranslationUnit ) )
{
Review . Add ( TranslationUnit ) ;
}
}
}
}
}
2014-03-14 14:13:41 -04:00
GWarn - > EndSlowTask ( ) ;
}
2014-04-02 18:09:23 -04:00
void FTranslationDataManager : : HandlePropertyChanged ( FName PropertyName )
{
// When a property changes, write the data so we don't lose changes if user forgets to save or editor crashes
WriteTranslationData ( ) ;
}
2014-04-23 17:49:26 -04:00
void FTranslationDataManager : : PreviewAllTranslationsInEditor ( )
{
FString ManifestFullPath = FPaths : : ConvertRelativePathToFull ( ManifestFilePath ) ;
FString EngineFullPath = FPaths : : ConvertRelativePathToFull ( FPaths : : EngineContentDir ( ) ) ;
bool IsEngineManifest = false ;
if ( ManifestFullPath . StartsWith ( EngineFullPath ) )
{
IsEngineManifest = true ;
}
FString ConfigDirectory ;
if ( IsEngineManifest )
{
ConfigDirectory = FPaths : : EngineConfigDir ( ) ;
}
else
{
ConfigDirectory = FPaths : : GameConfigDir ( ) ;
}
FString ConfigFilePath = ConfigDirectory / " Localization " / " Regenerate " + FPaths : : GetBaseFilename ( ManifestFilePath ) + " .ini " ;
2015-03-03 12:30:55 -05:00
FJsonInternationalizationArchiveSerializer LocalizationArchiveSerializer ;
FJsonInternationalizationManifestSerializer LocalizationManifestSerializer ;
2014-09-25 18:03:04 -04:00
2015-04-16 15:39:05 -04:00
FTextLocalizationManager : : Get ( ) . LoadFromManifestAndArchives ( ConfigFilePath , LocalizationArchiveSerializer , LocalizationManifestSerializer ) ;
2014-04-23 17:49:26 -04:00
}
2014-04-23 18:48:34 -04:00
void FTranslationDataManager : : PopulateSearchResultsUsingFilter ( const FString & SearchFilter )
{
SearchResults . Empty ( ) ;
for ( UTranslationUnit * TranslationUnit : AllTranslations )
{
2014-09-25 18:03:04 -04:00
if ( TranslationUnit ! = nullptr )
2014-04-23 18:48:34 -04:00
{
2014-09-15 17:32:46 -04:00
bool bAdded = false ;
2014-05-01 00:16:11 -04:00
if ( TranslationUnit - > Source . Contains ( SearchFilter ) | |
2014-09-15 17:32:46 -04:00
TranslationUnit - > Translation . Contains ( SearchFilter ) | |
TranslationUnit - > Namespace . Contains ( SearchFilter ) )
2014-05-01 00:16:11 -04:00
{
SearchResults . Add ( TranslationUnit ) ;
2014-09-15 17:32:46 -04:00
bAdded = true ;
}
for ( FTranslationContextInfo CurrentContext : TranslationUnit - > Contexts )
{
if ( ! bAdded & &
( CurrentContext . Context . Contains ( SearchFilter ) | |
CurrentContext . Key . Contains ( SearchFilter ) ) )
{
SearchResults . Add ( TranslationUnit ) ;
break ;
}
2014-05-01 00:16:11 -04:00
}
2014-04-23 18:48:34 -04:00
}
}
}
2014-04-23 19:14:07 -04:00
void FTranslationDataManager : : LoadFromArchive ( TArray < UTranslationUnit * > & InTranslationUnits , bool bTrackChanges /*= false*/ , bool bReloadFromFile /*=false*/ )
{
GWarn - > BeginSlowTask ( LOCTEXT ( " LoadingArchiveEntries " , " Loading Entries from Translation Archive... " ) , true ) ;
if ( bReloadFromFile )
{
ArchivePtr = ReadArchive ( ) ;
}
if ( ArchivePtr . IsValid ( ) )
{
TSharedRef < FInternationalizationArchive > Archive = ArchivePtr . ToSharedRef ( ) ;
// Make a local copy of this array before we empty the arrays below (we might have been passed AllTranslations array)
TArray < UTranslationUnit * > TranslationUnits ;
TranslationUnits . Append ( InTranslationUnits ) ;
AllTranslations . Empty ( ) ;
Untranslated . Empty ( ) ;
Review . Empty ( ) ;
Complete . Empty ( ) ;
ChangedOnImport . Empty ( ) ;
for ( int32 CurrentTranslationUnitIndex = 0 ; CurrentTranslationUnitIndex < TranslationUnits . Num ( ) ; + + CurrentTranslationUnitIndex )
{
UTranslationUnit * TranslationUnit = TranslationUnits [ CurrentTranslationUnitIndex ] ;
2014-09-25 18:03:04 -04:00
if ( TranslationUnit ! = nullptr )
2014-04-23 19:14:07 -04:00
{
2014-05-01 23:21:37 -04:00
if ( ! TranslationUnit - > IsRooted ( ) )
{
TranslationUnit - > AddToRoot ( ) ; // Disable garbage collection for UTranslationUnit objects
}
2014-05-01 00:16:11 -04:00
AllTranslations . Add ( TranslationUnit ) ;
2014-04-23 19:14:07 -04:00
2014-05-01 00:16:11 -04:00
GWarn - > StatusUpdate ( CurrentTranslationUnitIndex , TranslationUnits . Num ( ) , FText : : Format ( LOCTEXT ( " LoadingCurrentArchiveEntries " , " Loading Entry {0} of {1} from Translation Archive... " ) , FText : : AsNumber ( CurrentTranslationUnitIndex ) , FText : : AsNumber ( TranslationUnits . Num ( ) ) ) ) ;
const FLocItem SourceSearch ( TranslationUnit - > Source ) ;
2014-09-25 18:03:04 -04:00
TSharedPtr < FArchiveEntry > ArchiveEntry = Archive - > FindEntryBySource ( TranslationUnit - > Namespace , SourceSearch , nullptr ) ;
2014-05-01 00:16:11 -04:00
if ( ArchiveEntry . IsValid ( ) )
2014-04-23 19:14:07 -04:00
{
2014-05-01 00:16:11 -04:00
const FString PreviousTranslation = TranslationUnit - > Translation ;
TranslationUnit - > Translation = " " ; // Reset to null string
2014-09-17 16:37:28 -04:00
const FString TranslatedString = ArchiveEntry - > Translation . Text ;
2014-04-23 19:14:07 -04:00
2014-09-17 16:37:28 -04:00
if ( TranslatedString . IsEmpty ( ) )
2014-04-23 19:14:07 -04:00
{
2014-05-01 00:16:11 -04:00
bool bHasTranslationHistory = false ;
int32 MostRecentNonNullTranslationIndex = - 1 ;
int32 ContextForRecentTranslation = - 1 ;
for ( int32 ContextIndex = 0 ; ContextIndex < TranslationUnit - > Contexts . Num ( ) ; + + ContextIndex )
2014-04-23 19:14:07 -04:00
{
2014-05-01 00:16:11 -04:00
for ( int32 ChangeIndex = 0 ; ChangeIndex < TranslationUnit - > Contexts [ ContextIndex ] . Changes . Num ( ) ; + + ChangeIndex )
{
if ( ! ( TranslationUnit - > Contexts [ ContextIndex ] . Changes [ ChangeIndex ] . Translation . IsEmpty ( ) ) )
{
bHasTranslationHistory = true ;
MostRecentNonNullTranslationIndex = ChangeIndex ;
ContextForRecentTranslation = ContextIndex ;
break ;
}
}
if ( bHasTranslationHistory )
2014-04-23 19:14:07 -04:00
{
break ;
}
}
2014-05-01 00:16:11 -04:00
// If we have history, but current translation is empty, this goes in the Needs Review tab
2014-04-23 19:14:07 -04:00
if ( bHasTranslationHistory )
{
2014-05-01 00:16:11 -04:00
// Offer the most recent translation (for the first context in the list) as a suggestion or starting point (not saved unless user checks "Has Been Reviewed")
TranslationUnit - > Translation = TranslationUnit - > Contexts [ ContextForRecentTranslation ] . Changes [ MostRecentNonNullTranslationIndex ] . Translation ;
Review . Add ( TranslationUnit ) ;
}
else
{
Untranslated . Add ( TranslationUnit ) ;
2014-04-23 19:14:07 -04:00
}
}
else
{
2014-09-17 16:37:28 -04:00
TranslationUnit - > Translation = TranslatedString ;
2014-05-01 00:16:11 -04:00
TranslationUnit - > HasBeenReviewed = true ;
Complete . Add ( TranslationUnit ) ;
2014-04-23 19:14:07 -04:00
}
2014-05-12 23:27:09 -04:00
// Add to changed array if we're tracking changes (i.e. when we import from .po files)
if ( bTrackChanges )
2014-05-01 00:16:11 -04:00
{
2014-05-12 23:27:09 -04:00
if ( PreviousTranslation ! = TranslationUnit - > Translation )
{
FString PreviousTranslationTrimmed = PreviousTranslation ;
PreviousTranslationTrimmed . Trim ( ) . TrimTrailing ( ) ;
FString CurrentTranslationTrimmed = TranslationUnit - > Translation ;
CurrentTranslationTrimmed . Trim ( ) . TrimTrailing ( ) ;
// Ignore changes to only whitespace at beginning and/or end of string on import
if ( PreviousTranslationTrimmed = = CurrentTranslationTrimmed )
{
TranslationUnit - > Translation = PreviousTranslation ;
}
else
{
ChangedOnImport . Add ( TranslationUnit ) ;
TranslationUnit - > TranslationBeforeImport = PreviousTranslation ;
}
}
2014-05-01 00:16:11 -04:00
}
2014-04-23 19:14:07 -04:00
}
}
}
}
else // ArchivePtr.IsValid() is false
{
FFormatNamedArguments Arguments ;
Arguments . Add ( TEXT ( " ArchiveFilePath " ) , FText : : FromString ( ArchiveFilePath ) ) ;
FMessageLog TranslationEditorMessageLog ( " TranslationEditor " ) ;
TranslationEditorMessageLog . Error ( FText : : Format ( LOCTEXT ( " FailedToLoadCurrentArchive " , " Failed to load most current translation archive ({ArchiveFilePath}), unable to load translations. " ) , Arguments ) ) ;
TranslationEditorMessageLog . Notify ( LOCTEXT ( " TranslationLoadError " , " Error Loading Translations! " ) ) ;
TranslationEditorMessageLog . Open ( EMessageSeverity : : Error ) ;
}
GWarn - > EndSlowTask ( ) ;
}
2014-05-01 23:21:37 -04:00
void FTranslationDataManager : : RemoveTranslationUnitArrayfromRoot ( TArray < UTranslationUnit * > & TranslationUnits )
{
for ( UTranslationUnit * TranslationUnit : TranslationUnits )
{
TranslationUnit - > RemoveFromRoot ( ) ;
}
}
2014-11-18 03:20:09 -05:00
void FTranslationDataManager : : UnloadHistoryInformation ( )
{
TArray < UTranslationUnit * > & TranslationUnits = AllTranslations ;
for ( int32 CurrentTranslationUnitIndex = 0 ; CurrentTranslationUnitIndex < TranslationUnits . Num ( ) ; + + CurrentTranslationUnitIndex )
{
UTranslationUnit * TranslationUnit = TranslationUnits [ CurrentTranslationUnitIndex ] ;
if ( TranslationUnit ! = nullptr )
{
// If HasBeenReviewed is false, this is a suggestion translation from a previous translation for the same Namespace/Key pair
if ( ! TranslationUnit - > HasBeenReviewed )
{
if ( ! Untranslated . Contains ( TranslationUnit ) )
{
Untranslated . Add ( TranslationUnit ) ;
}
if ( Review . Contains ( TranslationUnit ) )
{
Review . Remove ( TranslationUnit ) ;
}
// Erase previously suggested translation from history (it has not been reviewed)
TranslationUnit - > Translation . Empty ( ) ;
// Remove all history entries
for ( FTranslationContextInfo Context : TranslationUnit - > Contexts )
{
Context . Changes . Empty ( ) ;
}
}
}
}
}
bool FTranslationDataManager : : SaveSelectedTranslations ( TArray < UTranslationUnit * > TranslationUnitsToSave )
{
bool bSucceeded = true ;
TMap < FString , TSharedPtr < TArray < UTranslationUnit * > > > TextsToSavePerProject ;
// Regroup the translations to save by project
for ( UTranslationUnit * TextToSave : TranslationUnitsToSave )
{
FString LocresFilePath = TextToSave - > LocresPath ;
if ( ! LocresFilePath . IsEmpty ( ) )
{
if ( ! TextsToSavePerProject . Contains ( LocresFilePath ) )
{
TextsToSavePerProject . Add ( LocresFilePath , MakeShareable ( new TArray < UTranslationUnit * > ( ) ) ) ;
}
TSharedPtr < TArray < UTranslationUnit * > > ProjectArray = TextsToSavePerProject . FindRef ( LocresFilePath ) ;
ProjectArray - > Add ( TextToSave ) ;
}
}
for ( auto TextIt = TextsToSavePerProject . CreateIterator ( ) ; TextIt ; + + TextIt )
{
auto Item = * TextIt ;
FString CurrentLocResPath = Item . Key ;
FString ManifestAndArchiveName = FPaths : : GetBaseFilename ( CurrentLocResPath ) ;
FString ArchiveFilePath = FPaths : : GetPath ( CurrentLocResPath ) ;
FString CultureName = FPaths : : GetBaseFilename ( ArchiveFilePath ) ;
FString ManifestPath = FPaths : : GetPath ( ArchiveFilePath ) ;
FString ArchiveFullPath = ArchiveFilePath / ManifestAndArchiveName + " .archive " ;
FString ManifestFullPath = ManifestPath / ManifestAndArchiveName + " .manifest " ;
if ( FPaths : : FileExists ( ManifestFullPath ) & & FPaths : : FileExists ( ArchiveFullPath ) )
{
TSharedRef < FTranslationDataManager > DataManager = MakeShareable ( new FTranslationDataManager ( ManifestFullPath , ArchiveFullPath ) ) ;
TArray < UTranslationUnit * > & TranslationsArray = DataManager - > GetAllTranslationsArray ( ) ;
TSharedPtr < TArray < UTranslationUnit * > > EditedItems = Item . Value ;
// For each edited item belonging to this manifest/archive pair
for ( auto EditedItemIt = EditedItems - > CreateIterator ( ) ; EditedItemIt ; + + EditedItemIt )
{
UTranslationUnit * EditedItem = * EditedItemIt ;
// Search all translations for the one that matches this FText
for ( UTranslationUnit * Translation : TranslationsArray )
{
// If namespace matches...
if ( Translation - > Namespace = = EditedItem - > Namespace )
{
// And source matches
if ( Translation - > Source = = EditedItem - > Source )
{
// Update the translation in TranslationDataManager, and finish searching these translations
Translation - > Translation = EditedItem - > Translation ;
break ;
}
}
}
}
// Save the data to file, and preview in editor
bSucceeded = bSucceeded & & DataManager - > WriteTranslationData ( ) ;
DataManager - > PreviewAllTranslationsInEditor ( ) ;
}
else
{
bSucceeded = false ;
}
}
return bSucceeded ;
}
2014-04-02 18:09:23 -04:00
# undef LOCTEXT_NAMESPACE