Basic tutorials

New to i18n? Start here. i18n (internationalization) means: write your app in one language, and users see it in theirs. For everyday use you only need THREE things: t for texts, m to define a message once, and .note() to leave a comment for translators. Everything else (plurals, ordinals, number/date formats) is completed automatically by the i18n engine. Each step shows the CODE you write and the OUTPUT users see — every OUTPUT below is translated live by the API server, so give the inputs a try.

1 Very easy way to use @aurorah/i18n

Wrap your text with t. The text itself is the key — there are no ID files to maintain. If a language has no translation yet, the original text is shown, so nothing ever breaks. t renders synchronously from the in-memory cache: on a miss it shows the source text immediately and fetches the translation in the background, and await i18n.load() on a later run downloads that language's whole cache in ONE request.

CODE
// "www.example.com" = your project's namespace
// "ko_kr" = {language}_{country} (ISO 639-1 + ISO 3166-1)
const i18n = new I18n({ namespace: "www.example.com", language: "ko_kr" });
const t = i18n.t;     // `t` stays bound to the instance

t("Hello~ world !!!");     // [1]
t("menu/marketplace");     // [2]
Full Example Code
OUTPUT
[1] ko_krHello~ world !!!
[2] ko_krmenu/marketplace

2 Show a text in the user's language

The source texts can be written in ANY language — here the app is written in Japanese, and an English user sees English. The same t call, no other change.

CODE
const i18n = new I18n({
  namespace: "www.example.com",
  sourceLanguage: "ja_jp", language: "ja_jp",
});

t("こんにちは〜 世界 !!!")      // [1] Japanese (the source — no load() needed)

i18n.setLanguage("en_us");      // switch to English
await i18n.load();              // load the en_us cache
t("こんにちは〜 世界 !!!")      // [2] same call, now English
Full Example Code
OUTPUT
[1] ja_jpこんにちは〜 世界 !!!
[2] en_usこんにちは〜 世界 !!!

3 Switch the language

One call switches the whole app. setLanguage() plus await load() pulls that language's translations once; after that, every t() call renders instantly. A language without translations simply shows the original text. EVERY language in the world is supported — your i18n language code is {language}_{country} — language (ISO 639-1) + country (ISO 3166-1):

CodeLanguage (ISO 639-1)Country (ISO 3166-1)
ko_krKoreanSouth Korea
en_usEnglishUnited States
ja_jpJapaneseJapan
zh_cnChineseChina
es_esSpanishSpain
fr_frFrenchFrance
de_deGermanGermany
.........
CODE
const i18n = new I18n({ namespace: "www.example.com" });

// default language is 'en_us'
t("Hello~ world !!!")        // [1] the source — no load() needed

i18n.setLanguage("ko_kr");   // switch to Korean
await i18n.load();           // load the ko_kr cache
t("Hello~ world !!!")        // [2] same call, now Korean

i18n.setLanguage("ja_jp");   // Japanese
await i18n.load();           
t("Hello~ world !!!")        // [3]

i18n.setLanguage("zh_cn");   // Chinese
await i18n.load();           
t("Hello~ world !!!")        // [4]

i18n.setLanguage("es_es");   // Spanish
await i18n.load();           
t("Hello~ world !!!")        // [5]
Full Example Code
OUTPUT
[1] en_usHello~ world !!!
[2] ko_krHello~ world !!!
[3] ja_jpHello~ world !!!
[4] zh_cnHello~ world !!!
[5] es_esHello~ world !!!

await i18n.load() right after setLanguage() downloads that language's whole translation cache in ONE request, so every text renders translated instantly. Without it, users briefly see the source text while each string is translated one by one in the background. Only the current language's table is kept in memory, so call it again after every switch.

4 Put values into your text - Positional Placeholder

Drop a value into the sentence with ${...}. It becomes a positional placeholder ({0}, {1}, ...) — each language's translation can move it to wherever its word order needs it.

CODE
const name = "Steve";     // <- the input below
const age = 20;

i18n.setLanguage("en_us");
t`${name} is ${age} years old !!!`     // [1]

i18n.setLanguage("ko_kr");
await i18n.load();
t`${name} is ${age} years old !!!`     // [2]
Full Example Code
OUTPUT
[1] en_usSteve is 20 years old !!!
[2] ko_krSteve is 20 years old !!!
CODE
// Each language moves {0}, {1} to fit its own word order —
// draft/refined translation updates re-render via subscribe():
t`Is ${name} really ${age} years old ???`     // [1] en_us
t`Is ${name} really ${age} years old ???`     // [2] ko_kr
OUTPUT
[1] en_usIs Steve really 20 years old ???
[2] ko_krIs Steve really 20 years old ???

5 Put values into your text - Named Placeholder

Write ${{ something }} to give the placeholder a NAME ({something}). With m you define a message once and reuse it anywhere with different values — and .note() leaves a comment for the i18n translator. The value at define time is only for shape (a dummy is fine); pass real values when you call the message.

CODE
const something = "Steve";     // <- the input below

// define once, reuse anywhere — .note() is for the translator:
const hello = i18n.m`Hello~ world !!! And this is ${{ something }} !!!`
  .note("Greeting on the home page casually. {something} is the user's name, introducing himself.");

i18n.setLanguage("ko_kr");
hello({ something })            // [1]
hello({ something: "Alex" })    // [2] same message, a new value
Full Example Code
OUTPUT
[1] ko_krHello~ world !!! And this is Steve !!!
[2] ko_kr, "Alex"Hello~ world !!! And this is Alex !!!

6 i18n engine - Auto-complete

Your code stays a plain t call — grammar such as ordinals (1st, 2nd, 3rd), plurals, and number/date formats is completed AUTOMATICALLY by the i18n engine. With upgradeSourceLanguage: true even the source language (en_us) resolves through the i18n engine, so it works here too. The engine is self-improving: a fast first answer arrives immediately (draft) and a higher-quality refined version replaces it shortly after — subscribe() re-renders the UI on each.

CODE
const i18n = new I18n({
  namespace: "www.example.com",
  language: "en_us", sourceLanguage: "en_us",
  upgradeSourceLanguage: true, // improve the source language too
});

const name = "Steve";
for (let n = 1; n <= 4; n++) {
  // the i18n engine completes the grammar — 2nd, 3rd, 4th
  t`${name} is ${n}st winner in the contest.`
}
Full Example Code
OUTPUT
n=1Steve is 1st winner in the contest.
n=2Steve is 2nd winner in the contest.
n=3Steve is 3rd winner in the contest.
n=4Steve is 4th winner in the contest.

That's the whole everyday API — t, m, and .note(), plus subscribe() when the UI needs to follow updates. Plurals, gender branches, and number/date formats are handled by the i18n engine, so your code never needs to worry about them. Writing ICU MessageFormat by hand stays an option for advanced users — see the Full ICU support section on the home page.