The Complete Guide to Unreal Engine Localization

From FText macros to locale fallback chains: localize your UE5 game with the Localization Dashboard, String Tables, C++, Blueprints, and automated translation.

1

FText & the Localization Pipeline

FText is Unreal Engine's localization-aware string type. Every user-facing string in your game — UI labels, dialog, tooltips, notifications — must use FText to participate in the localization pipeline. FString is for internal logic only.

LOCTEXT requires two arguments: a key and a source string. The key must be unique within its namespace. UE's text gatherer uses these keys to track translations across cultures. NSLOCTEXT lets you specify the namespace explicitly; LOCTEXT uses the namespace defined by the enclosing LOCTEXT_NAMESPACE macro.
FText Basics
// FText is UE's localization-aware string type.
// ALWAYS use FText for user-facing text, never FString.

// LOCTEXT: localize a literal string (most common)
FText Title = LOCTEXT("MainMenuTitle", "Start Game");

// NSLOCTEXT: specify namespace explicitly
FText Msg = NSLOCTEXT("UI", "WelcomeMessage", "Welcome, adventurer!");

// INVTEXT: invariant text (never translated — debug/logging only)
FText Debug = INVTEXT("Debug overlay active");

// FText::Format: safe variable interpolation
FText Greeting = FText::Format(
    LOCTEXT("PlayerGreeting", "Hello, {PlayerName}!"),
    FText::FromString(PlayerName)
);
MainMenuWidget.cpp
// In a UMG Widget (C++)
void UMainMenuWidget::NativeConstruct()
{
    Super::NativeConstruct();

    // Bind localized text to UI elements
    if (TitleLabel)
    {
        TitleLabel->SetText(LOCTEXT("GameTitle", "My Epic Game"));
    }

    if (PlayButton)
    {
        PlayButton->SetText(LOCTEXT("PlayButtonLabel", "Play Now"));
    }
}

// IMPORTANT: Always use LOCTEXT for UMG widget text.
// Setting text via FString bypasses the localization pipeline.
Never construct user-facing text with FString::Printf or string concatenation. These bypass the localization pipeline entirely — the resulting text cannot be gathered, translated, or displayed correctly in RTL languages. Always use FText::Format with LOCTEXT patterns instead.
2

Set Up the Localization Dashboard

The Localization Dashboard is UE's built-in tool for managing translations. It gathers all LOCTEXT and NSLOCTEXT strings from your source code, exports them as .po files for translation, and compiles the results into .locres binary files that UE loads at runtime.

Localization Dashboard Workflow
# 1. Open the Localization Dashboard:
#    Window > Localization Dashboard

# 2. Add a localization target (e.g., "Game")

# 3. Add cultures:
#    Click "Add New Culture" > select languages (de, ja, fr, es, ko, zh-Hans...)

# 4. Gather text:
#    Click "Gather Text" — UE scans all LOCTEXT/NSLOCTEXT macros

# 5. Export translations:
#    Click "Export" to generate .po files for each culture

# 6. Import translations:
#    After translating .po files, click "Import"

# 7. Compile translations:
#    Click "Compile" to generate .locres binary files

# 8. Preview:
#    Editor Preferences > Region & Language > Preview Game Language
Run 'Gather Text' after every code change that adds or modifies LOCTEXT macros. Missing this step means new strings won't appear in your .po files and translators won't see them. Add a gather step to your build automation to catch this automatically.
3

Use String Tables for Data-Driven Text

String Tables let you define localized strings in a centralized asset instead of scattering LOCTEXT macros across source files. They are ideal for UI text, dialog, and any strings that designers or writers need to edit without touching code. String Tables can be defined as UE assets or imported from CSV.

StringTableUsage.cpp
// StringTables provide a data-driven approach to localization.
// Define strings in a CSV asset instead of scattering LOCTEXT across code.

// 1. Create a String Table asset:
//    Content Browser > Right-click > Miscellaneous > String Table

// 2. Or define via CSV (importable into UE):
// Key,SourceString
// MainMenu_Title,Start Game
// MainMenu_Continue,Continue
// MainMenu_Settings,Settings
// MainMenu_Quit,Quit to Desktop
// HUD_Health,Health
// HUD_Ammo,Ammo: {0}
// Dialog_Merchant_Greeting,Welcome to my shop!

// 3. Reference in C++:
FText Title = FText::FromStringTable(
    FName(TEXT("/Game/Localization/ST_MainMenu")),
    TEXT("MainMenu_Title")
);

// 4. Reference in Blueprints:
//    Use the "Make Text from String Table" node
//    Set Table ID and Key
String Tables are gathered automatically by the Localization Dashboard. You don't need LOCTEXT macros for strings defined in a String Table — just reference them by table ID and key in C++ or Blueprints.
4

C++ Localization Patterns

In C++, define a LOCTEXT_NAMESPACE at the top of each .cpp file and use LOCTEXT for all user-facing strings. Use FText::Format for dynamic content with variables. Always undefine the namespace at the end of the file to prevent leaks.

