bool LocalizationCommandletTasks::ImportTextForCulture(const TSharedRef<SWindow>& ParentWindow, ULocalizationTarget* const Target, const FString& CultureName, const TOptional<FString> FilePath)
{
	FCulturePtr Culture = FInternationalization::Get().GetCulture(CultureName);
	if (!Culture.IsValid())
	{
		return false;
	}

	TArray<LocalizationCommandletExecution::FTask> Tasks;
	const bool ShouldUseProjectFile = !Target->IsMemberOfEngineTargetSet();

	const FString DefaultImportScriptPath = LocalizationConfigurationScript::GetImportTextConfigPath(Target, TOptional<FString>(CultureName));
	const FString ImportScriptPath = FPaths::CreateTempFilename(*FPaths::GetPath(DefaultImportScriptPath), *FPaths::GetBaseFilename(DefaultImportScriptPath), *FPaths::GetExtension(DefaultImportScriptPath, true));
	LocalizationConfigurationScript::GenerateImportTextConfigFile(Target, TOptional<FString>(CultureName), FilePath).Write(ImportScriptPath);
	Tasks.Add(LocalizationCommandletExecution::FTask(LOCTEXT("ImportTaskName", "Import Translations"), ImportScriptPath, ShouldUseProjectFile));

	const FString ReportScriptPath = LocalizationConfigurationScript::GetWordCountReportConfigPath(Target);
	LocalizationConfigurationScript::GenerateWordCountReportConfigFile(Target).Write(ReportScriptPath);
	Tasks.Add(LocalizationCommandletExecution::FTask(LOCTEXT("ReportTaskName", "Generate Reports"), ReportScriptPath, ShouldUseProjectFile));

	FFormatNamedArguments Arguments;
	Arguments.Add(TEXT("CultureName"), FText::FromString(Culture->GetDisplayName()));
	Arguments.Add(TEXT("TargetName"), FText::FromString(Target->Settings.Name));

	const FText WindowTitle = FText::Format(LOCTEXT("ImportCultureForTargetWindowTitle", "Import {CultureName} Translations for Target {TargetName}"), Arguments);

	bool HasSucceeeded = LocalizationCommandletExecution::Execute(ParentWindow, WindowTitle, Tasks);
	IFileManager::Get().Delete(*ImportScriptPath); // Don't clutter up the loc config directory with scripts for individual cultures.
	return HasSucceeeded;
}
void FInternationalizationSettingsModelDetails::RefreshAvailableLanguages()
{
	AvailableLanguages.Empty();
	
	FString SelectedLanguageName;
	if ( SelectedCulture.IsValid() )
	{
		SelectedLanguageName = SelectedCulture->GetNativeLanguage();
	}

	// Setup the language list
	for( const auto& Culture : AvailableCultures )
	{
		FCulturePtr LanguageCulture = FInternationalization::Get().GetCulture(Culture->GetTwoLetterISOLanguageName());
		if (LanguageCulture.IsValid() && AvailableLanguages.Find(LanguageCulture) == INDEX_NONE)
		{
			AvailableLanguages.Add(LanguageCulture);
				
			// Do we have a match for the base language
			const FString CultureLanguageName = Culture->GetNativeLanguage();
			if ( SelectedLanguageName == CultureLanguageName)
			{
				SelectedLanguage = Culture;
			}
		}
	}

	AvailableLanguages.Sort( FCompareCultureByNativeLanguage() );

	RefreshAvailableRegions();
}
FText SLocalizationDashboardTargetRow::GetCulturesText() const
{
	FLocalizationTargetSettings* const TargetSettings = GetTargetSettings();
	if (TargetSettings)
	{
		TArray<FString> OrderedCultureNames;
		OrderedCultureNames.Add(TargetSettings->NativeCultureStatistics.CultureName);
		for (auto& ForeignCultureStatistics : TargetSettings->SupportedCulturesStatistics)
		{
			OrderedCultureNames.Add(ForeignCultureStatistics.CultureName);
		}

		FString Result;
		for (FString& CultureName : OrderedCultureNames)
		{
			const FCulturePtr Culture = FInternationalization::Get().GetCulture(CultureName);
			if (Culture.IsValid())
			{
				const FString CultureDisplayName = Culture->GetDisplayName();

				if (!Result.IsEmpty())
				{
					Result.Append(TEXT(", "));
				}

				Result.Append(CultureDisplayName);
			}
		}

		return FText::FromString(Result);
	}

	return FText::GetEmpty();
}
	static FText GetCultureNativeRegionText( const FCulturePtr Culture )
	{
		check( Culture.IsValid() );
		FString Region = Culture->GetNativeRegion();
		if ( Region.IsEmpty() )
		{
			// Fallback to displaying the language, if no region is available
			return LOCTEXT("NoSpecificRegionOption", "Non-Specific Region");
		}
		return FText::FromString(Region);
	}
