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 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());
	});
}