NSLOCTEXT -- or -- FText::FindText ?

The NSLOCTEXT is used for c++ localization in general. It’s a macro to the following method:


/**
 * Creates an FText. All parameters must be string literals. All literals will be passed through the localization system.
 */
#define NSLOCTEXT(InNamespace, InKey, InTextLiteral) FInternationalization::ForUseOnlyByLocMacroAndGraphNodeTextLiterals_CreateText(TEXT(InTextLiteral), TEXT(InNamespace), TEXT(InKey))

The method says it’s apparently reserved for the macro and graph nodes, but nothing stops me from calling it. I don’t like macros because I’d rather be able to use variables like this:

UFUNCTION(BlueprintCallable, Category = "BPFL|Utils|Localization", meta = (CallableWithoutWorldContext))
static FText Localize(const FName& InNameSpace, const FString& InKey, bool bWarnIfFail = true);

Since the Macro doesn’t take variables… I thought FText::FindText (By its description) should do the same thing. Yet there are multiple complex systems involved which seem to work totally different… So what is FText::FindText good for? is it fine to use instead of NSLOCTEXT? Not much info on the webs.

If you’re working with variables, it’s usually better to just deal with an FText variable and let whatever other system deal with figuring out where it’s coming from, either in native using LOCTEXT or NSLOCTEXT, in blueprint using that Localize function, or in blueprint and assets just declaring an FText property and letting the default deal with all that stuff.

In native you shouldn’t ever really have a reason that you need to manually localize something with variables that you don’t know the contents of.

1 Like

I agree with that :slightly_smiling_face: I’d rather not know what dark magic processes the text property as long as it just works. There is this very specific case for which I need a more dynamic system than the macro in c++, this is a procedurally generated list of input mappings. Whenever a mapping is not present in a string table the log generates an error such as “Action_Jump” / “Action_Jump_Description” / “Axis_MoveForward” / “Axis_MoveForward_Inverse” / “Axis_MoveForward_Inverse_Description” is not present. Here the middle part is the input mapping name taken from the configured mappings and displayed in a procedurally generated list. I wouldn’t want the widget designer to have to add and configure a 100+ keybind environment on just one panel. This leaves me with little option but to figure out how to work with the FText dark magic?

To be clear I do get the right results with FText::FindText usually, sometimes the result is empty. I had to screw around a bit to make sure the string tables are loaded as well. The issues is that sometimes it gives an empty result and I want to make absolutely sure it is not interfering with any “standard way” of localizing in the engine. I have a different issue just from reading text properties which is most likely not related but I need to be sure one does not cause a bug in the engine to the other anyhow. I disabled FText::FindText for now so I can debug if the issue in the post below is unrelated. meanwhile I need to find a solution to be able to localize a dynamic string the correct way in c++

[UE5.1.1, Bug report] Widget does not set text unless asset is reloaded. - #13 by Roy_Wierer.Seda145

I’m going to test a bit with NSLOCTEXT on that input system because that doesn’t require the namespace variable of that more generic localize utility i showed anyway. Still curious if FText::FindText is any good at all. I rather be able to store my namespaces in a static constant like “UCoreUtils::LocaleNSHID” than writing it all over the place.

From reading the documentation FText::FindText would be used to check if a localization has been set.

NSLOCTEXT - short for name space localized text = takes in a namespace, key & a literal

LOCTEXT - is a macro that doesn’t let you define the namespace (enforces LOCTEXT_NAMESPACE), the rest is the same as NSLOCTEXT

NSLOCTEXT creates the localization and FText::FindText would be used to find if it that localization combo exists.

Regarding the title It’s not really an ‘or’ question as their functions don’t really intersect. (only the domain that they operate on).

And it seems NSLOCTEXT is sprinkled liberally around the engine code so the I’m guessing it started out with that restriction and then was incorporated nearly everywhere.

2 Likes

NSLOCTEXT should take a namespace string. It’s possible that you’re requesting text for the input mapping name in multiple places that have an inconsistent namespace.

Personally I’d write something that acts as the authoritative source for action display names so that you can make the namespacing consistent. That way multiple systems aren’t accidentally trying to use different ones specified through LOCTEXT_NAMESPACE.

I think any solution where you’re making code actually aware of the namespaces at runtime is probably the start of a mistake. But that’s me.

What do you mean by this? I wrote helper methods in my core to get input mappings localized, it uses a namespace (static const FName instead of Macro) and the action string is put together automatically.

FText ULocalizationUtils::LocalizeInputActionName(const FName& InInputActionName) {
	const FString Key = FString("Action_" + InInputActionName.ToString());
	return Localize(UCoreUtils::LocaleNSHID, Key);
}

Reason I made the namespace a static const FName and not macro is because i don’t want to be the guy causing a compile error in another plugin because a macro has already been defined XD I avoid them at all costs.

In this case of localizing the input mappings procedurally with the prefixes and suffixes in place is more of a challenge than the namespaces which could be made macro in case nothing else works :stuck_out_tongue:

If you do a search through the engine code - It appears the common practice is to use LOCTEXT() - defining LOCTEXT_NAMESPACE at the begining of the code as mentioned here, but doing an #undef of it at the end of the file to avoid any dangling defines - this seems to be even more important as of 5.2 now the compiling is no longer monolythic…

1 Like

Yeah, that’s mostly what I was talking about. I may not have thought it all the way through. But that just gets the text from the localization data, it’s not going to cause it to be included in the loc data so that the strings are actually localized.

I’d have an actual TMap in data somewhere that’s something like FName => FText that this function uses to look it up. That way the FText’s will be found by the localization process and end up in the .po file.

The way localization works (apologies if you already know this) is that there’s a process that goes digging through assets for FText and goes digging through code for the NSLOCTEXT and LOCTEXT macros to generate the table of namespaces/keys/english text. So if you try to do something outside that paradigm (like trying to call this localize utility) it may not work the way you expect. When Localize is used in blueprint that might be hooked into this process too, or it’s ignored and it’s assumed it’s referenced someplace else. Localization needs to know compile time values which is why the macros only work on literal, so that the tools works to pull all the data back out.

Thanks for this info, this changes the way i look at localization. I don’t quite get why if the namespace / key / translation is already known from the string tables. I might have a problem with the procedurally generated input mapping localization . so far the localize method I had does work in PIE with the native language. I need to test all this what we found so far and see what works after culture swapping in a packaged game.

So this means that if I do have an entry in a string table, but there is no “reference” to the entry found by the localization system, it won’t be processed at all? I do enforce cooking and packaging of string tables but I was not aware of that.

I found something cool :slightly_smiling_face: It seems don’t actually have to scan c++ files with the localization dashboard. All my texts are present only in string tables which i do scan

Even if I don’t reference a string table entry anywhere, it will show up in exported po files and it will compile into the locres, archive and manifest files. Looks just fine.

Probably there are other cases where the macro is still required then. Ofc I’d need to scan source for NSLOCTEXT if they were not already in string tables.

That’s great! I don’t recall any string tables coming up with our localization, but I wasn’t the person actually managing that stuff for our project.

1 Like

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.