void FInternationalizationSettingsModelDetails::RefreshAvailableRegions()
{
	AvailableRegions.Empty();

	FCulturePtr DefaultCulture;
	if ( SelectedLanguage.IsValid() )
	{
		const FString SelectedLanguageName = SelectedLanguage->GetTwoLetterISOLanguageName();

		// Setup the region list
		for( const auto& Culture : AvailableCultures )
		{
			const FString CultureLanguageName = Culture->GetTwoLetterISOLanguageName();
			if ( SelectedLanguageName == CultureLanguageName)
			{
				AvailableRegions.Add( Culture );

				// If this doesn't have a valid region... assume it's the default
				const FString CultureRegionName = Culture->GetNativeRegion();
				if ( CultureRegionName.IsEmpty() )
				{	
					DefaultCulture = Culture;
				}
			}
		}

		AvailableRegions.Sort( FCompareCultureByNativeRegion() );
	}

	// If we have a preferred default (or there's only one in the list), select that now
	if ( !DefaultCulture.IsValid() )
	{
		if(AvailableRegions.Num() == 1)
		{
			DefaultCulture = AvailableRegions.Last();
		}
	}

	if ( DefaultCulture.IsValid() )
	{
		// Set it as our default region, if one hasn't already been chosen
		if ( !SelectedCulture.IsValid() && RegionComboBox.IsValid() )
		{
			// We have to update the combo box like this, otherwise it'll do a null selection when we next click on it
			RegionComboBox->SetSelectedItem( DefaultCulture );
		}
	}

	if ( RegionComboBox.IsValid() )
	{
		RegionComboBox->RefreshOptions();
	}
}
	FORCEINLINE bool operator()( const FCulturePtr A, const FCulturePtr B ) const
	{
		check( A.IsValid() );
		check( B.IsValid() );
		// Non-Specific Region should appear before all else.
		if(A->GetNativeRegion().IsEmpty())
		{
			return true;
		}
		// Non-Specific Region should appear before all else.
		if(B->GetNativeRegion().IsEmpty())
		{
			return false;
		}
		// Compare native region strings.
		return( GetCultureNativeRegionText( A ).CompareToCaseIgnored( GetCultureNativeRegionText( B ) ) ) < 0;
	}
	static FText GetCultureNativeLanguageText( const FCulturePtr Culture )
	{
		check( Culture.IsValid() );
		const FString Language = Culture->GetNativeLanguage();
		return FText::FromString(Language);
	}
	FORCEINLINE bool operator()( const FCulturePtr A, const FCulturePtr B ) const
	{
		check( A.IsValid() );
		check( B.IsValid() );
		return( GetCultureNativeLanguageText( A ).CompareToCaseIgnored( GetCultureNativeLanguageText( B ) ) ) < 0;
	}
