EM_Task/CoreUObject/Private/Internationalization/PackageLocalizationManager.cpp

171 lines
7.5 KiB
C++
Raw Permalink Normal View History

2026-02-13 16:18:33 +08:00
// Copyright Epic Games, Inc. All Rights Reserved.
#include "Internationalization/PackageLocalizationManager.h"
#include "Misc/Paths.h"
#include "Misc/PackageName.h"
#include "Interfaces/IPluginManager.h"
#include "Internationalization/Culture.h"
#include "Internationalization/IPackageLocalizationCache.h"
#include "Internationalization/PackageLocalizationCache.h"
DEFINE_LOG_CATEGORY_STATIC(LogPackageLocalizationManager, Log, All);
class FDefaultPackageLocalizationCache: public FPackageLocalizationCache
{
public:
virtual ~FDefaultPackageLocalizationCache() {}
protected:
//~ FPackageLocalizationCache interface
virtual void FindLocalizedPackages(const FString& InSourceRoot, const FString& InLocalizedRoot, TMap<FName, TArray<FName>>& InOutSourcePackagesToLocalizedPackages) override;
virtual void FindAssetGroupPackages(const FName InAssetGroupName, const FName InAssetClassName) override;
};
void FDefaultPackageLocalizationCache::FindLocalizedPackages(const FString& InSourceRoot, const FString& InLocalizedRoot, TMap<FName, TArray<FName>>& InOutSourcePackagesToLocalizedPackages)
{
// Convert the package path to a filename with no extension (directory)
FString LocalizedPackageFilePath;
if (!FPackageName::TryConvertLongPackageNameToFilename(InLocalizedRoot / TEXT(""), LocalizedPackageFilePath))
{
return;
}
FPackageName::IteratePackagesInDirectory(LocalizedPackageFilePath, FPackageName::FPackageNameVisitor([&](const TCHAR* InPackageFileName) -> bool
{
const FString PackageSubPath = FPaths::ChangeExtension(InPackageFileName + LocalizedPackageFilePath.Len(), FString());
const FName SourcePackageName = *(InSourceRoot / PackageSubPath);
const FName LocalizedPackageName = *(InLocalizedRoot / PackageSubPath);
TArray<FName>& PrioritizedLocalizedPackageNames = InOutSourcePackagesToLocalizedPackages.FindOrAdd(SourcePackageName);
PrioritizedLocalizedPackageNames.AddUnique(LocalizedPackageName);
return true;
}));
}
void FDefaultPackageLocalizationCache::FindAssetGroupPackages(const FName InAssetGroupName, const FName InAssetClassName)
{
// Not supported without the asset registry
}
void FPackageLocalizationManager::PerformLazyInitialization()
{
if (!ActiveCache.IsValid() && LazyInitFunc)
{
LazyInitFunc(*this);
if (!ActiveCache.IsValid())
{
UE_LOG(LogPackageLocalizationManager, Warning, TEXT("InitializeFromLazyCallback was bound to a callback that didn't initialize the active cache."));
}
}
}
void FPackageLocalizationManager::InitializeFromLazyCallback(FLazyInitFunc InLazyInitFunc)
{
LazyInitFunc = MoveTemp(InLazyInitFunc);
ActiveCache.Reset();
}
void FPackageLocalizationManager::InitializeFromCache(const TSharedRef<IPackageLocalizationCache>& InCache)
{
ActiveCache = InCache;
// Only preemptively attempt to conditionally update the cache outside of the editor where such things
// will happen almost immediately in a localized game, where as in the editor it's a bunch of work that
// likely won't be used until using some localization menus in the editor.
#if !WITH_EDITOR
ActiveCache->ConditionalUpdateCache();
// Allow the plugin manager to update the package localization cache by exposing access through a delegate.
// PluginManager is a Core class, but package localization functionality is added at the CoreUObject level.
IPluginManager::Get().SetUpdatePackageLocalizationCacheDelegate(IPluginManager::FUpdatePackageLocalizationCacheDelegate::CreateRaw(this, &FPackageLocalizationManager::ConditionalUpdateCache));
#endif
}
void FPackageLocalizationManager::InitializeFromDefaultCache()
{
InitializeFromCache(MakeShared<FDefaultPackageLocalizationCache>());
}
FName FPackageLocalizationManager::FindLocalizedPackageName(const FName InSourcePackageName)
{
PerformLazyInitialization();
if (ActiveCache.IsValid())
{
return ActiveCache->FindLocalizedPackageName(InSourcePackageName);
}
UE_LOG(LogPackageLocalizationManager, Warning, TEXT("Localized package requested for '%s' before the package localization manager cache was ready. Falling back to a non-cached look-up..."), *InSourcePackageName.ToString());
const FString CurrentCultureName = FInternationalization::Get().GetCurrentCulture()->GetName();
return FindLocalizedPackageNameNoCache(InSourcePackageName, CurrentCultureName);
}
FName FPackageLocalizationManager::FindLocalizedPackageNameForCulture(const FName InSourcePackageName, const FString& InCultureName)
{
PerformLazyInitialization();
if (ActiveCache.IsValid())
{
return ActiveCache->FindLocalizedPackageNameForCulture(InSourcePackageName, InCultureName);
}
UE_LOG(LogPackageLocalizationManager, Warning, TEXT("Localized package requested for '%s' before the package localization manager cache was ready. Falling back to a non-cached look-up..."), *InSourcePackageName.ToString());
return FindLocalizedPackageNameNoCache(InSourcePackageName, InCultureName);
}
FName FPackageLocalizationManager::FindLocalizedPackageNameNoCache(const FName InSourcePackageName, const FString& InCultureName) const
{
// Split the package name into its root and sub-path so that we can convert it into its localized variants for testing
FString PackageNameRoot;
FString PackageNameSubPath;
{
const FString SourcePackageNameStr = InSourcePackageName.ToString();
TArray<FString> RootPaths;
FPackageName::QueryRootContentPaths(RootPaths);
for (const FString& RootPath: RootPaths)
{
if (SourcePackageNameStr.StartsWith(RootPath, ESearchCase::IgnoreCase))
{
PackageNameRoot = RootPath;
PackageNameSubPath = SourcePackageNameStr.Mid(RootPath.Len());
break;
}
}
}
if (PackageNameRoot.IsEmpty() || PackageNameSubPath.IsEmpty())
{
return NAME_None;
}
const TArray<FString> PrioritizedCultureNames = FInternationalization::Get().GetPrioritizedCultureNames(InCultureName);
for (const FString& PrioritizedCultureName: PrioritizedCultureNames)
{
const FString LocalizedPackageName = PackageNameRoot / TEXT("L10N") / PrioritizedCultureName / PackageNameSubPath;
if (FPackageName::DoesPackageExist(LocalizedPackageName))
{
return *LocalizedPackageName;
}
}
return NAME_None;
}
void FPackageLocalizationManager::ConditionalUpdateCache()
{
if (ActiveCache.IsValid())
{
ActiveCache->ConditionalUpdateCache();
}
}
FPackageLocalizationManager& FPackageLocalizationManager::Get()
{
static FPackageLocalizationManager PackageLocalizationManager;
return PackageLocalizationManager;
}