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:
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:
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".Key
– unique identifier for a namespace, used together with a namespace to identify a localized text entryText 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.The UE4 localization system works with three types of 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.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" } ] }, ]
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.
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.
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); }
void AHummingbirdFPSLocalizationHUD::ChangeLanguage(FString Language) { FInternationalization::Get().SetCurrentCulture(Language); }
// In the HUD's constructor HummingbirdHUD->ReadyForBindings.AddDynamic(this, &AHummingbirdFPSLocalizationHUD::BindUI); void ACoUIGTTestFPSMenuHUD::BindUI() { HummingbirdHUD->GetView()->BindCall("ChangeLanguage", cohtml::MakeHandler(this, &AHummingbirdFPSLocalizationHUD::ChangeLanguage)); }
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.