assets & locale
Resolve a packaged asset id to a URL this shell can load, and follow the player's language.
Two small capabilities, declared independently:
{
"contributes": {
"runtimeCapabilities": ["assets", "locale"]
}
}assets
app.game.assets turns an asset id from the project's pack into a URL the current shell can load. Without it a plugin would have to hardcode the game's internal protocol string, which differs between the desktop shell and the web export.
| Method | Signature |
|---|---|
url | (assetId: string) => string |
const src = app.game.assets?.url(String(ctx.params.iconAssetId ?? ""));Synchronous, so it is safe inside a widget renderer or a pure node.
assets is absent in Dev Mode. Asset resolution there goes through asynchronous IPC, and this capability's signature is a synchronous url() — the two cannot be reconciled, so the domain is simply not offered. A plugin that renders asset-backed imagery must have a fallback for Dev Mode, or the author testing it will see an empty box and blame the plugin. It is available in Preview and in every exported build.
locale
app.game.locale is the game's display language — the one the player chose, set by the running project's localization data — and the project's translated words in it.
| Member | Signature |
|---|---|
current | readonly string |
onChange | (listener: (locale: string) => void) => RuntimePluginCleanup |
text | (key: string) => string | null |
words | (id: string, text: string) => string |
const locale = app.game.locale?.current ?? "en";
const stop = app.game.locale?.onChange(next => {
// read the words again for `next`
});text(key)reads one of the project's translation keys in the current language, through the same table and fallback chain as theGet Textnode. It returnsnullwhen the project declares no such key.words(id, text)reads words the plugin wrote directly and offered for translation from its studio entry withapp.services.localization.registerWords: the translation of the unitidnames, along the language's fallback chain, elsetext. The game carries translations and not source words, so the plugin passes the words as written. A plugin reads only its own words.
This is read-only: a plugin can follow the player's language but cannot change it.
Do not confuse this with app.services.i18n, which is the editor's UI language and exists only on the studio entry. They are different languages set by different people — an author editing in English can be testing a game running in Japanese.
Words read with text or words are translated in the project's translation table with the rest of the game, in every language the author adds. Read them again on onChange.