Skip to content

Loc

Translated text for anything a script builds itself — item names, dialogue, log messages. For UI labels, TextComponent.LocalizationKey needs no code at all. See the Localization guide.

Loc.Get(string key)

The translated string for a key.

csharp
m_Label.Text = Loc.Get("menu.play");

Never returns blank. The fallback ladder is: the current language → English → the key itself. A missing translation shows up on screen as menu.play — visible and obviously wrong, rather than an empty space nobody notices until a player reports it.

Loc.Format(string key, params object[] args)

Get, then positional substitution of {0}, {1} and so on.

json
{ "hud.coins": "Coins: {0}" }
csharp
m_Label.Text = Loc.Format("hud.coins", coinCount);

Why numbered placeholders

Word order differs between languages — a translator has to be able to move {0} to the front of the sentence. Never build a translated sentence by concatenating fragments: Loc.Get("you_have") + n + Loc.Get("apples") is unbuildable in most languages. One key, one whole sentence.

Loc.Plural(string key, int count)

Picks the right plural form for the current language and substitutes count as {0}.

json
{
  "enemy.defeated.one":   "Defeated {0} enemy",
  "enemy.defeated.other": "Defeated {0} enemies"
}
csharp
m_Label.Text = Loc.Plural("enemy.defeated", killCount);

You write sibling keys sharing a prefix, one per CLDR category — zero, one, two, few, many, other. English needs two, Polish four, Japanese one.

A lone .one key is not a plural

A plural is detected only when two or more siblings with different category suffixes share a prefix. Write .one without .other and it stays an ordinary string — Loc.Plural then returns the wrong thing rather than warning. scripts/validate_loc.py catches this; nothing at runtime does.

Loc.Language

The active locale code, like "es". Read and write. Setting it persists, so the game comes back in the same language next time.

csharp
Loc.Language = "es";

Every TextComponent using a LocalizationKey updates on its own, every frame, with no subscription.

Loc.OnLanguageChanged

An event fired right after the language changes. Subscribe if your script built some text by hand and needs to rebuild it.

csharp
protected override void OnCreate()
{
    Loc.OnLanguageChanged += RefreshLabels;
    RefreshLabels();
}

private void RefreshLabels()
{
    m_Title.Text = Loc.Get("menu.title");
    m_Hint.Text  = Loc.Format("menu.hint", m_KeyName);
}

Loc.Available

Every discovered language, as an array of (string Code, string NativeName) tuples — exactly what a settings dropdown needs.

csharp
foreach ((string code, string nativeName) in Loc.Available)
    AddLanguageOption(code, nativeName);

NativeName is the language's own name for itself — "Español", "日本語" — because a Spanish speaker looking for their language is not looking for the word "Spanish". It comes from the reserved language.name key in each catalog.

Not a per-frame call

This builds a small array each time. Populate your menu once when it opens.

Example: a language picker

csharp
using JoystickEngine;

public class LanguageMenu : GameEntity
{
    protected override void OnCreate()
    {
        foreach ((string code, string nativeName) in Loc.Available)
            Log.Info($"{code}: {nativeName}");

        Loc.OnLanguageChanged += () => Log.Info($"Now in {Loc.Language}");
    }

    public void Choose(string code) => Loc.Language = code;
}

See also