Skip to content

Localization Migration Guide: GetText → LocString

This guide covers every scenario for migrating from the legacy GetText(key) API to the newer LocString + Format() API.

Backwards compatibility guarantee: GetText and GetTextFormatted are currently marked [Obsolete] with a pointer to this document, but they remain available and are not planned for hard removal.


Scenario 1: Static strings (no variables)

These need no migration — GetText still works, and the overhead is zero. If you want to opt into the new API for consistency:

Before

string label = locService.GetText("UI_PLAY_BUTTON");

After

string label = locService.Format(new LocString(LocTable.UI, "UI_PLAY_BUTTON"));

Both call paths produce the same output and use the same CSV lookup.


Scenario 2: Strings with string.Format placeholders ({0}, {1}, …)

Before

// CSV: "Score: {0}"
string text = locService.GetTextFormatted("SCORE_TEXT", playerScore);

After (option A — keep GetTextFormatted temporarily) No functional change is required, but GetTextFormatted is obsolete. Use this only while migrating existing indexed-placeholder content.

After (option B — named DynamicVar)

// CSV: "Score: {score}"
var ls = new LocString(LocTable.UI, "SCORE_TEXT", new DynamicVar("score", playerScore));
string text = locService.Format(ls);
Named tokens are preferred for anything that changes at runtime: the name documents intent and is stable if the template wording changes.


Scenario 3: Dynamic number that recalculates (tooltip / card text)

This is the main migration case.

Before

// Manually build the string each time the value changes
string desc = locService.GetText("IRON_STRIKE_DESC")
    .Replace("{damage}", damage.ToString())
    .Replace("{hits}", hits.ToString());

After

// CSV: "Deal {damage} damage {hits} time(s)."
var desc = new LocString(LocTable.Cards, "IRON_STRIKE_DESC",
    new DynamicVar("damage", baseDamage),
    new DynamicVar("hits",   baseHits));

// Bind to UI
descText.text = locService.Format(desc);

// When the value changes (upgrade, buff, etc.):
desc.GetVar("damage").SetCalculatedValue(newDamage);
descText.text = locService.Format(desc);


Scenario 4: Upgrade preview (show new value before confirming)

Before No standard pattern — typically a one-off string manipulation at the call site.

After

// Show preview while the user is hovering the upgrade button
desc.GetVar("damage").SetPreviewValue(upgradedDamage);
previewText.text = locService.Format(desc);  // uses PreviewValue

// Confirm upgrade
desc.GetVar("damage").Upgrade(delta);         // accumulates into CalculatedValue, WasJustUpgraded = true
desc.GetVar("damage").ClearOverrides();       // clears both PreviewValue and CalculatedValue override slot
                                              // (CalculatedValue now = BaseValue + accumulated upgrades)

// After UI has shown the highlight
desc.GetVar("damage").ClearUpgradeFlag();


Scenario 5: Rich-text / custom specifiers

Before Not supported — callers applied rich-text tags manually after getting the plain string.

After

// Register a formatter once (after service init)
locService.AddLocStringFormatter(new HighlightFormatter());

// CSV: "Deal {damage:highlight} damage"
var desc = new LocString(LocTable.Cards, "IRON_STRIKE_DESC", new DynamicVar("damage", 6f));
string text = locService.Format(desc);  // "Deal 6 damage" normally, "<color=yellow>8</color>" when upgraded

public class HighlightFormatter : ILocStringFormatter
{
    public string Format(string varName, string specifier, DynamicVar variable)
    {
        if (specifier != "highlight") return null;
        string val = variable?.ToString() ?? $"{{{varName}}}";
        return variable?.WasJustUpgraded == true ? $"<color=yellow>{val}</color>" : val;
    }
}


Scenario 6: Plural strings

Plural support uses the existing GetPluralText API — no migration to LocString needed. Use GetPluralText(key, count) for count-dependent strings.

// CSV: "ITEM_COUNT[one]" = "1 item" / "ITEM_COUNT[other]" = "{count} items"
string text = locService.GetPluralText("ITEM_COUNT", n);

CSV template syntax for LocString

Template Meaning
{damage} Substitute DynamicVar("damage").CalculatedValue as integer
{damage:highlight} Pass to HighlightFormatter (or any registered formatter matching specifier)
{missing} No DynamicVar named "missing" → renders as {missing} (visible, not blank)

Quick checklist

  • [ ] Static strings → no change required
  • [ ] GetTextFormatted indexed {0} → no change required OR switch to named DynamicVar
  • [ ] Dynamic tooltip / card text → new LocString(table, key, new DynamicVar(name, baseValue))
  • [ ] Upgrade preview → use SetPreviewValue / Upgrade(delta) / ClearOverrides
  • [ ] Rich-text / custom format → implement ILocStringFormatter, register once
  • [ ] CI validation → call LocValidator.ValidateAll() in build script
  • [ ] Language switch → resolves automatically via locService.Format(locString) (respects current language + fallback chain)