Unreal UIs and Localization

As a UI programmer, you must be aware of localization and support it, or you're
going to have some very frustrated translators and players. Localization is
a huge topic, but for the purposes of this article we will only talk about the
aspects that affect UI design and implementation:

Making sure that all in-game text is localizable.

Avoiding practices that break localization.

Creating a localization-friendly UI.

Making Sure All In-game Text is Localizable

In order for text to be localizable by Unreal, it must be picked up by the
localization system. Unfortunately this isn't just as simple as entering your
text into Unreal, as only FText variables are localized by Unreal.

FText vs FString vs FName

There are a few variable types in Unreal that are used for text. Each
of them has its own specific purpose, and you need to be careful about which
you use from the start of your project. It's a lot harder to change these
later on, so it's worth taking the time to understand the differences between
the three.

FName

FString

FText

Case-insensitive

Case-sensitive

Case-sensitive

Not localized

Not localized

Localized

Use for IDs

???

Use for all text shown to players

Anything that you want to display to players must be localizable, therefore it
must be stored in an FText variable.

You need to be a real zealot about this. If other developers on the team are
adding data classes, you need to communicate to them that any player-facing
text must be stored as FText.

Skipping Placeholder Text

There are a lot of places in UIs where your UserWidgets will contain TextBlock
widgets, the contents of which will be replaced at run-time from a data asset.

▲ Example of placeholder text that is dynamically replaced when the game is run. As 'Page Title Text' and 'Button Text' will never be shown to players, it it should be marked as 'culture independent' as shown below.

In this example above, by default the placeholder text "Page Title Text" and
"Button Text" will end up in your localization database, and translators will
waste time time translating something that will never be seen by players.

Thankfully it's possible to mark any FText fields that shouldn't be
translated as Culture Independent.

▲ Setting a button's placeholder text as not
localizable will remove it from localization results, simplifying the
translators' job.

Dealing wth Enums

If you are using Enums and you want to display them in-game, there is a EnumToString function but it simply returns an un-localized string. You need to create some other way of creating an FText that is associated with them. Usually the best way is to create a helper library that returns an FText when passed the enum.

// Return a localized string for the enum EWeatherType
UFUNCTION(BlueprintImplementableEvent,BlueprintCallable,Category="Weather")FTextGetTextForWeather(EWeatherTypeWeather)const;

If you have a centralised Blueprint subclass instance that has this function, you will be able to call it from both C++ and Blueprints, and get the right text for your Enum values. And most importantly these will be localized.

Don't break localization

So you've made sure all your text data is stored in FText variables, but
there are still some things you that can accidentally break
localization or make it very hard for translators to do their job
well.

Don't Concatenate Strings

The most obvious and easy way to mess up localization is to concatenate (join
together) strings. The problem with joining together strings is that the
contents of each string is not always guaranteed to be the same between
languages.

For example in your game about cleaning an apartment, your character's log of
things they did could have entries like "Washed dishes", "Ate apple". You could
structure your UI so you have a Verb TextBlock followed by an Thing TextBlock.

However in Japanese, the verb follows the noun, so your game would be
impossible to localize for Japanese without changing the order of UI elements.

This might seem like a contrived example, but there are a few ways to get into
the same situation by accident.

Don't Use Blueprint String Concatenation

▲ The FString-using node Concatenate might seem
logical for sticking two string variables together. However it is not
localizable.

In Blueprints it's super easy to convert between string types (FText, FString
and FName) and it's equally easy to start concatenating text. Especially when
you're rushing to hit a deadline.

Don't Concatenate with UMG Widgets

▲ Putting two text widgets in a horizontal box can make life hard for
translators, as the two words can be in different orders in other languages.

By adding FirstName and LastName TextBlock widgets to a HorizontalBox, you're
implictly creating concatenation in the same way as above.

Do Use FormatText

The solution to all of this concatenation mess is to use FormatText. FormatText uses a string to define how some text should be displayed, and the format string itself is localizable. Using our previous name example, we would create an FText"{FirstName} {LastName}", that defines how full names should be displayed in English.

In Japanese, this string could be localized to "{LastName} {FirstName}", to
allow names to be shown in their preferred order.

▲ FormatText is the correct way of joining
these two variables. The format text itself is localizable and so the word
order can be changed in other languages.

Note that FormatText is not "free" in terms of performance. It's still the only way to make localizable strings, but you should avoid doing it every frame. See the article on UMG and performance for more details.

Making a Localization-friendly UI

At this point if you've followed the advice above, you should have all of the
player-facing text in your game localizable, and you should have set up your UI
in such a way that localization efforts will not be broken by your UI layout.

The final step is to make some efforts to make your UI work really well in
all languages.

Text Length

When designing your game's UI, it was probably done in a single language.
Artists may have designed buttons to fit the size of the text that they
contain. For example a "Start" button might only be the size of 5 characters
plus a little bit of margin.

To make your UI work with multiple languages, you will need to consider how
your TextBlock widgets and their surrounding widgets will react when your
default text is replaced by much longer or much shorter text.

There are a few behaviours you can implement to handle this:

Scale Text to Fit

This is often the simplest to implement, but can lead to the ugliest results.
Wrapping your TextBlock with a ScaleBox will force the text to be shown in
a smaller size if it does not fit the container.

+ All text is visible

– In extreme cases text will not be readable

– A large variety of texts sizes in a UI can look ugly

Wrap Text

Set an explicit width on the container element using a SizeBox and set your
widget to scale to content but only vertically.

+ Text size is preserved

– Container element's size can change significantly

– Generally only works for larger blocks of text

Clip Text With Ellipsis (…)

When the text is longer than the container allows, you replace the last word or
last few characters of the word with an ellipsis character
"…" to show it has been
clipped.

This is not supported by Unreal by default and would require you to write some
custom C++ code to do it.

+ Preserve text size

+ Preserve container widget size

– Can cut off important information

– What text is cut off is unknown to translators unless they play the game

– Can look kind of ugly

Marquee

Scrolling or bouncing text within a box, so some of it is clipped. Think of
scrolling text that's shown on a LED billboard.

This is not supported by Unreal by default and would require you to write some
custom C++ code to do it.

If your UI is sci-fi or futuristic, this could fit quite well with the
aesthetic. But for a game about elves and wizards, a marquee would probably be
out of place.

+ Preserve container widget size

– Only works with very short text

– Depending on UI style, could not fit

Right-to-Left languages

If you've achieved all of this, you could consider how you will support the
holy grail of localization, right-to-left languages.

This is not just a simple case of replacing text as with left-to-right
languages. To have a truly natural-feeling UI, you will need to flip entire
elements or entire screens for your UI to feel natural to right-to-left
language users.