Localizing your game UI
This guide takes a menu from hard-coded English to a game that speaks the player's language and lets them switch it:
keys in markup, one .po file per language, a language setting, text built in code, and a layer for community
translations.
You need a page loaded from markup (Your first screen). The Localization page describes the pieces; this guide wires them together. The settings page is the one from Building a game menu.
1. Use keys in markup
Replace each visible text with {Localize Key}. Keys are names you choose; Menu.Play reads better than the English
sentence, and survives a rewording of it:
<!-- A main menu whose texts are keys; the combo box picks the language. The data context is a LanguageSetting. -->
<StackPanel Orientation="Vertical" HorizontalAlignment="Center" VerticalAlignment="Center" Width="280">
<TextBlock x:Name="Title" FontSize="32" Margin="0,0,0,16" Text="{Localize Menu.Title}" />
<Button x:Name="Play" Margin="0,0,0,8" Content="{Localize Menu.Play}" />
<TextBlock Margin="0,16,0,4" Text="{Localize Settings.Language}" />
<ComboBox x:Name="Language" ItemsSource="{Binding Languages}" SelectedItem="{Binding Selected, Mode=TwoWay}" />
</StackPanel>
2. Write the translation files
Create a Localization folder next to the game with one gettext file per language, en.po and ru.po:
# Localization/en.po
msgid "Menu.Title"
msgstr "My Game"
msgid "Menu.Play"
msgstr "Play"
msgid "Settings.Language"
msgstr "Language"
# Localization/ru.po
msgid "Menu.Title"
msgstr "Моя игра"
msgid "Menu.Play"
msgstr "Играть"
msgid "Settings.Language"
msgstr "Язык"
Copy the files to the build output. Translators open them in Poedit (or a service such as Weblate or Crowdin), so give
them the files and the keys' meaning; they don't need your code. A player can add de.po to the folder and get a new
language without a new build.
3. Turn the localizer on
Game.UseIcyUI(ui => ui
.ConfigureAssets()
.UsePoLocalization("Localization", CultureInfo.GetCultureInfo("en"))); // Localization/en.po, ru.po, ...
en is the default language: a key missing from ru.po shows its English text rather than the key. The player's system
language is used until you set one.
4. Add a language setting
A combo box on the settings page sets the configuration's Culture; every {Localize} text changes at once, and numbers
bound in the UI switch to the new culture's format:
/// <summary>The Settings page's language choice. Choosing a language switches every localized text at once.</summary>
public sealed partial class LanguageSetting : ObservableObject
{
private readonly IcyConfiguration configuration;
private string selected;
public LanguageSetting(IcyConfiguration configuration)
{
this.configuration = configuration;
selected = NameOf(configuration.Culture);
}
/// <summary>The languages the game ships, as the player sees them (not translated).</summary>
public IReadOnlyList<string> Languages { get; } = ["English", "Русский"];
public string Selected
{
get => selected;
set
{
if (SetProperty(ref selected, value))
configuration.Culture = CultureInfo.GetCultureInfo(value == "Русский" ? "ru" : "en");
}
}
private static string NameOf(CultureInfo culture) => culture.TwoLetterISOLanguageName == "ru" ? "Русский" : "English";
}
Give the setting its own data context on the settings page, as the game menu guide does for its other settings. Show
each language by its own name, not translated, so a player who ended up in the wrong language can find theirs. Save the
choice with the rest of the player's settings and set Culture from it at startup.
5. Text built in code
score.Localize(nameof(TextBlock.Text), "Hud.Score");
This text follows the language like markup does. For a string you format yourself, call Configuration.Localize("Hud.Score").
6. Community translations
To let players override or add strings from a Mods/Localization folder, chain two localizers. Give only the last one a
default language:
var mods = new PoLocalizer(configuration.Assets, "Mods/Localization"); // no default: only the player's language
var game = new PoLocalizer(configuration.Assets, "Localization", CultureInfo.GetCultureInfo("en"));
configuration.Assets.Localizer = new CompositeLocalizer(mods, game);
7. Fonts
A language needs a font with its letters. If the default font misses Cyrillic or CJK glyphs, text shows as boxes. See Assets and fonts to choose one that covers your languages.
What you built
A menu with no text in code or markup, a translation file per language, a setting that switches language while the game runs, and a folder where players can add their own.
Note
Text with arguments ("Score: {0}") isn't filled in for you: localize the format string with
Configuration.Localize and format it in code.
Next
- Localization: the localizers, fallbacks and pitfalls.
- Data binding: numbers and dates in the current culture.