44
55#include < HttpManager.h>
66#include < HttpModule.h>
7+ #include < ISourceControlModule.h>
8+ #include < ISourceControlOperation.h>
9+ #include < ISourceControlProvider.h>
10+ #include < ISourceControlState.h>
711#include < Interfaces/IHttpRequest.h>
812#include < Interfaces/IHttpResponse.h>
913#include < Interfaces/IMainFrameModule.h>
1923#include < Misc/MessageDialog.h>
2024#include < Serialization/JsonInternationalizationManifestSerializer.h>
2125#include < Settings/ProjectPackagingSettings.h>
26+ #include < SourceControlOperations.h>
2227
2328#include " STolgeeSyncDialog.h"
2429#include " TolgeeEditor.h"
@@ -147,6 +152,114 @@ void UTolgeeEditorIntegrationSubsystem::Sync()
147152 }
148153}
149154
155+ void UTolgeeEditorIntegrationSubsystem::DownloadTranslationsJson ()
156+ {
157+ // Gather remote keys
158+ UTolgeeLocalizationSubsystem* LocalizationSubsystem = GEngine->GetEngineSubsystem <UTolgeeLocalizationSubsystem>();
159+ LocalizationSubsystem->ManualFetch ();
160+
161+ // We need to wait synchronously wait for the fetch response to ensure we have the latest data
162+ while (LocalizationSubsystem->IsFetchInprogress ())
163+ {
164+ FPlatformProcess::Sleep (0 .01f );
165+
166+ if (IsInGameThread ())
167+ {
168+ FHttpModule::Get ().GetHttpManager ().Tick (0 .01f );
169+ }
170+ }
171+
172+ const FString FilePath = TolgeeUtils::GetLocalizationSourceFile ();
173+
174+ EnsureFileCheckedOutSourceControl (FilePath);
175+ bool bWasModified = ExportLocalTranslations ();
176+ EnsureAddedStateSourceControl (FilePath, bWasModified);
177+ }
178+
179+ bool UTolgeeEditorIntegrationSubsystem::EnsureFileCheckedOutSourceControl (FString FilePath)
180+ {
181+ if (!FPaths::FileExists (FilePath))
182+ {
183+ return true ;
184+ }
185+
186+ ISourceControlProvider& SourceControlProvider = ISourceControlModule::Get ().GetProvider ();
187+
188+ if (!SourceControlProvider.IsEnabled ())
189+ {
190+ return true ;
191+ }
192+
193+ FSourceControlStatePtr SourceControlState = SourceControlProvider.GetState (FilePath, EStateCacheUsage::ForceUpdate);
194+ if (!SourceControlState.IsValid () || !SourceControlState->IsSourceControlled ())
195+ {
196+ return true ;
197+ }
198+
199+ if (!SourceControlState->IsCheckedOut ())
200+ {
201+ TSharedRef<FCheckOut, ESPMode::ThreadSafe> CheckOutOperation = ISourceControlOperation::Create<FCheckOut>();
202+
203+ if (SourceControlProvider.Execute (CheckOutOperation, FilePath) == ECommandResult::Succeeded)
204+ {
205+ return true ;
206+ }
207+
208+ return false ;
209+ }
210+
211+ return true ;
212+ }
213+
214+ bool UTolgeeEditorIntegrationSubsystem::EnsureAddedStateSourceControl (FString FilePath, bool bWasFileModified)
215+ {
216+ if (!FPaths::FileExists (FilePath))
217+ {
218+ return false ;
219+ }
220+
221+ ISourceControlProvider& SourceControlProvider = ISourceControlModule::Get ().GetProvider ();
222+
223+ if (!SourceControlProvider.IsEnabled ())
224+ {
225+ return true ;
226+ }
227+
228+ // Ensure we have the latest state from Perforce
229+ TArray<FString> FilesToUpdate = { FilePath };
230+ SourceControlProvider.Execute (ISourceControlOperation::Create<FUpdateStatus>(), FilesToUpdate);
231+
232+ // Fetch the refreshed state
233+ FSourceControlStatePtr SourceControlState = SourceControlProvider.GetState (FilePath, EStateCacheUsage::ForceUpdate);
234+
235+ if (SourceControlState.IsValid () && SourceControlState->IsSourceControlled () && SourceControlState->IsCheckedOut ())
236+ {
237+ // TODO: The idiomatic way of checking if the file was modified would be to use SourceControlState->IsModified()
238+ // But that did not work in a reliable way with Perforce even when retrying with waiting up to 5 seconds and still the file was
239+ // reverted even if it was modified. Relying on the provided bool bWasFileModified is a workaround to make this reliable.
240+
241+ if (bWasFileModified)
242+ {
243+ // File is modified, do NOT revert it
244+ return true ;
245+ }
246+
247+ // If the file is checked out but NOT modified, revert it
248+ TSharedRef<FRevert, ESPMode::ThreadSafe> RevertOperation = ISourceControlOperation::Create<FRevert>();
249+ SourceControlProvider.Execute (RevertOperation, FilePath);
250+
251+ return true ;
252+ }
253+
254+ TSharedRef<FMarkForAdd, ESPMode::ThreadSafe> AddOperation = ISourceControlOperation::Create<FMarkForAdd>();
255+ if (SourceControlProvider.Execute (AddOperation, FilePath) == ECommandResult::Succeeded)
256+ {
257+ return true ;
258+ }
259+
260+ return false ;
261+ }
262+
150263void UTolgeeEditorIntegrationSubsystem::UploadLocalKeys (TArray<FLocalizationKey> NewLocalKeys)
151264{
152265 const UTolgeeSettings* Settings = GetDefault<UTolgeeSettings>();
@@ -449,7 +562,7 @@ void UTolgeeEditorIntegrationSubsystem::OnMainFrameReady(TSharedPtr<SWindow> InR
449562#endif
450563}
451564
452- void UTolgeeEditorIntegrationSubsystem::ExportLocalTranslations ()
565+ bool UTolgeeEditorIntegrationSubsystem::ExportLocalTranslations ()
453566{
454567 const UTolgeeLocalizationSubsystem* LocalizationSubsystem = GEngine->GetEngineSubsystem <UTolgeeLocalizationSubsystem>();
455568 while (LocalizationSubsystem->GetLocalizedDictionary ().Keys .Num () == 0 )
@@ -468,14 +581,26 @@ void UTolgeeEditorIntegrationSubsystem::ExportLocalTranslations()
468581 if (!FJsonObjectConverter::UStructToJsonObjectString (Dictionary, JsonString))
469582 {
470583 UE_LOG (LogTolgee, Error, TEXT (" Couldn't convert the localized dictionary to string" ));
471- return ;
584+ return false ;
472585 }
473586
474587 const FString Filename = TolgeeUtils::GetLocalizationSourceFile ();
588+ FString BeforeHash;
589+ if (FPaths::FileExists (Filename))
590+ {
591+ FString BeforeFileContents;
592+ if (FFileHelper::LoadFileToString (BeforeFileContents, *Filename))
593+ {
594+ BeforeHash = FMD5::HashAnsiString (*BeforeFileContents);
595+ }
596+ }
597+
598+ bool wasModified = BeforeHash != FMD5::HashAnsiString (*JsonString);
599+
475600 if (!FFileHelper::SaveStringToFile (JsonString, *Filename))
476601 {
477602 UE_LOG (LogTolgee, Error, TEXT (" Couldn't save the localized dictionary to file: %s" ), *Filename);
478- return ;
603+ return false ;
479604 }
480605
481606 const FDirectoryPath TolgeeLocalizationPath = TolgeeUtils::GetLocalizationDirectory ();
@@ -492,9 +617,10 @@ void UTolgeeEditorIntegrationSubsystem::ExportLocalTranslations()
492617 ProjectPackagingSettings->DirectoriesToAlwaysStageAsNonUFS .Add (TolgeeLocalizationPath);
493618 ProjectPackagingSettings->SaveConfig ();
494619 }
495-
496-
620+
497621 UE_LOG (LogTolgee, Display, TEXT (" Localized dictionary succesfully saved to file: %s" ), *Filename);
622+
623+ return wasModified;
498624}
499625
500626void UTolgeeEditorIntegrationSubsystem::Initialize (FSubsystemCollectionBase& Collection)
@@ -523,9 +649,9 @@ void UTolgeeEditorIntegrationSubsystem::Initialize(FSubsystemCollectionBase& Col
523649#endif
524650
525651 const UTolgeeSettings* Settings = GetDefault<UTolgeeSettings>();
526- if (bIsRunningCookCommandlet && !Settings->bLiveTranslationUpdates )
652+ if (bIsRunningCookCommandlet && !Settings->bLiveTranslationUpdates && Settings-> bFetchTranslationsOnCook )
527653 {
528- ExportLocalTranslations ();
654+ DownloadTranslationsJson ();
529655 }
530656}
531657
0 commit comments