The project's UI strings
UIStrings — the one thing this layer localizes, why everything else is already done, and the gate that breaks if you copy a translated value into a variable.
All documentation — Reference
Start here
Concepts
Authoring for a game
Playing a story
The generated code
The plugin and sync
UIStrings holds the project’s own menus, buttons and labels. That is all this layer owns.
Using it
| Member | |
|---|---|
UIStrings(Func<string, string?>? loadText = null, Action<string>? report = null) | Defaults read from Resources and report to the Unity console |
static string ResourcePath(string language) | Where the generator puts a language’s strings, as Resources.Load addresses it — no extension |
string Language | The code this table currently holds |
int Count | Zero means the load failed |
IReadOnlyCollection<string> Keys | For an audit or a debug dump. Empty, never null. |
event Action? Changed | Raised after the new strings are in place, so a handler reads the new ones |
void SetLanguage(string language) | |
string Get(string key) · this[string key] | |
bool TryGet(string key, out string value) |
The table follows the current language rather than owning it, and switching never needs a scene reload:
ui.SetLanguage(player.Language) | The project’s code — en, never a storage key |
player.LanguageChanged += ui.SetLanguage | The chrome follows the story |
ui.Changed += Redraw |
loadText is the same seam the generated bootstrap takes — given a resource path with no extension,
return its text or null — so a project on Addressables, an asset bundle or a downloaded pack passes
the loader it already has.
report defaults to an error log because everything it says is a real defect: a language the
player asked for and the build cannot render.
The rules worth knowing
A missing key renders as the key, never as an empty string — which would be indistinguishable
from a label the author deliberately left blank. TryGet is the one way to tell a present key from
that fallback.
A failed load empties the table rather than keeping the previous language’s strings, so every label renders as its own key and the problem is visible at a glance instead of being half a screen of the wrong language.
SetLanguage never throws and always raises Changed, including on a refusal. Language records
what was last asked for, including a value that was refused — so pair it with Count, which is zero
whenever nothing loaded.
What the generator writes
A flat map, one file per language: { "menu.title": "Extraction" }.
Every value in it is already resolved. The generator fills a missing translation with the master language’s string at sync time, so there is no fallback chain at runtime. A non-string member is skipped and reported rather than coerced.
The folder names are exactly the codes in the project’s language list.