Localization Guide
On this page
Localization of Prysm resources in Unreal Engine
The purpose of this tutorial is to show you how to use the built-in localization feature of UE4 with Prysm in a few simple steps. For a localization-ready sample, you can check our localized menu map - MenuLocalization, located in the CoherentSample sample project. At the end of this tutorial, you will know everything you need to create a localized UI with Prysm.
LocalizationManager
class or integrate Prysm into an existing localization system, you can do so by providing your custom ILocalizationManager
implementation and passing it to the system via the OnGetLocalizationManager
delegate, located inside ICohtmlPlugin.h
The localization workflow is the following:
- In the UI HTML page, declare the text that will be translated.
- Generate Translation manifest and archive files.
- Enter the translation strings for texts that will be translated.
- Generate the translation manifest and archive files again to rebuild the binary localization file.
- Change the engine’s current culture and do the C++/HTML binding.
- Enable Prysm’s localization facility.
-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 HTML and declare that we want a translation for a text entry.
The Menu map loads the coui://UIResources/LocalizedMenu/menu.html
page, which will be our starting point. On 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>
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:
<!--NSLOCTEXT("MenuNamespace", "NewGameButton", "NewGameButton translation in every language")-->
It doesn’t matter where on the HTML page the comment is located. Now, what is this C++-like declaration and why do we need it? NSLOCTEXT
is a C++ macro, used by Unreal that declares a new localization text. It takes 3 parameters:
Localization Namespace
– it allows localization texts to be grouped together, and thus allows for easier bookkeeping – e.g. all button names can be in the “MenuNamespace”.Key
– a unique identifier for a namespace, used together with a namespace to identify a localized text entry.Text Literal
– the text literal is a sample text that is used to describe the key. If a translation for the currentCulture
is not found, theText Literal
will be used instead.
Step 2: Generate translation manifest and archive files
The Unreal Engine 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 the.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 also 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 Unreal 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 an 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\CoherentSample\CoherentSample.uproject -run=GatherText -config="E:\unreal\CoherentSample\Config\Localization\CoherentSample.ini" -log=localization.log
-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 the log what and where.The configuration Localization/CoherentSample.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/CoherentSample
DestinationPath=./Content/Localization/CoherentSample
ManifestName=CoherentSample.manifest
ArchiveName=CoherentSample.archive
ResourceName=CoherentSample.locres
PortableObjectName=CoherentSample.po
NativeCulture=en
SourceCulture=en
CulturesToGenerate=en
CulturesToGenerate=fr
;Get all FTexts from source code
[GatherTextStep0]
CommandletClass=GatherTextFromSource
SearchDirectoryPaths=./Source/CoherentSample
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 that should be pointed out:
CulturesToGenerate=en
,CulturesToGenerate=fr
andCulturesToGenerate=bg
– used to generate English and French localizations. For every language/locale you want to have localization for, you must add a row in the config file with the language/region code.SourceFileSearchFilters=*.html
– used to tell the parser to look inside.html
files.SourcePath
andDestinationPath
pointing toProjectName/Content/Localization/CoherentSample
– 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
`ProjectName/Content/Localization/CoherentSample`
In order for these translations to take effect in your game, in DefaultGame.ini you should add:
[Internationalization]
+LocalizationPaths /Content/Localization/CoherentSample
[/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) into French. In order to do so, navigate to ProjectName/Content/Localization/CoherentSample/fr
and open CoherentSample.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\CoherentSample\CoherentSample.uproject -run=GatherText -config="E:\unreal\CoherentSample\Config\Localization\CoherentSample.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 ProjectName/Content/Localization/CoherentSample/fr/CoherentSample.locres
file with a hex editor and inspect its contents.
Step 5: Change the 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
Prysm will automatically translate the elements tagged with data-l10n-id
using the default locale. To dynamically change the language, we need to 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 the 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 CoherentSampleLocalizationHUD
void ACoherentSampleLocalizationHUD::ChangeLanguage(FString Language)
{
FInternationalization::Get().SetCurrentCulture(Language);
}
- Bind these methods to the UI
// In the HUD's constructor
CohtmlHUD->ReadyForBindings.AddDynamic(this,
&ACoherentSampleLocalizationHUD::BindUI);
void ACoUIGTTestFPSMenuHUD::BindUI()
{
CohtmlHUD->GetView()->BindCall("ChangeLanguage",
cohtml::MakeHandler(this, &ACoherentSampleLocalizationHUD::ChangeLanguage));
}
Step 6: Enable Prysm’s localization facility.
Localization is disabled by default in Prysm. To enable it, navigate to Prysm -> Options inside the Unreal Engine Editor and check Enable Localization. By doing this, Prysm will automatically translate any elements tagged with the data-l10n-id
attribute.