1.14.0.5
Coherent HB for UE4
A modern user interface library for games
Localization of Hummingbird resources in Unreal Engine 4

The purpose of this tutorial is to show you how in a few simple steps to use the built-in localization feature of UE4 along with Hummingbird. For a localization ready sample you can check our localized menu map - MenuLocalization, located in the HummingbirdFPS sample project. At the end of this tutorial, you will know everything you need to create a localized UI with Hummingbird.

The localization workflow is the following:

  1. In the UI HTML page, declare the text that will be translated.
  2. Generate Translation manifest and archive files.
  3. Enter the translation strings for texts that will be translated.
  4. Generate the translation manifest and archive files again to rebuild the binary localization file.
  5. Change engine’s current culture and do the C++/HTML binding.
  6. Enable Hummingbird's localization facility.
Note
Due to Unreal Engine localization restrictions the game localization will not be loaded in Editor mode. You can enable game mode by adding the -game argument to the project's command-line arguments.

Step 1: Declaring strings that will be translated

In this step we will tag the buttons that will have their text translated in the HTML and declare that we want translation for a text entry.

The Menu map loads the coui://UIResources/LocalizedMenu/menu.html page, which will be our starting point. In the page you can find some CSS includes, JavaScript includes and most importantly – the 3 menu buttons. In the button tag of the start button we will add a property called data-l10n-id and set its key to MenuNamespace.NewGameButton e.g.

<button id="play" class="btn btn-large btn-primary" type="button"
 data-l10n-id="MenuNamespace.NewGameButton">Play</button>

Note: To demonstrate how the current localization language can be changed at runtime, there are two additional buttons that switch the current language to English/Spanish.

Now we have to declare that we want a translation for the "NewGameButton" key. In the menu.html file we will add a comment containing the following declaration:


It doesn't matter where in the HTML page the comment is. Now, what is this C++ like declaration and why we need it? NSLOCTEXT is a C++ macro, used by UE4 that declares a new localization text. It takes 3 parameters:

  1. Localization Namespace – it allows localization texts to be grouped together, and thus allow easier bookkeeping – e.g. all button names can be in the "MenuNamespace".
  2. Key – unique identifier for a namespace, used together with a namespace to identify a localized text entry
  3. Text Literal – the text literal is a sample text that is used to describe the key. If a translation for the current Culture is not found, the Text Literal will be used instead.

Step 2: Generate translation manifest and archive files

The UE4 localization system works with three types of files:

  • .manifest - contains JSON code describing namespaces, keys, text literals and the file/line where the localized text is. Most probably they are used to generate .archive files.
  • .archive - it has a very similar structure to .manifest file, but instead of the location of the localized text, it contains the translation of the localized text for a specific language.
  • .po - it does the same job as the .archive files but in another well known localization format, it contains the translation of the localized text for a specific language.
  • .locres - a binary file that contains the binary representation of the localized texts for a specific language. The UE4 localization system can only read from .locres files.

Now, back to the practical part. The generation of the manifest, archive and locres files happens with the help of a Unreal Engine commandlet called GatherText. Commandlets are mini-programs with a specific purpose. In this case the commandlet reads a configuration file and executes the steps defined in it. The commandlet is called with:

PathToEditor\UE4Editor.exe FullPathToYourGame\YourGame.uproject -run=GatherText
-config=FullPathToYourGame\Config\Localization\YourGame.ini

or in our case:

E:\unreal\Engine\UE4\Engine\Binaries\Win64\UE4Editor.exe E:\unreal\HummingbirdFPS\HummingbirdFPS.uproject
-run=GatherText -config="E:\unreal\HummingbirdFPS\Config\Localization\HB.ini" -log > localization.log

Note: The -log > localization.log bit is optional and is used to generate a localization.log file. The log will be created in your current working directory and will contain the output log of the GatherText commandlet. If something goes wrong, you can check in the log what and where.

The configuration Localization/HB.ini file contains a list of settings and series of steps that will be executed by the GatherText commandlet. The configuration that I have used is the following:

[CommonSettings]
SourcePath=./Content/Localization/HummingBird
DestinationPath=./Content/Localization/HummingBird
ManifestName=HummingBird.manifest
ArchiveName=HummingBird.archive
ResourceName=HummingBird.locres
PortableObjectName=HummingBird.po
NativeCulture=en
SourceCulture=en
CulturesToGenerate=en
CulturesToGenerate=fr

;Get all FTexts from source code
[GatherTextStep0]
CommandletClass=GatherTextFromSource
SearchDirectoryPaths=./Source/HummingbirdFPS
SearchDirectoryPaths=./Content/uiresources
;File extensions that will be parsed
FileNameFilters=*.cpp
FileNameFilters=*.h
FileNameFilters=*.html

