String Tables "Missing String Table Entry" inside DataTables after updating to UE 5.4

Still problem here. Missing String Tables show and localization dashboard cannot gather texts :frowning:

I was having a similar issue (and confusion) on earlier versions, is this code still of use?

Confusion with String Table localization, c++ - #3 by Roy_Wierer.Seda145

I am on 4.2, gathered the texts, confirmed that they are there, and yet these specific texts do not show up and i also did the GameInstance BP change to the locale I gathered (“en”)

Hmm, i restarted the project, collected the texts again, Editor got stuck while I tried to edit the translations just to look at what it gathhered, so then I restarted the editor again, and now it works.

So many bugs in this crucial system :stuck_out_tongue:

This needs to be solved ASAP. So many marketplace plugins can’t update to 5.4 because of it.

1 Like

It was finally fixed in 5.4.2!

3 Likes

Is this an issue in 5.3? Or is it only meant to be a problem for 5.4? Because I’m having the same problem on 5.3 outside of data tables…

Update: It needs a “/” at the very start.

I solved this for me by adding the folder where my tables were to my packaging. Please let me know if this is a bad idea, but nothing else seems to fix it.

That’s actually required for them to be loaded if not referenced elsewhere (by a pointer in a UAsset). Otherwise you will get missing strings when attempting to localize from code, say by calling "FText::FromStringTable(InStringTableId, InKey)"

A “reference” from c++ does not count. For this reason I prefer to just add my string tables (and datatables too) directly from c++ to the scan settings, done from C++ like this:

/**Copyright: Roy Wierer (Ferrefy). All Rights Reserved.**/
#include "AssetUtils.h"
#include "Modules/ModuleManager.h"
#include "PropertyEditorModule.h"
#include "PropertyEditorDelegates.h"
#include "Settings/ProjectPackagingSettings.h"
#include "Engine/AssetManagerSettings.h"


void UAssetUtils::AddDirectoryToScanAndCook(const FString& InDirectory, const FName& InAssetType, const TSoftClassPtr<UObject> InAssetBaseClass) {
	UProjectPackagingSettings* PackagingSettings = GetMutableDefault<UProjectPackagingSettings>();
	check(IsValid(PackagingSettings));

	// For whatever reason the datatype required is a poorly implemented directory one, containing just a string path.
	const FDirectoryPath* PathX = PackagingSettings->DirectoriesToAlwaysCook.FindByPredicate([&InDirectory](const FDirectoryPath& PathX) { return PathX.Path == InDirectory; });
	if (PathX == nullptr) {
		FDirectoryPath NewPath = FDirectoryPath();
		NewPath.Path = InDirectory;
		PackagingSettings->DirectoriesToAlwaysCook.Add(NewPath);
	}

	UAssetManagerSettings* AssetManagerSettings = GetMutableDefault<UAssetManagerSettings>();
	check(IsValid(AssetManagerSettings));
	check(!InAssetBaseClass.IsNull());

	/*
	* The following error appears when managing assets in the asset manager which don't implement GetPrimaryAssetID, which is oddly.... the datatable and string table.
	* [UE5.1.1] LogAssetManager: Error: Registered PrimaryAssetId DataTable:DT_XXXX for asset /SomePlugin/DT_XXXX.XXXX does not match object’s real id of ! This will not load properly at runtime!
	* Setting the bShouldManagerDetermineTypeAndName to true works around this.
	*/
	AssetManagerSettings->bShouldManagerDetermineTypeAndName = true;

	/**
	* Note that there can only be one entry in the array per type, so merge the rules into an existing one:
	* LogAssetManager: Error: Found multiple "StringTable" Primary Asset Type entries in "Primary Asset Types To Scan" config. Only a single entry per type is supported.
	*/

	FPrimaryAssetTypeInfo* ExistingInfo = AssetManagerSettings->PrimaryAssetTypesToScan.FindByPredicate([&InAssetType](const FPrimaryAssetTypeInfo& InfoX) { return InfoX.PrimaryAssetType == InAssetType; });
	if (ExistingInfo == nullptr) {
		FPrimaryAssetTypeInfo NewInfo = FPrimaryAssetTypeInfo();
		NewInfo.PrimaryAssetType = InAssetType;
		NewInfo.SetAssetBaseClass(InAssetBaseClass);
		FDirectoryPath NewDir = FDirectoryPath();
		NewDir.Path = InDirectory;
		NewInfo.GetDirectories().Add(NewDir);
		NewInfo.Rules.CookRule = EPrimaryAssetCookRule::AlwaysCook;
		AssetManagerSettings->PrimaryAssetTypesToScan.Add(NewInfo);
	}
	else {
		// See if the directory exists already and return if so.
		if (ExistingInfo->GetDirectories().ContainsByPredicate([&InDirectory](const FDirectoryPath& PathX) { return PathX.Path == InDirectory; })) {
			return;
		}

		// Let's not assume there is an existing entry with valid data, just because we can. Set the properties we need.
		//ExistingInfo->PrimaryAssetType = InAssetType; // We already know this is set.
		ExistingInfo->SetAssetBaseClass(InAssetBaseClass);
		FDirectoryPath NewDir = FDirectoryPath();
		NewDir.Path = InDirectory;
		ExistingInfo->GetDirectories().Add(NewDir);
		ExistingInfo->Rules.CookRule = EPrimaryAssetCookRule::AlwaysCook;
	}
}

And called like this:

/**Copyright: Roy Wierer (Ferrefy). All Rights Reserved.**/
#include "CustomSettingsPluginEditorInstaller.h"
#include "AssetUtils.h"
#include "Engine/DataTable.h"
#include "Internationalization/StringTable.h"


// Setup

void FCustomSettingsPluginEditorInstaller::RunAutomatedInstaller() {
	UAssetUtils::AddDirectoryToScanAndCook(TEXT("/CustomSettingsPlugin/Localization/Text"), TEXT("StringTable"), UStringTable::StaticClass());
	UAssetUtils::AddDirectoryToScanAndCook(TEXT("/CustomSettingsPlugin/Data/DataTables"), TEXT("DataTable"), UDataTable::StaticClass());
}

Users just localizing UI through a text node in a UAsset (selecting a datatable and key) should not have to go through all that, since their node references the string table for them.