Skip to main content

Internationalization

RPGJS uses the same i18n shape on the server and the client. Modules can ship default translations, and the game can override them with provideI18n().

Configure Translations

Use the same config on both sides when the game needs translated gameplay text on the server and translated local UI on the client.

Translate Gameplay Text

The GUI APIs still receive final strings. Translate explicitly with player.i18n() or player.t().

Player Locale

The locale is stored in the player snapshot, so save(), load(), and session restore keep the selected language.

Module Defaults and Game Overrides

Reusable modules can provide default translations:
If the game provides the same key through provideI18n(), the game translation wins. Use namespaces such as quest.* or my-module.* for module keys.

Client Menus

Built-in RPGJS menus use rpg.* keys internally, such as rpg.menu.items and rpg.save.title. Override these keys in the game config to rename or translate the default menus.

Choosing a language in Options

The built-in Options menu lists only languages registered by the game through provideI18n({ messages }). Module/framework catalogues do not add choices. With no game catalogue, only defaultLocale is listed. Configure the same catalogues on client and server for both standalone RPG and MMORPG games. RPGJS ships English and French interface labels; provide rpg.* translations for other game languages.
I18nService.getAvailableLocales() exposes the same game-only list. Language names use their native spelling. Automatic mode tries each navigator.languages entry in order: exact locale first, then an available base language (fr-CA → fr). If none matches, it uses defaultLocale. An unavailable stored choice returns to automatic mode. Storage is scoped by globalConfig.projectId (or _id, then the page pathname); assign a unique project id when hosting several games together. Unavailable browser storage simply makes the preference session-only. Client getLocale() and t() participate in CanvasEngine reactive computations. Use computed(() => client.t('menu.title')) for derived labels, not a string translated once at component creation. Open built-in menus retain their current selection while their labels change. The selected language is included in each connection before gameplay join hooks, and subsequent changes use the player.locale action. The server validates the locale against its game catalogues (or its configured defaultLocale fallback) and changes only the sending player’s locale. Reconnection and room transfers carry the current preference; loading an older save does not override a connected player’s preference. Future player.t() calls use it. An already displayed dialogue and literal authored strings are not automatically retranslated. An offline snapshot still retains its saved locale until a current connection preference is applied.