reckfellENGINE

Internationalizing the game and UI

Automatic translation, computed text, resource remaps, fonts, RTL and startup checks.

On this page

An internationalized game separates message lookup, player language selection, text rendering and localized assets. A correct translated string does not prove that the font, layout or resource is correct. Check each layer independently.

Prepare the project

  1. Import and register RU/EN catalogs using translation setup.
  2. Keep stable keys for UI text. Use context when one source word has different meanings; preserve placeholders in every translation.
  3. Choose only among languages the game ships. Apply a saved player preference after loading settings; otherwise select from the OS locale with an explicit fallback. The locale guide supplies a complete RU/EN resolver.
  4. Give the player a language selector. TranslationServer.SetLocale changes the current language, but does not save the choice.

Use CSV or PO as the source of translations. Do not replace catalog-based translation with a separate if/else for every label.

Automatic text, calculated text and player text

KindApproach
Static Label/Button captionStore the translation key in Text; enable automatic translation
Count, formatted result or generated sentenceLook up the complete template, choose plural form if needed, format it, and refresh on a translation-change notification
Player name, chat, external textDisable automatic translation; display the supplied text

A Label retains its source key in Text while its internal translated text changes. Do not read Text expecting the displayed translation. Disabled automatic translation can be inherited from an ancestor; Always can override that inheritance for an individual Entity.

A complete localized HUD

Create a VBoxContainer and attach LocalizedHud.cs below. Register tables containing PLAY_KEY and plural keys ITEM/ITEMS. PLAY_KEY should translate to Play/Играть; use the CSV plural table for ITEM/ITEMS.

C#
using Reckfell;

public partial class LocalizedHud : VBoxContainer
{
    public Label PlayLabel { get; private set; } = null!;
    public Label PlayerLabel { get; private set; } = null!;
    public Label CountLabel { get; private set; } = null!;
    public int ItemCount { get; set; } = 5;

    public override void _Ready()
    {
        PlayLabel = new Label { Text = "PLAY_KEY" };
        PlayerLabel = new Label
        {
            AutoTranslateMode = AutoTranslateModeEnum.Disabled,
            Text = "PLAY_KEY"
        };
        CountLabel = new Label { AutoTranslateMode = AutoTranslateModeEnum.Disabled };
        AddChild(PlayLabel);
        AddChild(PlayerLabel);
        AddChild(CountLabel);
        RefreshCount();
    }

    public override void _Notification(int what)
    {
        if (what == NotificationTranslationChanged && CountLabel != null)
            RefreshCount();
    }

    public void RefreshCount()
    {
        string form = TranslationServer.TranslatePlural("ITEM", "ITEMS", ItemCount).ToString();
        CountLabel.Text = string.Format(form, ItemCount);
    }

    public void SetGameLocale(string locale)
    {
        TranslationServer.SetLocale(locale);
    }
}

Call SetGameLocale("en"), then SetGameLocale("ru"). The first line changes from Play to Играть; the second remains literally PLAY_KEY, simulating a player name identical to a key; the counter changes from 5 items to 5 предметов. After changing ItemCount, call RefreshCount. Replace the diagnostic player-name value with your actual player text.

TranslatePlural selects a template; string.Format inserts the count. Do not concatenate separately translated fragments: translators may need a different word order. The complete message and its placeholders belong in the catalog.

Context must reach the lookup

For an automatic Control, set TranslationContext before assigning Text. For computed text, pass context directly to TranslationServer.Translate. Changing TranslationContext alone currently does not recalculate an existing Label's translated buffer. To refresh after a context change, call label.PropagateNotification((int)Entity.NotificationTranslationChanged), where label is your existing Label. Reassigning the same Text value alone does not force a refresh.

Localized resources

In Project Settings → Localization → Remaps, select an original resource, add alternatives and assign their locales. Continue loading the original path in game code. Loading an alternative path directly bypasses selection for the original.

