NarraLeaf

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.

MethodSignature
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.

MemberSignature
currentreadonly 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 the Get Text node. It returns null when the project declares no such key.
  • words(id, text) reads words the plugin wrote directly and offered for translation from its studio entry with app.services.localization.registerWords: the translation of the unit id names, along the language's fallback chain, else text. 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.

On this page