;Gather text from assets in content.
[GatherTextStep1]
CommandletClass=GatherTextFromAssets
IncludePaths=./Content/
ExcludePaths=*/Content/Localization/*
PackageExtensions=*.umap
PackageExtensions=*.uasset

;Create manifest with all gathered source text.
[GatherTextStep2]
CommandletClass=GenerateGatherManifest

;Generate Archives
[GatherTextStep3]
CommandletClass=GenerateGatherArchive
bPurgeOldEmptyEntries=true

;Export new source from existing archives into PO (portable object) files.
[GatherTextStep5]
CommandletClass=InternationalizationExport
bExportLoc=true

;Import new translations from PO (portable object) files into existing archives.
[GatherTextStep6]
CommandletClass=InternationalizationExport
bImportLoc=true

;Compile source text and translations into binary form for use by the application.
[GatherTextStep7]
CommandletClass=GenerateTextLocalizationResource

Most of the options in the configuration file are self-explanatory, but there are some things which I would like to point out:

  • CulturesToGenerate=en, CulturesToGenerate=fr and CulturesToGenerate=bg – used to generate English and French localizations. For every language/locale you want to have a localization, you must add a row there with the language/region code.
  • SourceFileSearchFilters=*.html – used to tell the parser to look inside .html files.
  • SourcePath and DestinationPath pointing to D:/unreal/HummingbirdFPS/Content/Localization/HummingBird – this is a default path used to find .locres files by the engine. If your files are in it, they should be automatically loaded by the engine.
  • The search filters for this samples are set to .h, .cpp, .html.

Step 3: Enter the translation strings for texts that will be translated

After you have run the commandlet, in

`YourGame/Content/Localization/HummingBird`

In order for these translations take effect in your game, in DefaultGame.ini you should add: [Internationalization] +LocalizationPaths=GAMEDIR%/Content/Localization/HummingBird

[/Script/UnrealEd.ProjectPackagingSettings] InternationalizationPreset=English +CulturesToStage=en +CulturesToStage=fr +CulturesToStage=bg DefaultCulture=en

Now we would like to translate the key for NewGameButton (from section 1) in French. In order to do so, navigate to YourGame/Content/Localization/HummingBird/fr and open Hummingbird.archive. In the Translation field you should enter the translation of Start Button in French.

"Subnamespaces": [
    {
        "Namespace": "MenuNamespace",
        "Children": [
            {
                "Source":
                    {
                        "Text": "Start new game"
                    },
                    "Translation":
                    {
                        "Text": "Commencer une nouvelle partie"
                    },
                    "Key": "NewGameButton"
            }
        ]
    },
]

Step 4: Generate translation manifest and archive files again

After we have filled the empty translation fields, we have to run the same commandlet again:

E:\unreal\Engine\UE4\Engine\Binaries\Win64\UE4Editor.exe E:\unreal\HummingbirdFPS\HummingbirdFPS.uproject
-run=GatherText -config="E:\unreal\HummingbirdFPS\Config\Localization\HB.ini" -log > loc.log

Once the translation strings are filled, re-running the commandlet will not erase the existing translation entries and will re-generate the binary .locres file. If you are not sure if the French translation made it to the .locres file, you can open the YourGame/Content/Localization/HummingBird/fr/Hummingbird.locres file with a hex editor and inspect its contents.

Step 5: Change engine's current culture and do the C++/HTML binding.

After we have generated the locres files with the localized tests it is time to head to the UI again and bind it to the game.

HTML

Hummingbird will automatically translate the elements tagged with data-l10n-id using the default locale. To dynamically change the language, we create a few buttons:

// buttons for changing localization in menu.html
<input type="button" id="en" class="btn language-menu-item selected-language" value ="English">
<input type="button" id="fr" class="btn language-menu-item" value="Francais (French)">

And then add JS code to add binding and localization reloading:

// js for changing localization in menu.html
var changeLanguage = function (language) {
    engine.call('ChangeLanguage', language).then(function () {
        engine.reloadLocalization();
    });
};

// Attach click handlers
var onLanguageChanged = function (eventArgs) {
    var button = eventArgs.target;
    // Change the current language to the value of data-language of the current button
    changeLanguage(button.id);
    // Move the selected-language class to the current button

    var buttons = document.getElementsByClassName('selected-language');
    for (var i = 0; i < buttons.length; i++) {
        var prevbutton = buttons.item(i);
        prevbutton.classList.remove('selected-language');
    }

    button.classList.add('selected-language');
};

var localizationChangers = document.getElementsByClassName('language-menu-item');
for (var i = 0; i < localizationChangers.length; i++) {
    var button = localizationChangers.item(i);
    button.addEventListener('click', onLanguageChanged);
}

C++

  • Setup methods for changing the current culture. You can find them in HummingbirdFPSLocalizationHUD
void AHummingbirdFPSLocalizationHUD::ChangeLanguage(FString Language)
{
    FInternationalization::Get().SetCurrentCulture(Language);
}
  • Bind these methods to the UI
// In the HUD's constructor
HummingbirdHUD->ReadyForBindings.AddDynamic(this,
   &AHummingbirdFPSLocalizationHUD::BindUI);

void ACoUIGTTestFPSMenuHUD::BindUI()
{
    HummingbirdHUD->GetView()->BindCall("ChangeLanguage",
        cohtml::MakeHandler(this, &AHummingbirdFPSLocalizationHUD::ChangeLanguage));
}

Step 6: Enable Hummingbird's localization facility.

By default Hummingbird disables localization to allow faster loading. To enable it, inside UE4's editor navigate to Hummingbird -> Options and check Enable Localization. By doing this, Hummingbird will automatically translate any elements tagged with the data-l10n-id attribute.