This project.reckfell setting demonstrates remapping with Gradient resources; merge it into the existing internationalization section while the editor is closed:

Configuration
[internationalization]
locale/translation_remaps={"asset://Locale/palette.rdata": PackedStringArray("asset://Locale/palette-en.rdata:en", "asset://Locale/palette-ru.rdata:ru")}

Create all three Gradient resources first. Load asset://Locale/palette.rdata. Changing locale reloads a loaded, remapped resource; the cached object can retain its identity while its contents change. For a locale with no matching alternative, the original resource is used. The text fallback locale does not automatically supply a resource alternative: provide an appropriate original.

Use the editor's remap workflow for localized images or voices, then test their actual consumers. A resource reload is not proof that an audio player restarts playback or that every cached copy in your code updates. Keep translated asset dimensions, timing and references compatible with the consumer.

Fonts and layout

Use the current Font/FontFile resources and Font.Fallbacks, not a DynamicFont resource. Set reusable typography through a Theme, for example its DefaultFont, rather than replacing every Control separately. Provide fonts covering the actual text, including player text that can differ from the selected UI language. Adding a fallback resource does not prove glyph coverage; test the required characters and respect each font's license.

Label.Language affects text shaping; it does not change TranslationServer's locale or choose another catalog. TextDirection controls text direction, while Control.LayoutDirection controls UI layout direction. These are different settings. Paths and other structured strings can require StructuredTextBidiOverride rather than ordinary paragraph ordering.

For RTL, test real mixed Arabic/Hebrew, numbers and Latin strings, focus order, icons and each relevant container. Explicit RTL layout is inherited by children unless overridden. It does not mirror the game's entire coordinate system or replace translation selection. Do not assume every directional illustration should be flipped.

Let containers grow and labels wrap where appropriate; test the smallest supported window. Use pseudolocalization to stress length, noting its C# placeholder and plural limitations. Accented Latin text is not a substitute for CJK or RTL tests.

TranslationServer.FormatNumber/ParseNumber convert supported numbering-system characters; they are not a complete currency, unit or cultural-formatting policy. Keep number formatting separate from catalog selection. For languages needing dictionary-based line breaking, review internationalization/locale/include_text_server_data and validate an exported build with the required TextServer support; do not infer export behavior from the editor.

Test startup and release configuration

internationalization/locale/test forces a startup locale. Clear it before release when the game should select the player's language. A command-line language can override the test setting:

PowerShell
& "C:\path\Reckfell Engine.console.exe" --path "C:\path\Game" --language ru

Replace both paths with your installed executable and project. Code that calls SetLocale afterward can still change the language again.

For a localized application title, application/config/name_localized stores locale-to-title entries. Check the actual target platform's exported app metadata: a translated in-game Label does not verify the launcher/store title.

Acceptance and diagnosis

FailureFirst check
Key is visibleRegistration, exact key/context, selected locale and fallback
Static caption updates, counter does notNotification handler and template reformatting
Player name unexpectedly translatesAutoTranslateMode on that element
Text changes but the localized asset does notOriginal load path, remap alternatives and the consumer's cached state
Correct string has boxes, clipping or wrong directionFont coverage, shaping and layout separately
Locale resets after restartSaved preference and startup test setting

Native tests exercise this exact HUD, Label text buffers, translation inheritance, context refresh, locale versus Language, loaded Gradient remaps and command-line startup. They also check explicit RTL flags and the FontFile fallback API. They do not certify rendered glyphs, mirrored layouts, overflow, image/audio playback, preference persistence, editor interaction or platform exports. Those remain separate acceptance tasks.

Reckfell Engine documentationBack to top ↑
QUICK LINKSGetting startedEntity hierarchyC# & .NET 10Scenes and prefabs
Reckfell guides and full API K
reckfellENGINE
RECKFELL ACCOUNT

Sign in

Sign in to your Reckfell account.

One account for the entire Reckfell platform.