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);
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
- [ ]
GetTextFormattedindexed{0}→ no change required OR switch to namedDynamicVar - [ ] 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)