Skip to content
Moku Labs
@moku-labs/game pages

i18n

Screen. Strings as data: ICU messages compiled per locale, tr() returns a Message, the screen gets parts, never a joined string.

On this page

What it does

i18n holds the strings of the game. tr(key, params?) builds a frozen Message { key, params }; no locale is read at the call site. format(message) turns it into Part[] at run time: text parts and element parts, never one joined string.

SetTierBuilds onEmits
screen, in screenComplexflowi18n:locale-changed

Strings. They live in strings/<locale>.json of any feature or sub-feature, as ICU MessageFormat: plural, select, selectordinal, number, date, time. No skeletons and no currency.

{n, plural, one {# order} other {# orders}}

moku-game keys (or bun run assets:keys) compiles them to plain functions in generated/strings.<locale>.ts, and writes the Strings type for defineGame. With strings: Strings in the game types, a wrong key or a missing parameter does not compile. The compiler is compileStrings of @moku-labs/game/assets.

Sub-features. A sub-feature's keys start with its name, ep01.title, and compile to generated/strings.ep01.<locale>.ts. It registers them lazily. With assets composed they load and unload with the bundle named like the sub-feature. While the bundle is not loaded, a key of it gives ⟦key⟧ and has(key) is false.

Missing keys. The lookup order is the current locale, then fallback. A missing key gives one text part ⟦key⟧ and one warning.

Config

pluginConfigs.i18n.

OptionTypeDefaultDescription
localestring"en"The locale at start.
fallbackstring"en"The locale a missing key is read from before it is reported missing.
localesRecord<string, module | loader>{}Compiled modules outside features, per locale. A loader is imported on first use.

A feature brings its own: defineFeature("hud", { strings: { en: () => import("./generated/strings.en") } }).

API

Mounted at app.i18n, reachable with ctx.require(i18nPlugin).

MethodSignatureDescription
locale() => stringThe current locale.
setLocale(locale: string) => Promise<void>Imports the module once, then emits i18n:locale-changed. text draws every label again.
format(message: I18n.Message, locale?: string) => readonly I18n.Part[]Formats in the current locale, or another registered one without switching.
plain(message) => stringThe parts as one string, elements dropped. For logs and tests.
has(key: string) => booleanWhether a key exists in the current locale or the fallback.
isKey(value: unknown) => value is KeyWhether a value read from a save is a key the game still has.
duration(ms: number, style?) => stringMilliseconds through Intl.DurationFormat, rounded up to whole seconds.
locales() => readonly string[]Every locale a module was brought for, sorted.
replace(locale: string, messages) => voidThe dev hot swap of a generated strings file.

messageArgument and messageDuration are root exports the compiled modules call. A game does not call them.

Events

Emits

EventPayloadWhen
i18n:locale-changed{ locale: string }The new locale is loaded, or replace swapped messages of the current locale or the fallback. Never at start.

Listens to

None.

Example

locale.ts
import { createApp, screen, tr } from "@moku-labs/game";

const app = createApp({
  plugins: [...screen],
  pluginConfigs: { i18n: { locale: "en", fallback: "en" } }
});
await app.start();

const orders = tr("hud.orders", { n: 3 }); // a frozen Message: no locale is read here
app.i18n.format(orders); // Part[]: text and element parts
app.i18n.plain(orders); // one string, for logs and tests

if (app.i18n.locales().includes("ru")) await app.i18n.setLocale("ru");
  • Never join parts yourself. An element part carries an icon or a styled run; plain drops it.
  • Check saved keys with isKey. defineGameApp({ strings: type<Strings>() }) types app.i18n, so a true isKey(value) narrows the value to a key.
  • The kit's tr takes keys, not plain strings. A key from a data table is typed I18n.TextKey or built with the kit's key("board.hint.red").
  • text: draws the messages.
  • Assets: compiling strings next to the asset keys.
  • Hot swap: a saved strings file swaps in the page.