i18n
@bedrock-core/i18n is the localization layer for Minecraft Bedrock addons: typed keys, typed interpolation and plurals, resolved on the client in each player's own language wherever possible, and on the server whenever your code needs the actual string.
@bedrock-core/i18n is in beta: the API can change between releases. Pin exact versions and read the changelog before upgrading.
What is @bedrock-core/i18n?
It is the runtime half of a two-part system. The build half is the i18n Regolith filter, which turns packs/data/i18n/<locale>.ts modules into .lang files, a runtime bundle and the types this package's API infers from.
The conventions are i18next's — {{var}} interpolation, _one/_other plural suffixes, the selector call shape — so existing knowledge transfers. Nothing from i18next ships in the script bundle: every locale rides statically in one generated bundle.
Install
Every addon already depends on @bedrock-core/server, which carries it at its own subpath — nothing extra to install:
import { createI18n } from '@bedrock-core/server/i18n';
A library that does not depend on the meta package adds it directly:
- npm
- yarn
- pnpm
npm install @bedrock-core/i18n
yarn add @bedrock-core/i18n
pnpm add @bedrock-core/i18n
import { createI18n } from '@bedrock-core/i18n';
Both entries export exactly the same surface — @bedrock-core/server/i18n is a one-line re-export of @bedrock-core/i18n. The rest of this section writes @bedrock-core/i18n; substitute the meta path if you prefer.
Setup
One instance per addon, built from the bundle the Regolith filter generates:
// BP/scripts/i18n.ts
import bundle from '@bedrock-core/generated/i18n';
import { createI18n } from '@bedrock-core/i18n';
export const i18n = createI18n(bundle);
That is the whole setup. Key paths, interpolation variables and plural forms are all inferred from the bundle's type — no module augmentation, no manual type imports. See bundler for the @bedrock-core/generated/i18n path alias this import needs.
createI18n also registers this instance as the addon's default translation source, which is what lets Text measure localized children with no further wiring.
What you get
- Client-first resolution.
key()andraw()defer to the client, so each player sees their own language at no per-player cost on the server;t()resolves now, server-side, for whichever locale you're bound to. See The three verbs. - Typed everything. Key paths, required interpolation variables and plural forms all come from the bundle's inferred type — an unknown path or a missing argument is a compile error, not a blank string in game.
- Plurals without
Intl. A built-in CLDR rule table picks_one/_few/_otherper locale, since Bedrock's script engine has noIntl.PluralRules. - One
DisplayTextchannel.Textchildren,HeaderandMenuRowlabels, and registry display fields all accept the samestring | RawMessageunion, resolved lazily by whichever resolver is active. - Cross-addon sharing.
core.translations.provide(bundle)publishes a bundle;core.translations.of(addonId)and.forPlayer(player)read across every published addon's strings — see Cross-addon sharing.
Next steps
- Authoring resources — locale files, namespacing, interpolation, plurals and the meta branch
- Libraries and overrides — folding a dependency's strings into your bundle, overriding library and vanilla strings
- API reference — the three verbs, locale resolution,
createI18nand every export - i18n Regolith filter — generating
.langfiles and the runtime bundle from your resources - useTranslation — binding the typed verbs to the viewing player inside a component
- TranslationContext — overriding which resolver a subtree sees