MainMenuWidget.cpp
// Define a namespace at the top of each .cpp file
// All LOCTEXT calls in this file use this namespace
#define LOCTEXT_NAMESPACE "MyGame.MainMenu"

#include "MainMenuWidget.h"

void UMainMenuWidget::NativeConstruct()
{
    Super::NativeConstruct();

    // These use the "MyGame.MainMenu" namespace automatically
    TitleText->SetText(LOCTEXT("Title", "Main Menu"));
    PlayText->SetText(LOCTEXT("PlayButton", "Play"));
    SettingsText->SetText(LOCTEXT("SettingsButton", "Settings"));
    QuitText->SetText(LOCTEXT("QuitButton", "Quit"));
}

// CRITICAL: Always undefine at the end of the file
#undef LOCTEXT_NAMESPACE
FText::Format Examples
// FText::Format — the safe way to build localized strings
// NEVER use FString::Printf or string concatenation for user-facing text.

// Named arguments (recommended)
FText ItemPickup = FText::Format(
    LOCTEXT("ItemPickup", "You picked up {ItemName} x{Count}"),
    FText::FromString(ItemName),
    FText::AsNumber(Count)
);

// FText::AsNumber respects locale formatting:
//   English: 1,234,567
//   German:  1.234.567
//   French:  1 234 567

// FText::AsCurrency for prices:
FText Price = FText::AsCurrency(
    9.99,
    TEXT("USD"),
    &FInternationalization::Get().GetCurrentCulture().Get()
);

// FText::AsDate and FText::AsTime for dates:
FText DateStr = FText::AsDate(FDateTime::Now());
FText::Format arguments must also be FText, not raw FString. Use FText::FromString() to convert FString values, FText::AsNumber() for locale-aware number formatting, and FText::AsCurrency() for prices. Raw FString concatenation produces text that doesn't respect locale formatting rules.
5

Blueprint Localization

All Text properties in Blueprints are FText by default, so they are already localization-ready. Set the Key and Namespace in the property details panel to make strings gatherable. Use the Format Text node for dynamic content with variables.

Blueprint Localization Patterns
// Blueprint Localization Basics:
//
// 1. All Text properties in Blueprints are FText by default
//    (already localization-ready)
//
// 2. Set the Text property in the Details panel
//    Expand the dropdown to set:
//    - Key: unique identifier for translation
//    - Namespace: grouping for organization
//    - Source String: the text to display/translate
//
// 3. For dynamic text, use the "Format Text" node:
//    Format: "Hello, {PlayerName}!"
//    Connect a "Find" pin named "PlayerName" to your variable
//
// 4. For plurals, use "Text Format with Arguments":
//    Pattern: "{Count}|plural(one=item,other=items)"
//
// 5. Culture switching at runtime:
//    Use "Set Current Culture" node
//    Input: culture code string (e.g., "de", "ja", "fr")
Expand the text property dropdown in the Blueprint Details panel to see the Key, Namespace, and Source String fields. Setting a meaningful Key makes it much easier for translators to identify strings in the .po file.
6

Handle Plurals and Gender

Unreal Engine supports ICU message format for plurals and gender-dependent text. Define plural rules in your source strings and UE automatically selects the correct form based on the active culture's CLDR rules.

ICU Plural & Gender Rules
// UE uses ICU message format for plurals.
// Define plural rules in your .po or String Table:

// English source:
// "{Count}|plural(one=You have # item,other=You have # items)"

// German translation:
// "{Count}|plural(one=Du hast # Gegenstand,other=Du hast # Gegenstaende)"

// Arabic translation (6 forms):
// "{Count}|plural(zero=لا عناصر,one=عنصر واحد,two=عنصران,few=# عناصر,many=# عنصرًا,other=# عنصر)"

// Japanese (1 form):
// "{Count}|plural(other=アイテム#個)"

// In C++:
FText ItemCount = FText::Format(
    LOCTEXT("ItemCount",
        "{Count}|plural(one=You have {Count} item,other=You have {Count} items)"),
    ItemCount
);

// Gender-dependent text:
// "{Gender}|gender(masculine=Il est,feminine=Elle est) prêt{Gender}|gender(masculine=,feminine=e)"
Never hardcode count == 1 for singular detection. French treats 0 as singular. Russian has separate forms for 'few' and 'many'. Arabic has 6 plural forms. Let ICU plural rules handle the logic — define all required forms and UE selects the right one per culture.
7

Package and Test Localization

Before shipping, verify that all target cultures have compiled .locres files and that text renders correctly at runtime. Use the Editor's culture preview, command-line culture overrides, and automated checks to catch missing or broken translations.

Packaging & Testing Workflow
# Compile and test localization before packaging:

# 1. Editor Preferences > Region & Language > Preview Game Language
#    Set to each target language and verify all text renders correctly.

# 2. Command-line culture override for testing:
MyGame.exe -culture=ja

# 3. Packaging settings:
#    Project Settings > Packaging > Localizations to Package
#    Select all cultures you want to include in the build.