void EndInitTextLocalization()
{
	DECLARE_SCOPE_CYCLE_COUNTER(TEXT("EndInitTextLocalization"), STAT_EndInitTextLocalization, STATGROUP_LoadTime);

	const bool ShouldLoadEditor = WITH_EDITOR;
	const bool ShouldLoadGame = FApp::IsGame();

	FInternationalization& I18N = FInternationalization::Get();

	// Set culture according to configuration now that configs are available.
#if ENABLE_LOC_TESTING
	if( FCommandLine::IsInitialized() && FParse::Param(FCommandLine::Get(), TEXT("LEET")) )
	{
		I18N.SetCurrentCulture(TEXT("LEET"));
	}
	else
#endif
	{
		FString RequestedCultureName;
		if (FParse::Value(FCommandLine::Get(), TEXT("CULTUREFORCOOKING="), RequestedCultureName))
		{
			// Write the culture passed in if first install...
			if (FParse::Param(FCommandLine::Get(), TEXT("firstinstall")))
			{
				GConfig->SetString(TEXT("Internationalization"), TEXT("Culture"), *RequestedCultureName, GEngineIni);
			}
		}
		else
#if !UE_BUILD_SHIPPING
		// Use culture override specified on commandline.
		if (FParse::Value(FCommandLine::Get(), TEXT("CULTURE="), RequestedCultureName))
		{
			//UE_LOG(LogInit, Log, TEXT("Overriding culture %s w/ command-line option".), *CultureName);
		}
		else
#endif // !UE_BUILD_SHIPPING
#if WITH_EDITOR
		// See if we've been provided a culture override in the editor
		if(GConfig->GetString( TEXT("Internationalization"), TEXT("Culture"), RequestedCultureName, GEditorGameAgnosticIni ))
		{
			//UE_LOG(LogInit, Log, TEXT("Overriding culture %s w/ editor configuration."), *CultureName);
		}
		else
#endif // WITH_EDITOR
		// Use culture specified in engine configuration.
		if(GConfig->GetString( TEXT("Internationalization"), TEXT("Culture"), RequestedCultureName, GEngineIni ))
		{
			//UE_LOG(LogInit, Log, TEXT("Overriding culture %s w/ engine configuration."), *CultureName);
		}
		else
		{
			RequestedCultureName = I18N.GetDefaultCulture()->GetName();
		}

		FString TargetCultureName = RequestedCultureName;
		{
			TArray<FString> LocalizationPaths;
			if(ShouldLoadEditor)
			{
				LocalizationPaths += FPaths::GetEditorLocalizationPaths();
			}
			if(ShouldLoadGame)
			{
				LocalizationPaths += FPaths::GetGameLocalizationPaths();
			}
			LocalizationPaths += FPaths::GetEngineLocalizationPaths();

			// Validate the locale has data or fallback to one that does.
			TArray< FCultureRef > AvailableCultures;
			I18N.GetCulturesWithAvailableLocalization(LocalizationPaths, AvailableCultures, false);

			TArray<FString> PrioritizedParentCultureNames = I18N.GetCurrentCulture()->GetPrioritizedParentCultureNames();
				
			FString ValidCultureName;
			for (const FString& CultureName : PrioritizedParentCultureNames)
			{
				FCulturePtr ValidCulture = I18N.GetCulture(CultureName);
				if (ValidCulture.IsValid() && AvailableCultures.Contains(ValidCulture.ToSharedRef()))
				{
					ValidCultureName = CultureName;
					break;
				}
			}

			if(!ValidCultureName.IsEmpty())
			{
				if(RequestedCultureName != ValidCultureName)
				{
					// Make the user aware that the localization data belongs to a parent culture.
					UE_LOG(LogTextLocalizationManager, Log, TEXT("The requested culture ('%s') has no localization data; parent culture's ('%s') localization data will be used."), *RequestedCultureName, *ValidCultureName);
				}
			}
			else
			{
				// Fallback to English.
				UE_LOG(LogTextLocalizationManager, Log, TEXT("The requested culture ('%s') has no localization data; falling back to 'en' for localization and internationalization data."), *RequestedCultureName);
				TargetCultureName = "en";
			}
		}

		I18N.SetCurrentCulture(TargetCultureName);
	}

	FTextLocalizationManager::Get().LoadResources(ShouldLoadEditor, ShouldLoadGame);
	FTextLocalizationManager::Get().bIsInitialized = true;
}
void FOneSkyLocalizationServiceProvider::ExportCultureForTargetToOneSky_Callback(const FLocalizationServiceOperationRef& Operation, ELocalizationServiceOperationCommandResult::Type Result, bool bIsTargetSet)
{
	TSharedPtr<FUploadLocalizationTargetFile, ESPMode::ThreadSafe> UploadLocalizationTargetOp = StaticCastSharedRef<FUploadLocalizationTargetFile>(Operation);
	bool bError = !(Result == ELocalizationServiceOperationCommandResult::Succeeded);
	FText ErrorText = FText::GetEmpty();
	FGuid InTargetGuid;
	FString InRelativeInputFilePathAndName;
	FString TargetName = "";
	FString CultureName = "";
	ULocalizationTarget* Target = nullptr;
	if (UploadLocalizationTargetOp.IsValid())
	{
		InTargetGuid = UploadLocalizationTargetOp->GetInTargetGuid();
		CultureName = UploadLocalizationTargetOp->GetInLocale();
		InRelativeInputFilePathAndName = UploadLocalizationTargetOp->GetInRelativeInputFilePathAndName();

		TargetName = FPaths::GetBaseFilename(InRelativeInputFilePathAndName);
		FString EngineOrGamePath = FPaths::GetBaseFilename(FPaths::GetPath(FPaths::GetPath(FPaths::GetPath(InRelativeInputFilePathAndName))));
		bool bIsEngineTarget = EngineOrGamePath == "Engine";
		Target = ILocalizationModule::Get().GetLocalizationTargetByName(TargetName, bIsEngineTarget);

		// Remove each file we get a callback for so we know when we've gotten a callback for all of them
		FilesDownloadingForImportFromOneSky.Remove(InRelativeInputFilePathAndName);

		ErrorText = UploadLocalizationTargetOp->GetOutErrorText();

		FilesUploadingForExportToOneSky.Remove(InRelativeInputFilePathAndName);

		int32 TotalNumber = 0;
		if (bIsTargetSet)
		{
			for (ULocalizationTarget* LocalizationTarget : Target->GetOuterULocalizationTargetSet()->TargetObjects)
			{
				TotalNumber += LocalizationTarget->Settings.SupportedCulturesStatistics.Num();
			}
		}
		else
		{
			TotalNumber = Target->Settings.SupportedCulturesStatistics.Num();
		}

		// Update progress bar
		GWarn->StatusUpdate(TotalNumber - FilesUploadingForExportToOneSky.Num(),
			TotalNumber,
			LOCTEXT("UploadingFilestoLocalizationService", "Uploading Files to Localization Service..."));

		if (FilesUploadingForExportToOneSky.Num() == 0)
		{
			GWarn->EndSlowTask();
		}
	}

	// Try to get display name
	FInternationalization& I18N = FInternationalization::Get();
	FCulturePtr CulturePtr = I18N.GetCulture(CultureName);
	FString CultureDisplayName = CultureName;
	if (CulturePtr.IsValid())
	{
		CultureName = CulturePtr->GetDisplayName();
	}

	if (!bError && ErrorText.IsEmpty())
	{
		FText SuccessText = FText::Format(LOCTEXT("ExportTranslationsToTranslationServiceSuccess", "{0} translations for {1} target uploaded for processing to Translation Service."), FText::FromString(CultureDisplayName), FText::FromString(TargetName));
		FMessageLog TranslationEditorMessageLog("TranslationEditor");
		TranslationEditorMessageLog.Info(SuccessText);
		TranslationEditorMessageLog.Notify(SuccessText, EMessageSeverity::Info, true);
	}
	else
	{
		if (ErrorText.IsEmpty())
		{
			ErrorText = LOCTEXT("ExportToLocalizationServiceUnspecifiedError", "An unspecified error occured when trying to export to the Localization Service.");
		}

		FText ErrorNotify = FText::Format(LOCTEXT("SaveSelectedTranslationsToTranslationServiceFail", "{0} translations for {1} target failed to export to Translation Service!"), FText::FromString(CultureDisplayName), FText::FromString(TargetName));
		FMessageLog TranslationEditorMessageLog("TranslationEditor");
		TranslationEditorMessageLog.Error(ErrorNotify);
		TranslationEditorMessageLog.Error(ErrorText);
		TranslationEditorMessageLog.Notify(ErrorNotify);
	}
}
void FInternationalization::GetCulturesWithAvailableLocalization(const TArray<FString>& InLocalizationPaths, TArray< FCultureRef >& OutAvailableCultures, const bool bIncludeDerivedCultures)
{
	OutAvailableCultures.Reset();

	TArray<FString> AllLocalizationFolders;
	IFileManager& FileManager = IFileManager::Get();
	for(const auto& LocalizationPath : InLocalizationPaths)
	{
		/* Visitor class used to enumerate directories of culture */
		class FCultureEnumeratorVistor : public IPlatformFile::FDirectoryVisitor
		{
		public:
			FCultureEnumeratorVistor( TArray<FString>& OutLocalizationFolders )
				: LocalizationFolders(OutLocalizationFolders)
			{
			}

			virtual bool Visit(const TCHAR* FilenameOrDirectory, bool bIsDirectory) override
			{
				if(bIsDirectory)
				{
					// UE localization resource folders use "en-US" style while ICU uses "en_US"
					const FString LocalizationFolder = FPaths::GetCleanFilename(FilenameOrDirectory);
					const FString CanonicalName = FCulture::GetCanonicalName(LocalizationFolder);
					LocalizationFolders.AddUnique(CanonicalName);
				}

				return true;
			}

			/** Array to fill with the names of the UE localization folders available at the given path */
			TArray<FString>& LocalizationFolders;
		};

		FCultureEnumeratorVistor CultureEnumeratorVistor(AllLocalizationFolders);
		FileManager.IterateDirectory(*LocalizationPath, CultureEnumeratorVistor);
	}

	// Find any cultures that are a partial match for those we have translations for.
	if(bIncludeDerivedCultures)
	{
		TArray<FString> CultureNames;
		GetCultureNames(CultureNames);
		for(const FString& CultureName : CultureNames)
		{
			FCulturePtr Culture = GetCulture(CultureName);
			if (Culture.IsValid())
			{
				TArray<FString> PrioritizedParentCultureNames = Culture->GetPrioritizedParentCultureNames();

				for (const FString& PrioritizedParentCultureName : PrioritizedParentCultureNames)
				{
					if(AllLocalizationFolders.Contains(PrioritizedParentCultureName))
					{
						OutAvailableCultures.AddUnique(Culture.ToSharedRef());
						break;
					}
				}
			}
		}
	}
	// Find any cultures that are a complete match for those we have translations for.
	else
	{
		for(const FString& LocalizationFolder : AllLocalizationFolders)
		{
			FCulturePtr Culture = GetCulture(LocalizationFolder);
			if(Culture.IsValid())
			{
				OutAvailableCultures.AddUnique(Culture.ToSharedRef());
			}
		}
	}

	// Remove any cultures that were explicitly disabled
	OutAvailableCultures.RemoveAll([&](const FCultureRef& InCulture) -> bool
	{
		return Implementation->IsCultureDisabled(InCulture->GetName());
	});
}