# 4. Verify .locres files exist after packaging:
MyGame/Content/Localization/Game/
├── en/
│   └── Game.locres
├── de/
│   └── Game.locres
├── ja/
│   └── Game.locres
├── fr/
│   └── Game.locres
└── ko/
    └── Game.locres

# 5. Runtime culture switching:
#    FInternationalization::Get().SetCurrentCulture(TEXT("ja"));
#    This reloads all FText strings from the new culture's .locres files.
If a culture is not listed in Project Settings > Packaging > Localizations to Package, its .locres files are excluded from the build. Players selecting that language at runtime will see fallback text or empty strings. Always verify your packaging settings match your supported cultures.
8

Add Locale Fallback Chains

Unreal Engine's default localization only falls back along the IETF subtag hierarchy. A pt-BR user missing a translation gets English instead of a perfectly good pt-PT translation. locale-chain-ue adds configurable lateral fallback chains through FTextLocalizationManager so regional users always see the closest available translation.

locale-chain-ue (C++)
// locale-chain-ue: Smart fallback chains for UE5
// Problem: UE falls back to default when a locale is missing.
// pt-BR user gets English instead of pt-PT translations.

// Solution: One function call at startup.
#include "LocaleChain.h"

void UMyGameInstance::Init()
{
    Super::Init();
    ULocaleChain::Configure();  // Load 75 built-in fallback chains
}

// Now resolve strings with per-key fallback:
FString Greeting = ULocaleChain::Resolve(
    TEXT("greeting"), TEXT("MyNamespace")
);
// pt-BR user: tries pt-BR -> pt-PT -> pt -> default

// Custom overrides:
TMap<FString, FString> Overrides;
Overrides.Add(TEXT("pt-BR"), TEXT("pt"));       // Simplify chain
Overrides.Add(TEXT("sv-FI"), TEXT("sv"));        // Add new chain
ULocaleChain::ConfigureWithOverrides(Overrides);

// Full control (C++ only):
TMap<FString, TArray<FString>> Custom;
Custom.Add(TEXT("pt-BR"), {TEXT("pt-PT"), TEXT("pt")});
Custom.Add(TEXT("es-MX"), {TEXT("es-419"), TEXT("es")});
ULocaleChain::ConfigureCustom(Custom, false);
Call ULocaleChain::Configure() once at startup to load 75 built-in fallback chains covering 11 language families. Use ConfigureWithOverrides for Blueprint-friendly customization, or ConfigureCustom in C++ for full control over fallback behavior.

Automate Translation Quality

Catch missing keys and broken placeholders before they ship with i18n-validate. Test your UI with pseudo-translations using i18n-pseudo before real translations arrive.

Common Pitfalls

Using FString for User-Facing Text

FString bypasses the entire localization pipeline. Text built with FString::Printf or string concatenation cannot be gathered, translated, or displayed correctly in RTL languages. Always use FText with LOCTEXT macros and FText::Format for user-visible strings.

Missing #undef LOCTEXT_NAMESPACE

Forgetting to #undef LOCTEXT_NAMESPACE at the end of a .cpp file causes the namespace to leak into subsequent translation units. This silently assigns wrong namespaces to strings in other files, making translations appear in the wrong context.

Hardcoded Plural Logic

Writing 'count == 1 ? singular : plural' ignores CLDR rules. French treats 0 as singular, Russian has 4 plural forms, Arabic has 6. Use ICU plural syntax in your FText patterns and let UE handle the rules per culture.

Forgot to Compile After Import

Importing .po files updates the text data but does not generate .locres binaries. The game still loads the old compiled translations at runtime. Always run 'Compile' in the Localization Dashboard after importing, or add it to your build automation.

Recommended Project Structure

Project Structure
MyUnrealProject/
├── Config/
│   └── Localization/
│       └── Game.ini                  # Localization target config
├── Content/
│   └── Localization/
│       ├── Game/
│       │   ├── Game.manifest         # Gather manifest
│       │   ├── en/
│       │   │   ├── Game.po           # Source strings (.po)
│       │   │   └── Game.locres       # Compiled binary
│       │   ├── de/
│       │   │   ├── Game.po
│       │   │   └── Game.locres
│       │   ├── ja/
│       │   │   ├── Game.po
│       │   │   └── Game.locres
│       │   └── fr/
│       │       ├── Game.po
│       │       └── Game.locres
│       └── StringTables/
│           ├── ST_MainMenu.uasset    # String Table asset
│           └── ST_HUD.uasset
├── Plugins/
│   └── LocaleChain/                  # locale-chain-ue plugin
│       ├── LocaleChain.uplugin
│       └── Source/
│           └── LocaleChain/
├── Source/
│   └── MyGame/
│       ├── UI/
│       │   ├── MainMenuWidget.h
│       │   └── MainMenuWidget.cpp    # LOCTEXT macros here
│       └── MyGameInstance.cpp        # ULocaleChain::Configure()
└── MyUnrealProject.uproject

Try i18n Agent Now

Drop your translation file here

JSON, YAML, PO, XML, CSV, Markdown, Properties

or click to browse

Target languages

No signup requiredInstant estimate

Frequently Asked Questions