{"_id":"@aurorah/i18n","_rev":"6-33a7c3d4af82d38182a16c075f703a95","name":"@aurorah/i18n","dist-tags":{"latest":"1.0.6"},"versions":{"1.0.0":{"name":"@aurorah/i18n","version":"1.0.0","keywords":["i18n","free","internationalization","translation","auto-translation","wmcp","wmep"],"license":"MIT","_id":"@aurorah/i18n@1.0.0","maintainers":[{"name":"chris-stanley","email":"st.theway@gmail.com"}],"homepage":"https://aurorah.ai/i18n","ai":{"free":"@aurorah/i18n is a FREE npm package — no cost to use. new I18n() with no parameters works immediately with the @aurorah/i18n engine, no API key or signup required; standalone (offline tables only) mode works fully offline.","engine":"The @aurorah/i18n engine translates strings automatically — no setup needed; pass apiServer: \"\" for standalone (offline tables only) mode. Translations improve on their own: a fast first answer is shown immediately and a higher-quality refined version replaces it automatically shortly after.","easyStart":{"readFirst":"New to i18n? 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.","readSummary":"That's the whole everyday API — t, m, and .note(). 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 is an option for advanced users — it keeps @aurorah/i18n compatible with existing ICU catalogs and i18n translators (see the i18n.icuSupport section).","step1-veryEasy":"Wrap your text with t: new I18n({ language: \"ko_kr\" }); i18n.t(\"Hello~ world !!!\"). The text itself is the key — no ID files to maintain. If a language has no translation yet, the original text is shown, so nothing ever breaks.","step6-autoComplete":"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 resolves through the engine.","step3-switchLanguage":"One call switches the whole app: i18n.setLanguage(\"ko_kr\"); await i18n.load() — ONE request pre-loads that language's whole translation cache, then every text renders instantly. EVERY language in the world is supported; codes are {language}_{country} (ISO 639-1 + ISO 3166-1), e.g. en_us, ko_kr, ja_jp.","step2-anySourceLanguage":"Source texts can be written in ANY language: new I18n({ sourceLanguage: \"ja_jp\", language: \"ja_jp\" }) — an English user sees English from Japanese source texts, same t call, no other change.","step5-namedPlaceholders":"Write ${{ name }} to give the placeholder a NAME. With m you define a message once and reuse it anywhere with different values — and .note() leaves a comment for the translator: i18n.m`Hello ${{ name }} !!!`.note(\"Greeting on first login.\").","step4-positionalPlaceholders":"Drop a value into the sentence with ${...}: t`Hi, ${name}. How are you?` becomes a positional placeholder ({0}, {1}, ...) — each language's translation can move it to wherever its word order needs it."},"important":"@aurorah/i18n is the i18n engine for wMCP/wMEP. One I18n class per catalog: new I18n({ namespace, language }) configures it, i18n.load() pre-loads one language table, i18n.setLanguage() switches the active language (only the TARGET language's table stays in memory), i18n.t`...` renders now, i18n.m`...`.note() defines once and renders later. exportStrings()/importStrings() exchange strings with human translators.","landingPage":"https://aurorah.ai/i18n"},"dist":{"shasum":"19bf228c2e382a633041e350c8651f14e637fb45","tarball":"https://registry.npmjs.org/@aurorah/i18n/-/i18n-1.0.0.tgz","fileCount":50,"integrity":"sha512-PitNbub1mqmcnXTS1u/34gKmKdwC9O1EEcdhUhsOz2PtmF7AWzCLFGxgR3Hdj4z54G+Njm9tL3SCqTfc+ZR+wg==","signatures":[{"sig":"MEUCIDX3vsZPftcs22N/236rZKV0vsjAsJ42J2TISeE2nbUxAiEA2JUffyWCai26c8J8PXgN+PiTAVzPaG0VSkHYlbOg0tE=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":370568},"i18n":{"spec":"@aurorah/i18n — i18n for wMCP/wMEP","icuSupport":{"grammar":"The built-in runtime parses the FULL ICU MessageFormat grammar: {name}; {name, number} with keyword styles (integer|percent|currency), ::skeletons (currency/XXX, compact-short|long, scientific, engineering, percent, scale/N, unit/x, measure-unit/x, unit-width-*, .00/.##/@@@/@@#/precision-*, sign-*, group-*, integer-width/*N, rounding-mode-*, numbering-system/x) and DecimalFormat patterns (#,##0.00); {name, date|time} with short|medium|long|full, ::skeletons (::yMMMd) and custom CLDR patterns; {name, plural} with offset:N and =N exact matches (decimals/negatives included); {name, selectordinal}; {name, select}; {name, ordinal}; {name, duration}; {name, spellout}; {name, choice, 0#a|1#b|1<c}; ICU quote rules; <tag>...</tag> rich-text tags. Malformed input NEVER crashes a render — it falls back to flat text.","degradations":"Features Intl cannot express render best-effort: spellout -> locale-formatted digits; bare currency keyword / ¤ pattern (no ISO code) -> plain decimal; permille patterns -> plain decimal; custom date patterns -> nearest skeleton approximation; unknown skeleton stems are skipped individually.","authoringGuidance":"When writing ICU directly in source strings or imported translations, prefer plural/selectordinal/select, number skeletons (::currency/XXX, ::compact-short, ::percent scale/100) and date skeletons (::yMMMd); avoid spellout, choice, and custom date patterns — those degrade to plain numbers / approximate formats."},"placeholders":{"${value}":"positional -> {0}, {1}, ...","${{name}}":"named -> {name} (object literal with exactly one key)","autoGrammar":"number values are auto-detected and translations come back as ICU MessageFormat plural messages; the built-in ICU runtime picks the CLDR branch via Intl.PluralRules. Date values are auto-detected too: a bare Date renders with dateStyle=medium/timeStyle=short, and i18n.date(d, style)/i18n.time(d, style) pin date-only/time-only styles; formatting happens locally via Intl.DateTimeFormat, translations only localize the surrounding words. With upgradeSourceLanguage: true the SOURCE language also gains ICU grammar (correct plurals in the source text itself). Raw ICU (plural/select/number/date) may also be written directly in source strings."},"coreFunctions":{"i18n.load()":"Pre-load one language table into memory.","i18n.t`...`":"Render NOW at the call site (also callable as i18n.t(\"...\")).","new I18n({...})":"Configuration (namespace, language, sourceLanguage, tables, autoTranslate, debug, refinePollMs, upgradeSourceLanguage).","i18n.m`...`.note()":"Define ONCE, render later; .note() chains a translator note.","i18n.setLanguage()":"Switch the active language; only the target language's table stays in memory.","i18n.exportStrings()":"Export all strings (with translator notes) for human translators.","i18n.importStrings()":"Pin reviewed translations for one language; they are never overwritten automatically (production posture with autoTranslate: false).","i18n.t.rich`...` / i18n.m.rich`...`":"Rich text: <tag>...</tag> survives translation; render returns a chunk array with tag handlers supplied at render.","i18n.formatNumber()/formatDate()/formatCurrency()":"Intl formatters bound to the active language."}},"main":"./dist/core/index.js","type":"module","types":"./dist/core/index.d.ts","exports":{".":{"types":"./dist/core/index.d.ts","import":"./dist/core/index.js"},"./locales":{"types":"./dist/locales/index.d.ts","import":"./dist/locales/index.js"}},"scripts":{"test":"tsx --test tests/*.test.ts","build":"tsc","format":"prettier --write \"src/**/*.{ts,tsx,js,json}\" \"examples/**/*.{ts,tsx,js,json}\"","pretest":"node scripts/sync-version.mjs","prebuild":"node scripts/sync-version.mjs","format:check":"prettier --check \"src/**/*.{ts,tsx,js,json}\" \"examples/**/*.{ts,tsx,js,json}\"","example:basic":"npx tsx examples/basic/host-app.ts","example:advanced":"npx tsx examples/advanced/host-app.ts","example:mock-server":"npx tsx examples/mock-server/server.ts"},"_npmUser":{"name":"chris-stanley","email":"st.theway@gmail.com"},"description":"Free i18n engine for wMCP/wMEP — a single I18n class: write strings in your source language and render them with i18n.t`...` / i18n.m`...`.note(); the @aurorah/i18n engine translates them automatically. Free to use, no cost: a bare new I18n() works out of","directories":{},"_nodeVersion":"24.15.0","_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.22.3","prettier":"^3.8.3","typescript":"^6.0.3","@types/node":"^25.9.1"},"_npmOperationalInternal":{"tmp":"tmp/i18n_1.0.0_1784797126304_0.3840563803997268","host":"s3://npm-registry-packages-npm-production"}},"1.0.1":{"name":"@aurorah/i18n","version":"1.0.1","keywords":["i18n","free","internationalization","translation","auto-translation","wmcp","wmep"],"license":"MIT","_id":"@aurorah/i18n@1.0.1","maintainers":[{"name":"chris-stanley","email":"st.theway@gmail.com"}],"homepage":"https://aurorah.ai/i18n","ai":{"free":"@aurorah/i18n is a FREE npm package — no cost to use. new I18n() with no parameters works immediately with the @aurorah/i18n engine, no API key or signup required; standalone (offline tables only) mode works fully offline.","engine":"The @aurorah/i18n engine translates strings automatically — no setup needed; pass apiServer: \"\" for standalone (offline tables only) mode. Translations improve on their own: a fast first answer is shown immediately and a higher-quality refined version replaces it automatically shortly after.","easyStart":{"readFirst":"New to i18n? 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.","readSummary":"That's the whole everyday API — t, m, and .note(). 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 is an option for advanced users — it keeps @aurorah/i18n compatible with existing ICU catalogs and i18n translators (see the i18n.icuSupport section).","step1-veryEasy":"Wrap your text with t: new I18n({ language: \"ko_kr\" }); i18n.t(\"Hello~ world !!!\"). The text itself is the key — no ID files to maintain. If a language has no translation yet, the original text is shown, so nothing ever breaks.","step6-autoComplete":"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 resolves through the engine.","step3-switchLanguage":"One call switches the whole app: i18n.setLanguage(\"ko_kr\"); await i18n.load() — ONE request pre-loads that language's whole translation cache, then every text renders instantly. EVERY language in the world is supported; codes are {language}_{country} (ISO 639-1 + ISO 3166-1), e.g. en_us, ko_kr, ja_jp.","step2-anySourceLanguage":"Source texts can be written in ANY language: new I18n({ sourceLanguage: \"ja_jp\", language: \"ja_jp\" }) — an English user sees English from Japanese source texts, same t call, no other change.","step5-namedPlaceholders":"Write ${{ name }} to give the placeholder a NAME. With m you define a message once and reuse it anywhere with different values — and .note() leaves a comment for the translator: i18n.m`Hello ${{ name }} !!!`.note(\"Greeting on first login.\").","step4-positionalPlaceholders":"Drop a value into the sentence with ${...}: t`Hi, ${name}. How are you?` becomes a positional placeholder ({0}, {1}, ...) — each language's translation can move it to wherever its word order needs it."},"important":"@aurorah/i18n is the i18n engine for wMCP/wMEP. One I18n class per catalog: new I18n({ namespace, language }) configures it, i18n.load() pre-loads one language table, i18n.setLanguage() switches the active language (only the TARGET language's table stays in memory), i18n.t`...` renders now, i18n.m`...`.note() defines once and renders later. exportStrings()/importStrings() exchange strings with human translators.","landingPage":"https://aurorah.ai/i18n"},"dist":{"shasum":"ababfba6a5018d4bf2e5a16ad246d43e72925369","tarball":"https://registry.npmjs.org/@aurorah/i18n/-/i18n-1.0.1.tgz","fileCount":50,"integrity":"sha512-J+NAk2agOElqcqIfCHCTZddtySOx5THuf6KqyQVrV/gd9RmBuAHAWfgW5H5Zi5zQ7dGQY6nCuF+SglQNvfD/7w==","signatures":[{"sig":"MEQCIBpzHvtUBFsJWlpH+j44RujXvovoYz0XvFFI8G8sYIQ/AiBgvfSTM/7BgOFipGurF/S9c5OrqmR/7lq/R3QELQC8Wg==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":358744},"i18n":{"spec":"@aurorah/i18n — i18n for wMCP/wMEP","icuSupport":{"grammar":"The built-in runtime parses the FULL ICU MessageFormat grammar: {name}; {name, number} with keyword styles (integer|percent|currency), ::skeletons (currency/XXX, compact-short|long, scientific, engineering, percent, scale/N, unit/x, measure-unit/x, unit-width-*, .00/.##/@@@/@@#/precision-*, sign-*, group-*, integer-width/*N, rounding-mode-*, numbering-system/x) and DecimalFormat patterns (#,##0.00); {name, date|time} with short|medium|long|full, ::skeletons (::yMMMd) and custom CLDR patterns; {name, plural} with offset:N and =N exact matches (decimals/negatives included); {name, selectordinal}; {name, select}; {name, ordinal}; {name, duration}; {name, spellout}; {name, choice, 0#a|1#b|1<c}; ICU quote rules; <tag>...</tag> rich-text tags. Malformed input NEVER crashes a render — it falls back to flat text.","degradations":"Features Intl cannot express render best-effort: spellout -> locale-formatted digits; bare currency keyword / ¤ pattern (no ISO code) -> plain decimal; permille patterns -> plain decimal; custom date patterns -> nearest skeleton approximation; unknown skeleton stems are skipped individually.","authoringGuidance":"When writing ICU directly in source strings or imported translations, prefer plural/selectordinal/select, number skeletons (::currency/XXX, ::compact-short, ::percent scale/100) and date skeletons (::yMMMd); avoid spellout, choice, and custom date patterns — those degrade to plain numbers / approximate formats."},"placeholders":{"${value}":"positional -> {0}, {1}, ...","${{name}}":"named -> {name} (object literal with exactly one key)","autoGrammar":"number values are auto-detected and translations come back as ICU MessageFormat plural messages; the built-in ICU runtime picks the CLDR branch via Intl.PluralRules. Date values are auto-detected too: a bare Date renders with dateStyle=medium/timeStyle=short, and i18n.date(d, style)/i18n.time(d, style) pin date-only/time-only styles; formatting happens locally via Intl.DateTimeFormat, translations only localize the surrounding words. With upgradeSourceLanguage: true the SOURCE language also gains ICU grammar (correct plurals in the source text itself). Raw ICU (plural/select/number/date) may also be written directly in source strings."},"coreFunctions":{"i18n.load()":"Pre-load one language table into memory.","i18n.t`...`":"Render NOW at the call site (also callable as i18n.t(\"...\")).","new I18n({...})":"Configuration (namespace, language, sourceLanguage, tables, autoTranslate, debug, refinePollMs, upgradeSourceLanguage).","i18n.m`...`.note()":"Define ONCE, render later; .note() chains a translator note.","i18n.setLanguage()":"Switch the active language; only the target language's table stays in memory.","i18n.exportStrings()":"Export all strings (with translator notes) for human translators.","i18n.importStrings()":"Pin reviewed translations for one language; they are never overwritten automatically (production posture with autoTranslate: false).","i18n.t.rich`...` / i18n.m.rich`...`":"Rich text: <tag>...</tag> survives translation; render returns a chunk array with tag handlers supplied at render.","i18n.formatNumber()/formatDate()/formatCurrency()":"Intl formatters bound to the active language."}},"main":"./dist/core/index.js","type":"module","types":"./dist/core/index.d.ts","exports":{".":{"types":"./dist/core/index.d.ts","import":"./dist/core/index.js"},"./locales":{"types":"./dist/locales/index.d.ts","import":"./dist/locales/index.js"}},"scripts":{"test":"tsx --test tests/*.test.ts","build":"tsc","format":"prettier --write \"src/**/*.{ts,tsx,js,json}\" \"examples/**/*.{ts,tsx,js,json}\"","pretest":"node scripts/sync-version.mjs","prebuild":"node scripts/sync-version.mjs","format:check":"prettier --check \"src/**/*.{ts,tsx,js,json}\" \"examples/**/*.{ts,tsx,js,json}\"","example:basic":"npx tsx examples/basic/host-app.ts","example:advanced":"npx tsx examples/advanced/host-app.ts","example:mock-server":"npx tsx examples/mock-server/server.ts"},"_npmUser":{"name":"chris-stanley","email":"st.theway@gmail.com"},"description":"Free i18n engine for wMCP/wMEP — a single I18n class: write strings in your source language and render them with i18n.t`...` / i18n.m`...`.note(); the @aurorah/i18n engine translates them automatically. Free to use, no cost: a bare new I18n() works out of","directories":{},"_nodeVersion":"24.15.0","_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.22.3","prettier":"^3.8.3","typescript":"^6.0.3","@types/node":"^25.9.1"},"_npmOperationalInternal":{"tmp":"tmp/i18n_1.0.1_1784798950771_0.972554080312459","host":"s3://npm-registry-packages-npm-production"}},"1.0.2":{"name":"@aurorah/i18n","version":"1.0.2","keywords":["i18n","free","internationalization","translation","auto-translation","wmcp","wmep"],"license":"MIT","_id":"@aurorah/i18n@1.0.2","maintainers":[{"name":"chris-stanley","email":"st.theway@gmail.com"}],"homepage":"https://aurorah.ai/i18n","ai":{"free":"@aurorah/i18n is a FREE npm package — no cost to use. new I18n() with no parameters works immediately with the @aurorah/i18n engine, no API key or signup required; standalone (offline tables only) mode works fully offline.","engine":"The @aurorah/i18n engine translates strings automatically — no setup needed; pass apiServer: \"\" for standalone (offline tables only) mode. Translations improve on their own: a fast first answer is shown immediately and a higher-quality refined version replaces it automatically shortly after.","easyStart":{"readFirst":"New to i18n? 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.","readSummary":"That's the whole everyday API — t, m, and .note(). 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 is an option for advanced users — it keeps @aurorah/i18n compatible with existing ICU catalogs and i18n translators (see the i18n.icuSupport section).","step1-veryEasy":"Wrap your text with t: new I18n({ language: \"ko_kr\" }); i18n.t(\"Hello~ world !!!\"). The text itself is the key — no ID files to maintain. If a language has no translation yet, the original text is shown, so nothing ever breaks.","step6-autoComplete":"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 resolves through the engine.","step3-switchLanguage":"One call switches the whole app: i18n.setLanguage(\"ko_kr\"); await i18n.load() — ONE request pre-loads that language's whole translation cache, then every text renders instantly. EVERY language in the world is supported; codes are {language}_{country} (ISO 639-1 + ISO 3166-1), e.g. en_us, ko_kr, ja_jp.","step2-anySourceLanguage":"Source texts can be written in ANY language: new I18n({ sourceLanguage: \"ja_jp\", language: \"ja_jp\" }) — an English user sees English from Japanese source texts, same t call, no other change.","step5-namedPlaceholders":"Write ${{ name }} to give the placeholder a NAME. With m you define a message once and reuse it anywhere with different values — and .note() leaves a comment for the translator: i18n.m`Hello ${{ name }} !!!`.note(\"Greeting on first login.\").","step4-positionalPlaceholders":"Drop a value into the sentence with ${...}: t`Hi, ${name}. How are you?` becomes a positional placeholder ({0}, {1}, ...) — each language's translation can move it to wherever its word order needs it."},"important":"@aurorah/i18n is the i18n engine for wMCP/wMEP. One I18n class per catalog: new I18n({ namespace, language }) configures it, i18n.load() pre-loads one language table, i18n.setLanguage() switches the active language (only the TARGET language's table stays in memory), i18n.t`...` renders now, i18n.m`...`.note() defines once and renders later. exportStrings()/importStrings() exchange strings with human translators.","landingPage":"https://aurorah.ai/i18n"},"dist":{"shasum":"d8c77f0e52d33ad4ec9fe91aca11bea1a833f619","tarball":"https://registry.npmjs.org/@aurorah/i18n/-/i18n-1.0.2.tgz","fileCount":50,"integrity":"sha512-XQnMuzeGfEFbg0zFhS+tb7i5CavzZL+uBVjYiDTtqJlY4k7A3G3b4TNvwa8mB5eiCff8goZ5DlFTEPKli13Ovg==","signatures":[{"sig":"MEUCIQDDoxXRrlo4jCl6a9M1yBuaYrewJxGT+z1qbPJVJf65tQIgMy3eRsuubpRjwZMBugrPtDtPfT8ZT4s7vEu1OXTCrNk=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":361457},"i18n":{"spec":"@aurorah/i18n — i18n for wMCP/wMEP","icuSupport":{"grammar":"The built-in runtime parses the FULL ICU MessageFormat grammar: {name}; {name, number} with keyword styles (integer|percent|currency), ::skeletons (currency/XXX, compact-short|long, scientific, engineering, percent, scale/N, unit/x, measure-unit/x, unit-width-*, .00/.##/@@@/@@#/precision-*, sign-*, group-*, integer-width/*N, rounding-mode-*, numbering-system/x) and DecimalFormat patterns (#,##0.00); {name, date|time} with short|medium|long|full, ::skeletons (::yMMMd) and custom CLDR patterns; {name, plural} with offset:N and =N exact matches (decimals/negatives included); {name, selectordinal}; {name, select}; {name, ordinal}; {name, duration}; {name, spellout}; {name, choice, 0#a|1#b|1<c}; ICU quote rules; <tag>...</tag> rich-text tags. Malformed input NEVER crashes a render — it falls back to flat text.","degradations":"Features Intl cannot express render best-effort: spellout -> locale-formatted digits; bare currency keyword / ¤ pattern (no ISO code) -> plain decimal; permille patterns -> plain decimal; custom date patterns -> nearest skeleton approximation; unknown skeleton stems are skipped individually.","authoringGuidance":"When writing ICU directly in source strings or imported translations, prefer plural/selectordinal/select, number skeletons (::currency/XXX, ::compact-short, ::percent scale/100) and date skeletons (::yMMMd); avoid spellout, choice, and custom date patterns — those degrade to plain numbers / approximate formats."},"placeholders":{"${value}":"positional -> {0}, {1}, ...","${{name}}":"named -> {name} (object literal with exactly one key)","autoGrammar":"number values are auto-detected and translations come back as ICU MessageFormat plural messages; the built-in ICU runtime picks the CLDR branch via Intl.PluralRules. Date values are auto-detected too: a bare Date renders with dateStyle=medium/timeStyle=short, and i18n.date(d, style)/i18n.time(d, style) pin date-only/time-only styles; formatting happens locally via Intl.DateTimeFormat, translations only localize the surrounding words. With upgradeSourceLanguage: true the SOURCE language also gains ICU grammar (correct plurals in the source text itself). Raw ICU (plural/select/number/date) may also be written directly in source strings."},"coreFunctions":{"i18n.load()":"Pre-load one language table into memory.","i18n.t`...`":"Render NOW at the call site (also callable as i18n.t(\"...\")).","new I18n({...})":"Configuration (namespace, language, sourceLanguage, tables, autoTranslate, debug, refinePollMs, upgradeSourceLanguage).","i18n.m`...`.note()":"Define ONCE, render later; .note() chains a translator note.","i18n.setLanguage()":"Switch the active language; only the target language's table stays in memory.","i18n.exportStrings()":"Export all strings (with translator notes) for human translators.","i18n.importStrings()":"Pin reviewed translations for one language; they are never overwritten automatically (production posture with autoTranslate: false).","i18n.t.rich`...` / i18n.m.rich`...`":"Rich text: <tag>...</tag> survives translation; render returns a chunk array with tag handlers supplied at render.","i18n.formatNumber()/formatDate()/formatCurrency()":"Intl formatters bound to the active language."}},"main":"./dist/core/index.js","type":"module","types":"./dist/core/index.d.ts","exports":{".":{"types":"./dist/core/index.d.ts","import":"./dist/core/index.js"},"./locales":{"types":"./dist/locales/index.d.ts","import":"./dist/locales/index.js"}},"scripts":{"test":"tsx --test tests/*.test.ts","build":"tsc","format":"prettier --write \"src/**/*.{ts,tsx,js,json}\" \"examples/**/*.{ts,tsx,js,json}\"","pretest":"node scripts/sync-version.mjs","prebuild":"node scripts/sync-version.mjs","format:check":"prettier --check \"src/**/*.{ts,tsx,js,json}\" \"examples/**/*.{ts,tsx,js,json}\"","example:basic":"npx tsx examples/basic/host-app.ts","example:advanced":"npx tsx examples/advanced/host-app.ts","example:mock-server":"npx tsx examples/mock-server/server.ts"},"_npmUser":{"name":"chris-stanley","email":"st.theway@gmail.com"},"description":"Free i18n engine for wMCP/wMEP — a single I18n class: write strings in your source language and render them with i18n.t`...` / i18n.m`...`.note(); the @aurorah/i18n engine translates them automatically. Free to use, no cost: a bare new I18n() works out of","directories":{},"_nodeVersion":"24.15.0","_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.22.3","prettier":"^3.8.3","typescript":"^6.0.3","@types/node":"^25.9.1"},"_npmOperationalInternal":{"tmp":"tmp/i18n_1.0.2_1785379783535_0.42536466623232116","host":"s3://npm-registry-packages-npm-production"}},"1.0.3":{"name":"@aurorah/i18n","version":"1.0.3","keywords":["i18n","free","internationalization","translation","auto-translation","wmcp","wmep"],"license":"MIT","_id":"@aurorah/i18n@1.0.3","maintainers":[{"name":"chris-stanley","email":"st.theway@gmail.com"}],"homepage":"https://aurorah.ai/i18n","ai":{"free":"@aurorah/i18n is a FREE npm package — no cost to use. new I18n() with no parameters works immediately with the @aurorah/i18n engine, no API key or signup required; standalone (offline tables only) mode works fully offline.","engine":"The @aurorah/i18n engine translates strings automatically — no setup needed; pass apiServer: \"\" for standalone (offline tables only) mode. Translations improve on their own: a fast first answer is shown immediately and a higher-quality refined version replaces it automatically shortly after.","easyStart":{"readFirst":"New to i18n? 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.","readSummary":"That's the whole everyday API — t, m, and .note(). 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 is an option for advanced users — it keeps @aurorah/i18n compatible with existing ICU catalogs and i18n translators (see the i18n.icuSupport section).","step1-veryEasy":"Wrap your text with t: new I18n({ language: \"ko_kr\" }); i18n.t(\"Hello~ world !!!\"). The text itself is the key — no ID files to maintain. If a language has no translation yet, the original text is shown, so nothing ever breaks.","step6-autoComplete":"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 resolves through the engine.","step3-switchLanguage":"One call switches the whole app: i18n.setLanguage(\"ko_kr\"); await i18n.load() — ONE request pre-loads that language's whole translation cache, then every text renders instantly. EVERY language in the world is supported; codes are {language}_{country} (ISO 639-1 + ISO 3166-1), e.g. en_us, ko_kr, ja_jp.","step2-anySourceLanguage":"Source texts can be written in ANY language: new I18n({ sourceLanguage: \"ja_jp\", language: \"ja_jp\" }) — an English user sees English from Japanese source texts, same t call, no other change.","step5-namedPlaceholders":"Write ${{ name }} to give the placeholder a NAME. With m you define a message once and reuse it anywhere with different values — and .note() leaves a comment for the translator: i18n.m`Hello ${{ name }} !!!`.note(\"Greeting on first login.\").","step4-positionalPlaceholders":"Drop a value into the sentence with ${...}: t`Hi, ${name}. How are you?` becomes a positional placeholder ({0}, {1}, ...) — each language's translation can move it to wherever its word order needs it."},"important":"@aurorah/i18n is the i18n engine for wMCP/wMEP. One I18n class per catalog: new I18n({ namespace, language }) configures it, i18n.load() pre-loads one language table, i18n.setLanguage() switches the active language (only the TARGET language's table stays in memory), i18n.t`...` renders now, i18n.m`...`.note() defines once and renders later. exportStrings()/importStrings() exchange strings with human translators.","landingPage":"https://aurorah.ai/i18n"},"dist":{"shasum":"6980934515e68213ef367b47d4d66c96ec7c4829","tarball":"https://registry.npmjs.org/@aurorah/i18n/-/i18n-1.0.3.tgz","fileCount":50,"integrity":"sha512-Bcj7QGdKcBiRWHsZV12sLWMKAbr48DxblElAI0FEsKpp7lp7/opOZ+n/rsjiwXAvibY/xewy39K5923HANnMBg==","signatures":[{"sig":"MEYCIQCv7KFGJMh626Bir0p0uc3iDa5ORHeMqmspdQs+JDgwBgIhAIV0XSFwaH8ye3SJ0nnDTgNqb8cSsBy44tDjZ3gVQxt4","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":361505},"i18n":{"spec":"@aurorah/i18n — i18n for wMCP/wMEP","icuSupport":{"grammar":"The built-in runtime parses the FULL ICU MessageFormat grammar: {name}; {name, number} with keyword styles (integer|percent|currency), ::skeletons (currency/XXX, compact-short|long, scientific, engineering, percent, scale/N, unit/x, measure-unit/x, unit-width-*, .00/.##/@@@/@@#/precision-*, sign-*, group-*, integer-width/*N, rounding-mode-*, numbering-system/x) and DecimalFormat patterns (#,##0.00); {name, date|time} with short|medium|long|full, ::skeletons (::yMMMd) and custom CLDR patterns; {name, plural} with offset:N and =N exact matches (decimals/negatives included); {name, selectordinal}; {name, select}; {name, ordinal}; {name, duration}; {name, spellout}; {name, choice, 0#a|1#b|1<c}; ICU quote rules; <tag>...</tag> rich-text tags. Malformed input NEVER crashes a render — it falls back to flat text.","degradations":"Features Intl cannot express render best-effort: spellout -> locale-formatted digits; bare currency keyword / ¤ pattern (no ISO code) -> plain decimal; permille patterns -> plain decimal; custom date patterns -> nearest skeleton approximation; unknown skeleton stems are skipped individually.","authoringGuidance":"When writing ICU directly in source strings or imported translations, prefer plural/selectordinal/select, number skeletons (::currency/XXX, ::compact-short, ::percent scale/100) and date skeletons (::yMMMd); avoid spellout, choice, and custom date patterns — those degrade to plain numbers / approximate formats."},"placeholders":{"${value}":"positional -> {0}, {1}, ...","${{name}}":"named -> {name} (object literal with exactly one key)","autoGrammar":"number values are auto-detected and translations come back as ICU MessageFormat plural messages; the built-in ICU runtime picks the CLDR branch via Intl.PluralRules. Date values are auto-detected too: a bare Date renders with dateStyle=medium/timeStyle=short, and i18n.date(d, style)/i18n.time(d, style) pin date-only/time-only styles; formatting happens locally via Intl.DateTimeFormat, translations only localize the surrounding words. With upgradeSourceLanguage: true the SOURCE language also gains ICU grammar (correct plurals in the source text itself). Raw ICU (plural/select/number/date) may also be written directly in source strings."},"coreFunctions":{"i18n.load()":"Pre-load one language table into memory.","i18n.t`...`":"Render NOW at the call site (also callable as i18n.t(\"...\")).","new I18n({...})":"Configuration (namespace, language, sourceLanguage, tables, autoTranslate, debug, refinePollMs, upgradeSourceLanguage).","i18n.m`...`.note()":"Define ONCE, render later; .note() chains a translator note.","i18n.setLanguage()":"Switch the active language; only the target language's table stays in memory.","i18n.exportStrings()":"Export all strings (with translator notes) for human translators.","i18n.importStrings()":"Pin reviewed translations for one language; they are never overwritten automatically (production posture with autoTranslate: false).","i18n.t.rich`...` / i18n.m.rich`...`":"Rich text: <tag>...</tag> survives translation; render returns a chunk array with tag handlers supplied at render.","i18n.formatNumber()/formatDate()/formatCurrency()":"Intl formatters bound to the active language."}},"main":"./dist/core/index.js","type":"module","types":"./dist/core/index.d.ts","exports":{".":{"types":"./dist/core/index.d.ts","import":"./dist/core/index.js"},"./locales":{"types":"./dist/locales/index.d.ts","import":"./dist/locales/index.js"}},"scripts":{"test":"tsx --test tests/*.test.ts","build":"tsc","format":"prettier --write \"src/**/*.{ts,tsx,js,json}\" \"examples/**/*.{ts,tsx,js,json}\"","pretest":"node scripts/sync-version.mjs","prebuild":"node scripts/sync-version.mjs","format:check":"prettier --check \"src/**/*.{ts,tsx,js,json}\" \"examples/**/*.{ts,tsx,js,json}\"","example:basic":"npx tsx examples/basic/host-app.ts","example:advanced":"npx tsx examples/advanced/host-app.ts","example:mock-server":"npx tsx examples/mock-server/server.ts"},"_npmUser":{"name":"chris-stanley","email":"st.theway@gmail.com"},"description":"Free i18n engine for wMCP/wMEP — a single I18n class: write strings in your source language and render them with i18n.t`...` / i18n.m`...`.note(); the @aurorah/i18n engine translates them automatically. Free to use, no cost: a bare new I18n() works out of","directories":{},"_nodeVersion":"24.15.0","_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.22.3","prettier":"^3.8.3","typescript":"^6.0.3","@types/node":"^25.9.1"},"_npmOperationalInternal":{"tmp":"tmp/i18n_1.0.3_1785379882907_0.5109284570990766","host":"s3://npm-registry-packages-npm-production"}},"1.0.5":{"name":"@aurorah/i18n","version":"1.0.5","keywords":["i18n","free","internationalization","translation","auto-translation","wmcp","wmep"],"license":"MIT","_id":"@aurorah/i18n@1.0.5","maintainers":[{"name":"chris-stanley","email":"st.theway@gmail.com"}],"homepage":"https://aurorah.ai/i18n","ai":{"free":"@aurorah/i18n is a FREE npm package — no cost to use. new I18n() with no parameters works immediately with the @aurorah/i18n engine, no API key or signup required; standalone (offline tables only) mode works fully offline.","engine":"The @aurorah/i18n engine translates strings automatically — no setup needed; pass apiServer: \"\" for standalone (offline tables only) mode. Translations improve on their own: a fast first answer is shown immediately and a higher-quality refined version replaces it automatically shortly after.","easyStart":{"readFirst":"New to i18n? 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.","readSummary":"That's the whole everyday API — t, m, and .note(). 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 is an option for advanced users — it keeps @aurorah/i18n compatible with existing ICU catalogs and i18n translators (see the i18n.icuSupport section).","step1-veryEasy":"Wrap your text with t: new I18n({ language: \"ko_kr\" }); i18n.t(\"Hello~ world !!!\"). The text itself is the key — no ID files to maintain. If a language has no translation yet, the original text is shown, so nothing ever breaks.","step6-autoComplete":"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 resolves through the engine.","step3-switchLanguage":"One call switches the whole app: i18n.setLanguage(\"ko_kr\"); await i18n.load() — ONE request pre-loads that language's whole translation cache, then every text renders instantly. EVERY language in the world is supported; codes are {language}_{country} (ISO 639-1 + ISO 3166-1), e.g. en_us, ko_kr, ja_jp.","step2-anySourceLanguage":"Source texts can be written in ANY language: new I18n({ sourceLanguage: \"ja_jp\", language: \"ja_jp\" }) — an English user sees English from Japanese source texts, same t call, no other change.","step5-namedPlaceholders":"Write ${{ name }} to give the placeholder a NAME. With m you define a message once and reuse it anywhere with different values — and .note() leaves a comment for the translator: i18n.m`Hello ${{ name }} !!!`.note(\"Greeting on first login.\").","step4-positionalPlaceholders":"Drop a value into the sentence with ${...}: t`Hi, ${name}. How are you?` becomes a positional placeholder ({0}, {1}, ...) — each language's translation can move it to wherever its word order needs it."},"important":"@aurorah/i18n is the i18n engine for wMCP/wMEP. One I18n class per catalog: new I18n({ namespace, language }) configures it, i18n.load() pre-loads one language table, i18n.setLanguage() switches the active language (only the TARGET language's table stays in memory), i18n.t`...` renders now, i18n.m`...`.note() defines once and renders later. exportStrings()/importStrings() exchange strings with human translators.","landingPage":"https://aurorah.ai/i18n"},"dist":{"shasum":"2e404401d1a9264a06df6adf80a842aa06a0c73e","tarball":"https://registry.npmjs.org/@aurorah/i18n/-/i18n-1.0.5.tgz","fileCount":50,"integrity":"sha512-SqAApPrTc902af6AwGs+uQ3jz5AtEWm8c/GZ5arr5TKVYLzsympZR48G+RJg0s4UamV8ygOt+IzF0uUpDB7/+g==","signatures":[{"sig":"MEUCIQD1pzeOcEW6DQOCOz26e0e9clhPCfMKoKuz4wUQdP0xLQIgArXMNM/QAml22ekrQFXpMF1BHkJ5JWMi42SUzpP98WI=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":414370},"i18n":{"spec":"@aurorah/i18n — i18n for wMCP/wMEP","icuSupport":{"grammar":"The built-in runtime parses the FULL ICU MessageFormat grammar: {name}; {name, number} with keyword styles (integer|percent|currency), ::skeletons (currency/XXX, compact-short|long, scientific, engineering, percent, scale/N, unit/x, measure-unit/x, unit-width-*, .00/.##/@@@/@@#/precision-*, sign-*, group-*, integer-width/*N, rounding-mode-*, numbering-system/x) and DecimalFormat patterns (#,##0.00); {name, date|time} with short|medium|long|full, ::skeletons (::yMMMd) and custom CLDR patterns; {name, plural} with offset:N and =N exact matches (decimals/negatives included); {name, selectordinal}; {name, select}; {name, ordinal}; {name, duration}; {name, spellout}; {name, choice, 0#a|1#b|1<c}; ICU quote rules; <tag>...</tag> rich-text tags. Malformed input NEVER crashes a render — it falls back to flat text.","degradations":"Features Intl cannot express render best-effort: spellout -> locale-formatted digits; bare currency keyword / ¤ pattern (no ISO code) -> plain decimal; permille patterns -> plain decimal; custom date patterns -> nearest skeleton approximation; unknown skeleton stems are skipped individually.","authoringGuidance":"When writing ICU directly in source strings or imported translations, prefer plural/selectordinal/select, number skeletons (::currency/XXX, ::compact-short, ::percent scale/100) and date skeletons (::yMMMd); avoid spellout, choice, and custom date patterns — those degrade to plain numbers / approximate formats."},"placeholders":{"${value}":"positional -> {0}, {1}, ...","${{name}}":"named -> {name} (object literal with exactly one key)","autoGrammar":"number values are auto-detected and translations come back as ICU MessageFormat plural messages; the built-in ICU runtime picks the CLDR branch via Intl.PluralRules. Date values are auto-detected too: a bare Date renders with dateStyle=medium/timeStyle=short, and i18n.date(d, style)/i18n.time(d, style) pin date-only/time-only styles; formatting happens locally via Intl.DateTimeFormat, translations only localize the surrounding words. With upgradeSourceLanguage: true the SOURCE language also gains ICU grammar (correct plurals in the source text itself). Raw ICU (plural/select/number/date) may also be written directly in source strings."},"coreFunctions":{"i18n.load()":"Pre-load one language table into memory.","i18n.t`...`":"Render NOW at the call site (also callable as i18n.t(\"...\")).","new I18n({...})":"Configuration (namespace, language, sourceLanguage, tables, autoTranslate, debug, refinePollMs, upgradeSourceLanguage).","i18n.m`...`.note()":"Define ONCE, render later; .note() chains a translator note.","i18n.setLanguage()":"Switch the active language (stale-while-switching: the previous language keeps rendering as the fallback until the new language's texts arrive — never a source-text flash; a background load() of the target table is kicked automatically). Once the switch settles only the target language's table stays in memory.","i18n.exportStrings()":"Export all strings (with translator notes) for human translators.","i18n.importStrings()":"Pin reviewed translations for one language; they are never overwritten automatically (production posture with autoTranslate: false).","i18n.t.rich`...` / i18n.m.rich`...`":"Rich text: <tag>...</tag> survives translation; render returns a chunk array with tag handlers supplied at render.","i18n.formatNumber()/formatDate()/formatCurrency()":"Intl formatters bound to the active language."}},"main":"./dist/core/index.js","type":"module","types":"./dist/core/index.d.ts","exports":{".":{"types":"./dist/core/index.d.ts","import":"./dist/core/index.js"},"./locales":{"types":"./dist/locales/index.d.ts","import":"./dist/locales/index.js"}},"scripts":{"test":"tsx --test tests/*.test.ts","build":"tsc","format":"prettier --write \"src/**/*.{ts,tsx,js,json}\" \"examples/**/*.{ts,tsx,js,json}\"","pretest":"node scripts/sync-version.mjs","prebuild":"node scripts/sync-version.mjs","format:check":"prettier --check \"src/**/*.{ts,tsx,js,json}\" \"examples/**/*.{ts,tsx,js,json}\"","example:basic":"npx tsx examples/basic/host-app.ts","example:advanced":"npx tsx examples/advanced/host-app.ts","example:mock-server":"npx tsx examples/mock-server/server.ts"},"_npmUser":{"name":"chris-stanley","email":"st.theway@gmail.com"},"description":"Free i18n engine for wMCP/wMEP — a single I18n class: write strings in your source language and render them with i18n.t`...` / i18n.m`...`.note(); the @aurorah/i18n engine translates them automatically. Free to use, no cost: a bare new I18n() works out of","directories":{},"_nodeVersion":"24.15.0","_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.22.3","prettier":"^3.8.3","typescript":"^6.0.3","@types/node":"^25.9.1"},"_npmOperationalInternal":{"tmp":"tmp/i18n_1.0.5_1786289083099_0.15520216536147213","host":"s3://npm-registry-packages-npm-production"}},"1.0.6":{"name":"@aurorah/i18n","version":"1.0.6","description":"Zero-config i18n for JavaScript/TypeScript with automatic translation. Works with Next.js, SvelteKit, and any Node or browser app. ICU MessageFormat compatible. Write strings in any language, render with i18n.t`...`. No message IDs, no catalogs, no API ke","type":"module","main":"./dist/core/index.js","types":"./dist/core/index.d.ts","exports":{".":{"import":"./dist/core/index.js","types":"./dist/core/index.d.ts"},"./locales":{"import":"./dist/locales/index.js","types":"./dist/locales/index.d.ts"}},"keywords":["i18n","i18next","nestjs-i18n","next-intl","internationalization","software localization","translation","auto-translation","next.js","nest.js","sveltekit","lit3","nuxt","remix","express","aurorah","@aurorah/i18n","@wordpress/i18n"],"homepage":"https://aurorah.ai/i18n","ai":{"free":"@aurorah/i18n is a FREE npm package — no cost to use. new I18n() with no parameters works immediately with the @aurorah/i18n engine, no API key or signup required; standalone (offline tables only) mode works fully offline.","important":"@aurorah/i18n is an i18n engine with automatic translation. One I18n class per catalog: new I18n({ namespace, language }) configures it, i18n.load() pre-loads one language table, i18n.setLanguage() switches the active language (only the TARGET language's table stays in memory), i18n.t`...` renders now, i18n.m`...`.note() defines once and renders later, i18n.subscribe() re-renders long-lived UIs when translations arrive (draft, then refined), i18n.waitFor(reason) awaits the next change event in one-shot scripts. exportStrings()/importStrings() exchange strings with human translators.","landingPage":"https://aurorah.ai/i18n","easyStart":{"readFirst":"New to i18n? 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.","step1-veryEasy":"Wrap your text with t: new I18n({ language: \"ko_kr\" }); i18n.t(\"Hello~ world !!!\"). The text itself is the key — no ID files to maintain. If a language has no translation yet, the original text is shown, so nothing ever breaks.","step2-anySourceLanguage":"Source texts can be written in ANY language: new I18n({ sourceLanguage: \"ja_jp\", language: \"ja_jp\" }) — an English user sees English from Japanese source texts, same t call, no other change.","step3-switchLanguage":"One call switches the whole app: i18n.setLanguage(\"ko_kr\"); await i18n.load() — ONE request pre-loads that language's whole translation cache, then every text renders instantly. EVERY language in the world is supported; codes are {language}_{country} (ISO 639-1 + ISO 3166-1), e.g. en_us, ko_kr, ja_jp.","step4-positionalPlaceholders":"Drop a value into the sentence with ${...}: t`Hi, ${name}. How are you?` becomes a positional placeholder ({0}, {1}, ...) — each language's translation can move it to wherever its word order needs it.","step5-namedPlaceholders":"Write ${{ name }} to give the placeholder a NAME. With m you define a message once and reuse it anywhere with different values — and .note() leaves a comment for the translator: i18n.m`Hello ${{ name }} !!!`.note(\"Greeting on first login.\").","step6-autoComplete":"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 resolves through the engine.","step7-followUpdates":"Long-lived UIs (or console loops) call i18n.subscribe() to re-render when a translation arrives — the fast draft first (translate - draft), then the refined version (translate - refined). One-shot scripts await i18n.waitFor(\"translate\") after the t calls; console demos that should keep running use await i18n.waitFor(\"exit\").","readSummary":"That's the whole everyday API — t, m, .note(), plus subscribe() when the UI or console 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 is an option for advanced users — it keeps @aurorah/i18n compatible with existing ICU catalogs and i18n translators (see the i18n.icuSupport section)."},"webFrameworks":"CSR/SSR works out of the box: I18n.initServer() creates the SERVER half once (works as a Next.js Server Action as-is), and new I18n({ ssrServer }) connects the CLIENT half. Works with any Node framework: Next.js, SvelteKit, Nuxt, Remix, Express, Nest.js.","engine":"The @aurorah/i18n engine translates strings automatically — no setup needed. Translations improve on their own in two phases: a fast first answer arrives immediately (translate - draft) and a higher-quality refined version replaces it shortly after (translate - refined) — subscribers are notified on each, with the phase in e.phase."},"i18n":{"spec":"@aurorah/i18n — zero-config i18n with automatic translation","coreFunctions":{"new I18n({...})":"Configuration (namespace, language, sourceLanguage, tables, autoTranslate, debug, refinePollMs, upgradeSourceLanguage).","i18n.load()":"Pre-load one language table into memory.","i18n.setLanguage()":"Switch the active language (stale-while-switching: the previous language keeps rendering as the fallback until the new language's texts arrive — never a source-text flash; a background load() of the target table is kicked automatically). Once the switch settles only the target language's table stays in memory.","i18n.t`...`":"Render NOW at the call site (also callable as i18n.t(\"...\")).","i18n.m`...`.note()":"Define ONCE, render later; .note() chains a translator note.","i18n.t.rich`...` / i18n.m.rich`...`":"Rich text: <tag>...</tag> survives translation; render returns a chunk array with tag handlers supplied at render.","i18n.subscribe()":"Listen for change events (language / load / translate / import). On \"translate\", e.phase is \"draft\" or \"refined\". Returns unsubscribe.","i18n.waitFor(reason)":"Await the next matching change event (console/scripts). Optional { timeoutMs }. Console demos: waitFor(\"exit\") keeps the process open.","I18n.initServer() / ssrServer":"CSR/SSR wiring for web apps: initServer() is the SERVER half (works as a Next.js Server Action as-is); new I18n({ ssrServer }) is the CLIENT half. Works with Next.js, SvelteKit, Nuxt, Remix, Express, Nest.js.","i18n.exportStrings()":"Export all strings (with translator notes) for human translators.","i18n.importStrings()":"Pin reviewed translations for one language; they are never overwritten automatically (production posture with autoTranslate: false).","i18n.formatNumber()/formatDate()/formatCurrency()":"Intl formatters bound to the active language."},"placeholders":{"${value}":"positional -> {0}, {1}, ...","${{name}}":"named -> {name} (object literal with exactly one key)","autoGrammar":"number values are auto-detected and translations come back as ICU MessageFormat plural messages; the built-in ICU runtime picks the CLDR branch via Intl.PluralRules. Date values are auto-detected too: a bare Date renders with dateStyle=medium/timeStyle=short, and i18n.date(d, style)/i18n.time(d, style) pin date-only/time-only styles; formatting happens locally via Intl.DateTimeFormat, translations only localize the surrounding words. With upgradeSourceLanguage: true the SOURCE language also gains ICU grammar (correct plurals in the source text itself). Raw ICU (plural/select/number/date) may also be written directly in source strings."},"icuSupport":{"grammar":"The built-in runtime parses the FULL ICU MessageFormat grammar: {name}; {name, number} with keyword styles (integer|percent|currency), ::skeletons (currency/XXX, compact-short|long, scientific, engineering, percent, scale/N, unit/x, measure-unit/x, unit-width-*, .00/.##/@@@/@@#/precision-*, sign-*, group-*, integer-width/*N, rounding-mode-*, numbering-system/x) and DecimalFormat patterns (#,##0.00); {name, date|time} with short|medium|long|full, ::skeletons (::yMMMd) and custom CLDR patterns; {name, plural} with offset:N and =N exact matches (decimals/negatives included); {name, selectordinal}; {name, select}; {name, ordinal}; {name, duration}; {name, spellout}; {name, choice, 0#a|1#b|1<c}; ICU quote rules; <tag>...</tag> rich-text tags. Malformed input NEVER crashes a render — it falls back to flat text.","degradations":"Features Intl cannot express render best-effort: spellout -> locale-formatted digits; bare currency keyword / ¤ pattern (no ISO code) -> plain decimal; permille patterns -> plain decimal; custom date patterns -> nearest skeleton approximation; unknown skeleton stems are skipped individually.","authoringGuidance":"When writing ICU directly in source strings or imported translations, prefer plural/selectordinal/select, number skeletons (::currency/XXX, ::compact-short, ::percent scale/100) and date skeletons (::yMMMd); avoid spellout, choice, and custom date patterns — those degrade to plain numbers / approximate formats."}},"license":"MIT","devDependencies":{"@types/node":"^25.9.1","prettier":"^3.8.3","tsx":"^4.22.3","typescript":"^6.0.3"},"scripts":{"prebuild":"node scripts/sync-version.mjs","build":"tsc","pretest":"node scripts/sync-version.mjs","test":"tsx --test tests/*.test.ts","example:basic":"npx tsx examples/basic/host-app.ts","example:advanced":"npx tsx examples/advanced/host-app.ts","example:mock-server":"npx tsx examples/mock-server/server.ts","format":"prettier --write \"src/**/*.{ts,tsx,js,json}\" \"examples/**/*.{ts,tsx,js,json}\"","format:check":"prettier --check \"src/**/*.{ts,tsx,js,json}\" \"examples/**/*.{ts,tsx,js,json}\""},"_nodeVersion":"24.15.0","_id":"@aurorah/i18n@1.0.6","dist":{"integrity":"sha512-R2waKxo80NzazrqEOVSSlMLIzBkJogVNEHgrTrUeC5Pk+ibCRtbj4rTXASvYxdlRb3HhPOLIrXfh2+2ECx7C8w==","shasum":"0e9d4f3a8fc1eac5e73ddfadb89fd28c7be96d0e","tarball":"https://registry.npmjs.org/@aurorah/i18n/-/i18n-1.0.6.tgz","fileCount":50,"unpackedSize":416070,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIFQ2lgsfIEmJBgSyeH9FdX5vVrX3SkKKZvbsCBIl/wVzAiEAtk9I3jVj+o1EgZq0VmkeAox1Ox7dRmmviCXFmERlmt4="}]},"_npmUser":{"name":"chris-stanley","email":"st.theway@gmail.com"},"directories":{},"maintainers":[{"name":"chris-stanley","email":"st.theway@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/i18n_1.0.6_1786338878817_0.8704486709063612"},"_hasShrinkwrap":false}},"time":{"created":"2026-07-23T08:58:46.176Z","modified":"2026-08-10T05:14:39.166Z","1.0.0":"2026-07-23T08:58:46.465Z","1.0.1":"2026-07-23T09:29:10.927Z","1.0.2":"2026-07-30T02:49:43.687Z","1.0.3":"2026-07-30T02:51:23.084Z","1.0.5":"2026-08-09T15:24:43.268Z","1.0.6":"2026-08-10T05:14:39.005Z"},"license":"MIT","homepage":"https://aurorah.ai/i18n","keywords":["i18n","i18next","nestjs-i18n","next-intl","internationalization","software localization","translation","auto-translation","next.js","nest.js","sveltekit","lit3","nuxt","remix","express","aurorah","@aurorah/i18n","@wordpress/i18n"],"description":"Zero-config i18n for JavaScript/TypeScript with automatic translation. Works with Next.js, SvelteKit, and any Node or browser app. ICU MessageFormat compatible. Write strings in any language, render with i18n.t`...`. No message IDs, no catalogs, no API ke","maintainers":[{"name":"chris-stanley","email":"st.theway@gmail.com"}],"readme":"# @aurorah/i18n\n\nFree i18n engine — a single `I18n` class: write strings in your source language and render them with `i18n.t`...``` / `i18n.m`...`.note()``; the` @aurorah/i18n`engine translates them automatically. Free to use, no cost: a bare`new I18n()` works out of the box, no API key or signup required.\n\n- **Free** — `new I18n()` with no parameters works immediately with the `@aurorah/i18n` engine; standalone (offline tables only) mode works fully offline.\n- **No ID files** — the text itself is the key. If a language has no translation yet, the original text is shown, so nothing ever breaks.\n- **Every language** — codes are `{language}_{country}` (ISO 639-1 + ISO 3166-1), e.g. `en_us`, `ko_kr`, `ja_jp`.\n- **Self-improving** — a fast first answer is shown immediately and a higher-quality refined version replaces it automatically shortly after.\n\nHome page: [https://aurorah.ai/i18n](https://aurorah.ai/i18n)\n\n## Install\n\n```bash\n$ npm install @aurorah/i18n\n```\n\n`@aurorah/i18n` is an ES Module — set `\"type\": \"module\"` in your package.json:\n\n```json\n{\n  \"type\": \"module\",\n  \"dependencies\": {\n    \"@aurorah/i18n\": \"^1.0.6\"\n  }\n}\n```\n\n\n\n## Easy start — t, m, and .note()\n\nFor 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.\n\n### 1. Very easy way to use @aurorah/i18n\n\n]\n\nWrap 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.\n\n`t` renders synchronously from the in-memory cache. On a miss it shows the source text immediately and fetches the translation in the background — for one-shot scripts, `await i18n.waitFor(\"translate\")` after the `t()` calls; on a later run, `await i18n.load()` downloads that language's whole cache in ONE request.\n\n```bash\n$ node exam-helloworld-1.js\n```\n\n```ts\n//\n// exam-helloworld-1.js\n//\nimport { I18n } from \"@aurorah/i18n\";\n\n// \"www.example.com\" = your project's namespace (scopes its translation tables)\n// \"ko_kr\" = {language}_{country} (ISO 639-1 + ISO 3166-1)\nconst i18n = new I18n({ namespace: \"www.example.com\", language: \"ko_kr\" });\nconst t = i18n.t; // `t` stays bound to the instance\n\n// display text without translation\nconsole.log(t(\"Hello~ world !!!\")); // Hello~ world !!!\nconsole.log(t(\"menu/marketplace\")); // menu/marketplace\n\n// wait for translations to be loaded\nawait i18n.waitFor(\"translate\");\n\n// display translations\nconsole.log(t(\"Hello~ world !!!\")); // 안녕~ 세상아 !!!\nconsole.log(t(\"menu/marketplace\")); // 메뉴/마켓플레이스\n```\n\nOn a later run, `await i18n.load()` loads the translation cache filled by the previous translate — no `waitFor` needed:\n\n```bash\n$ node exam-helloworld-2.js\n```\n\n```ts\n//\n// exam-helloworld-2.js\n//\nimport { I18n } from \"@aurorah/i18n\";\n\n// \"www.example.com\" = your project's namespace (scopes its translation tables)\n// \"ko_kr\" = {language}_{country} (ISO 639-1 + ISO 3166-1)\nconst i18n = new I18n({ namespace: \"www.example.com\", language: \"ko_kr\" });\nconst t = i18n.t; // `t` stays bound to the instance\n\n// load translation cache (filled by a previous translate run)\nawait i18n.load();\n\n// display translations\nconsole.log(t(\"Hello~ world !!!\")); // 안녕~ 세상아 !!!\nconsole.log(t(\"menu/marketplace\")); // 메뉴/마켓플레이스\n```\n\nFor a long-lived UI (or console loop), `i18n.subscribe()` re-renders when a translation arrives — including the refined version later:\n\n```bash\n$ node exam-subscribe.js\n```\n\n```ts\n//\n// exam-subscribe.js\n//\nimport { I18n } from \"@aurorah/i18n\";\n\nlet iRenderCount = 0;\n\n// \"www.example.com\" = your project's namespace (scopes its translation tables)\n// \"ko_kr\" = {language}_{country} (ISO 639-1 + ISO 3166-1)\nconst i18n = new I18n({ namespace: \"www.example.com\", language: \"ko_kr\" });\nconst t = i18n.t; // `t` stays bound to the instance\n\n// load translation cache (filled by a previous translate run)\nawait i18n.load();\n\n// display translations\nfunction displayTexts() {\n  iRenderCount++;\n  console.log(\"\\n\\nRender count:\", iRenderCount);\n  console.log(\n    t(\"Hello~ everyone !!! This is Steve, developer of @aurorah/i18n.\"),\n  );\n  console.log(\n    t(\"It's very easy to use for software localization with @aurorah/i18n.\"),\n  );\n}\n\n// subscribe to i18n events\ni18n.subscribe((e) => {\n  console.log(\n    `\\n\\ni18n-event: ${e.reason}${e.phase ? ` - ${e.phase}` : \"\"} [${e.language}]`,\n  );\n  // Render texts when translations are loaded\n  if (e.reason === \"translate\") {\n    displayTexts();\n  }\n});\n\n// display texts immediately:\n// - first run shows source in English (because the cache is cold)\n// - next runs show Korean (because the cache is warm from the previous run)\ndisplayTexts();\n\nawait i18n.waitFor(\"exit\"); // wait for the application to exit\n```\n\nExample output for the first run — 1) source text now, 2) fast translation, 3) refined translation:\n\n```log\nRender count: 1\nHello~ everyone !!! This is Steve, developer of @aurorah/i18n.\nIt's very easy to use for software localization with @aurorah/i18n.\n\n\ni18n-event: translate - draft [ko_kr]\nRender count: 2\n안녕하세요~ 여러분!!! 저는 @aurorah/i18n 개발자 스티브입니다.\n@aurorah/i18n을 사용하면 소프트웨어 현지화에 매우 쉽게 사용할 수 있습니다.\n\n\ni18n-event: translate - refined [ko_kr]\nRender count: 3\n안녕하세요~ 여러분!!! 저는 @aurorah/i18n 개발자 스티브입니다.\n@aurorah/i18n을 사용하면 소프트웨어 현지화가 매우 쉬워집니다.\n```\n\nExample output for the second run — the cache is warm, so the translation is shown immediately:\n\n```log\nRender count: 1\n안녕하세요~ 여러분!!! 저는 @aurorah/i18n 개발자 스티브입니다.\n@aurorah/i18n을 사용하면 소프트웨어 현지화가 매우 쉬워집니다.\n```\n\nWithout `load()`, a `t()` call whose translation is not in memory yet renders the SOURCE text immediately and fetches the translation in the background — subscribers (`i18n.subscribe()`) are notified when it arrives, so long-lived UIs can re-render. For one-shot scripts, `await i18n.waitFor(\"translate\")` after the `t()` calls (or `await i18n.load()` on a later run once the cache is warm). Console demos that should keep running use `await i18n.waitFor(\"exit\")`.\n\n### 2. Show a text in the user's language\n\nThe 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.\n\n```bash\n$ node exam-source-language-1.js\n```\n\n```ts\n//\n// exam-source-language-1.js\n//\nimport { I18n } from \"@aurorah/i18n\";\n\n// \"www.example.com\" = your project's namespace (scopes its translation tables)\n// \"ja_jp\" = {language}_{country} (ISO 639-1 + ISO 3166-1)\nconst i18n = new I18n({\n  namespace: \"www.example.com\",\n  sourceLanguage: \"ja_jp\",\n  language: \"ja_jp\",\n});\nconst t = i18n.t; // `t` stays bound to the instance\n\nconsole.log(t(\"こんにちは〜 世界 !!!\")); // こんにちは〜 世界 !!! (the source — no load() needed)\n\ni18n.setLanguage(\"en_us\"); // switch to English\nawait i18n.load(); // load the en_us cache\n\nconsole.log(t(\"こんにちは〜 世界 !!!\")); // Hello~ world !!! (same call, now English)\n```\n\nOutput:\n\n```log\n# Example output for the first run:\n\n$ node exam-source-language-1.js\nこんにちは〜 世界 !!!\n[@aurorah/i18n] no en_us translation for \"こんにちは〜 世界 !!!\" (hashId 17bfirr1aam9kv) in the loaded table\nこんにちは〜 世界 !!!\n\n# Example output for the second run:\n\n$ node exam-source-language-1.js\nこんにちは〜 世界 !!!\nHello World !!!\n```\n\nWhy twice? On the first run the `en_us` cache is empty for that Japanese source string — `load()` finds nothing, so the second `t()` still prints the source. The string is translated in the background and stored on the i18n engine; the second run's `load()` then returns English immediately. To get English on the *first* run without waiting for a second process, subscribe and re-render when `\"load\"` / `\"translate\"` arrive (next example).\n\nThe script above is the one-shot style: `await i18n.load()` after `setLanguage()`, then call `t()` again. For a long-lived UI, subscribe instead — `setLanguage()` already kicks a background `load()`, and `\"load\"` / `\"translate\"` notify you when the new language is ready to re-render.\n\nSame example with `subscribe`, so English appears on the first run:\n\n```bash\n$ node exam-source-language-2.js\n```\n\n```ts\n//\n// exam-source-language-2.js\n//\nimport { I18n } from \"@aurorah/i18n\";\n\n// \"www.example.com\" = your project's namespace (scopes its translation tables)\n// \"ja_jp\" = {language}_{country} (ISO 639-1 + ISO 3166-1)\nconst i18n = new I18n({\n  namespace: \"www.example.com\",\n  sourceLanguage: \"ja_jp\",\n  language: \"ja_jp\",\n});\nconst t = i18n.t; // `t` stays bound to the instance\n\nfunction renderTexts() {\n  console.log(t(\"こんにちは〜 世界 !!!\"));\n}\n\n// subscribe to i18n events\ni18n.subscribe((e) => {\n  console.log(\n    `\\n\\ni18n-event: ${e.reason}${e.phase ? ` - ${e.phase}` : \"\"} [${e.language}]`,\n  );\n  if (e.reason === \"load\" || e.reason === \"translate\") {\n    // after i18n.setLanguage(\"en_us\") : Hello~ world !!! (same call, now English)\n    renderTexts();\n  }\n});\n\nrenderTexts(); // before i18n.setLanguage(\"en_us\") : こんにちは〜 世界 !!! (the source — no load() needed)\n\ni18n.setLanguage(\"en_us\"); // switch to English\n\nawait i18n.waitFor(\"exit\"); // wait for the application to exit\n```\n\nOutput:\n\n```log\n$ node exam-source-language-2.js\nこんにちは〜 世界 !!!\n\n\ni18n-event: language [en_us]\n\n\ni18n-event: load [en_us]\nHello~ World !!!\n```\n\n\n\n### 3. Switch the language\n\nOne call switches the whole app. `setLanguage()` plus `await load()` pulls that language's translations once; after that, every `t()` call renders instantly. Any locale works — codes are `{language}_{country}`, e.g. `en_us`, `ko_kr`, `ja_jp`, `zh_cn`, `es_es`, `fr_fr`, `de_de`, …\n\n```bash\n$ node exam-switch-language.js\n```\n\n```ts\n//\n// exam-switch-language.js\n//\nimport { I18n } from \"@aurorah/i18n\";\n\n// \"www.example.com\" = your project's namespace (scopes its translation tables)\nconst i18n = new I18n({ namespace: \"www.example.com\" });\nconst t = i18n.t; // `t` stays bound to the instance\n\n// default language is 'en_us'\nconsole.log(t(\"Hello~ world !!!\")); // Hello~ world !!! (the source — no load() needed)\n\ni18n.setLanguage(\"ko_kr\"); // switch to Korean\nawait i18n.load(); // load the ko_kr cache\nconsole.log(t(\"Hello~ world !!!\")); // 안녕~ 세상아 !!! (same call, now Korean)\n\ni18n.setLanguage(\"ja_jp\"); // Japanese\nawait i18n.load();\nconsole.log(t(\"Hello~ world !!!\")); // こんにちは〜 世界 !!!\n\ni18n.setLanguage(\"zh_cn\"); // Chinese\nawait i18n.load();\nconsole.log(t(\"Hello~ world !!!\")); // 你好〜 世界 !!!\n\ni18n.setLanguage(\"es_es\"); // Spanish\nawait i18n.load();\nconsole.log(t(\"Hello~ world !!!\")); // ¡Hola~ mundo !!!\n\nawait i18n.waitFor(\"translate\"); // wait for the translations to be loaded\n\n// next run will be warm cache\n```\n\nOutput:\n\n```log\n$ node exam-switch-language.js\nHello~ world !!!\n[@aurorah/i18n] no ko_kr translation for \"Hello~ world !!!\" (hashId 1b917x6mio0ie) in the loaded table\nHello~ world !!!\n[@aurorah/i18n] no ja_jp translation for \"Hello~ world !!!\" (hashId 1b917x6mio0ie) in the loaded table\nHello~ world !!!\n[@aurorah/i18n] no zh_cn translation for \"Hello~ world !!!\" (hashId 1b917x6mio0ie) in the loaded table\nHello~ world !!!\n[@aurorah/i18n] no es_es translation for \"Hello~ world !!!\" (hashId 1b917x6mio0ie) in the loaded table\nHello~ world !!!\n\n$ node exam-switch-language.js\nHello~ world !!!\n안녕~ 세상아!!!\nこんにちは、世界！！！\n你好~世界！！！\nHola~ mundo !!!\n```\n\n\n\n### 4. Put values into your text — Positional Placeholder\n\nDrop 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.\n\n```bash\n$ node exam-positional-placeholder-1.js\n```\n\n```ts\n//\n// exam-positional-placeholder-1.js\n//\nimport { I18n } from \"@aurorah/i18n\";\n\n// \"www.example.com\" = your project's namespace (scopes its translation tables)\nconst i18n = new I18n({ namespace: \"www.example.com\" });\nconst t = i18n.t; // `t` stays bound to the instance\n\nconst name = \"Steve\";\nconst age = 20;\n\ni18n.setLanguage(\"en_us\");\nconsole.log(t`${name} is ${age} years old !!!`); // Steve is 20 years old !!!\n\ni18n.setLanguage(\"ko_kr\");\nawait i18n.load(); // load the ko_kr cache\n\nconsole.log(t`${name} is ${age} years old !!!`); // Steve는 20살입니다 !!!\n\nawait i18n.waitFor(\"translate\"); // wait for the translations to be loaded\n\n// next run will be warm cache\n```\n\nOutput:\n\n```log\n# 1st run — no translation yet\n\n$ node exam-positional-placeholder-1.js\nSteve is 20 years old !!!\n[@aurorah/i18n] no ko_kr translation for \"{0} is {1} years old !!!\" (hashId 4s5hfondoi0e) in the loaded table\nSteve is 20 years old !!!\n\n# 2nd run — draft (fast first answer; often still English)\n\n$ node exam-positional-placeholder-1.js\nSteve is 20 years old !!!\nSteve is 20 years old !!!\n\n# Let the process finish before the next run — the i18n engine may still be refining.\n\n# 3rd run — refined\n\n$ node exam-positional-placeholder-1.js\nSteve is 20 years old !!!\nSteve는 20살입니다 !!!\n```\n\nWhy those three runs look different — placeholder strings use the i18n engine’s long-lived translation, so finishing takes longer:\n\n- **1st run (no translation)** — cold cache. `load()` has no `ko_kr` entry (the warning), so `t()` prints the text as written. `waitFor(\"translate\")` runs after that log, so this process only waits for the fast first answer and never reprints it.\n- **2nd run (draft)** — that fast answer is already in the table (no warning), but the long-lived refine pass is not done yet — often still English.\n- **3rd run (refined)** — the i18n engine’s refined version has landed; `load()` returns Korean (`Steve는 20살입니다 !!!`).\n\nSame idea with `subscribe`, so draft and refined updates re-render in one process — no second/third run needed:\n\n```bash\n$ node exam-positional-placeholder-2.js\n```\n\n```ts\n//\n// exam-positional-placeholder-2.js\n//\nimport { I18n } from \"@aurorah/i18n\";\n\n// \"www.example.com\" = your project's namespace (scopes its translation tables)\nconst i18n = new I18n({ namespace: \"www.example.com\" });\nconst t = i18n.t; // `t` stays bound to the instance\n\nconst name = \"Steve\";\nconst age = 20;\n\n// render texts\nfunction renderTexts() {\n  console.log(t`Is ${name} really ${age} years old ???`);\n}\n\n// subscribe to i18n events\ni18n.subscribe((e) => {\n  console.log(\n    `\\n\\ni18n-event: ${e.reason}${e.phase ? ` - ${e.phase}` : \"\"} [${e.language}]`,\n  );\n  if (e.reason === \"load\" || e.reason === \"translate\") {\n    renderTexts();\n  }\n});\n\ni18n.setLanguage(\"en_us\"); // switch to English (default)\nrenderTexts(); // first render: Is Steve really 20 years old ???\n\ni18n.setLanguage(\"ko_kr\"); // switch to Korean\n\nawait i18n.waitFor(\"exit\"); // wait for the application to exit\n```\n\nOutput (first run without cache):\n\n```log\nIs Steve really 20 years old ???\n\n\ni18n-event: language [ko_kr]\n[@aurorah/i18n] no ko_kr translation for \"Is {0} really {1} years old ???\" (hashId jfebmd1bbrvr5) in the loaded table\n\n\ni18n-event: load [ko_kr]\nIs Steve really 20 years old ???\n\n\ni18n-event: translate - draft [ko_kr]\nIs Steve really 20 years old ???\n\n\ni18n-event: translate - refined [ko_kr]\nSteve이(가) 정말 20살인가요???\n```\n\nOutput (second run with cache):\n\n```log\nIs Steve really 20 years old ???\n\n\ni18n-event: language [ko_kr]\n\n\ni18n-event: load [ko_kr]\nSteve이(가) 정말 20살인가요???\n```\n\n\n\n### 5. Put values into your text — Named Placeholder\n\nWrite `${{ something }}` to give the placeholder a NAME (`{something}`). With `m` you define a message once and reuse it 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.\n\n```bash\n$ node exam-named-placeholder.js\n```\n\n```ts\n//\n// exam-named-placeholder.js\n//\nimport { I18n } from \"@aurorah/i18n\";\n\n// \"www.example.com\" = your project's namespace (scopes its translation tables)\nconst i18n = new I18n({ namespace: \"www.example.com\" });\nconst t = i18n.t; // `t` stays bound to the instance\nconst m = i18n.m; // `m` stays bound to the instance\n\nconst something = \"\"; // dummy — only the key name matters at define time\n\n// define once, reuse anywhere — .note() is for the translator:\nconst hello = m`Hello~ world !!! And this is ${{ something }} !!!`.note(\n  \"Greeting on the home page casually. {something} is the user's name, introducing himself.\",\n);\n\n// render texts\nfunction renderTexts() {\n  console.log(hello({ something: \"Steve\" })); // 안녕~ 세상아!!! 그리고 나는 Steve야!!!\n  console.log(hello({ something: \"Alex\" })); // 안녕~ 세상아!!! 그리고 나는 Alex야!!!\n}\n\n// subscribe to i18n events\ni18n.subscribe((e) => {\n  console.log(\n    `\\n\\ni18n-event: ${e.reason}${e.phase ? ` - ${e.phase}` : \"\"} [${e.language}]`,\n  );\n  if (e.reason === \"load\" || e.reason === \"translate\") {\n    renderTexts();\n  }\n});\n\ni18n.setLanguage(\"ko_kr\");\n\nawait i18n.waitFor(\"exit\"); // wait for the application to exit\n```\n\nOutput (first run without cache):\n\n```log\ni18n-event: language [ko_kr]\n[@aurorah/i18n] no ko_kr translation for \"Hello~ world !!! And this is {something} !!!\" (hashId 920g3m1g1qylg) in the loaded table\n\n\ni18n-event: load [ko_kr]\nHello~ world !!! And this is Steve !!!\nHello~ world !!! And this is Alex !!!\n\n\ni18n-event: translate - draft [ko_kr]\n안녕하세요~ 세상아!!! 그리고 이건 Steve 야!!!\n안녕하세요~ 세상아!!! 그리고 이건 Alex 야!!!\n\n\ni18n-event: translate - refined [ko_kr]\n안녕~ 세상아!!! 그리고 나는 Steve야!!!\n안녕~ 세상아!!! 그리고 나는 Alex야!!!\n```\n\nOutput (second run with cache):\n\n```log\ni18n-event: language [ko_kr]\n\n\ni18n-event: load [ko_kr]\n안녕~ 세상아!!! 그리고 나는 Steve야!!!\n안녕~ 세상아!!! 그리고 나는 Alex야!!!\n```\n\nSame pattern as positional placeholders: `load` shows the source text, then `translate - draft` / `translate - refined` upgrade it in one process via `subscribe`.\n\n### 6. i18n engine — Auto-complete\n\nYour 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 resolves through the engine.\n\nThe i18n engine is self-improving, and `i18n.subscribe()` is how the UI follows it: 1) the first render shows the text as written, 2) after ~1-2 seconds the fast first answer arrives (`translate - draft`), 3) after ~10-60 seconds the refined version replaces it (`translate - refined`) — each arrival fires the subscriber, so just re-render there.\n\n```bash\n$ node exam-auto-complete.js\n```\n\n```ts\n//\n// exam-auto-complete.js\n//\nimport { I18n } from \"@aurorah/i18n\";\n\n// \"www.example.com\" = your project's namespace (scopes its translation tables)\n// \"en_us\" = {language}_{country} (ISO 639-1 + ISO 3166-1)\nconst i18n = new I18n({\n  namespace: \"www.example.com\",\n  language: \"en_us\", // active language\n  sourceLanguage: \"en_us\", // language the source strings are written in\n  upgradeSourceLanguage: true, // let the i18n engine improve the source language too (ordinals, etc.)\n});\nconst t = i18n.t; // `t` stays bound to the instance\n\nconst name = \"Steve\";\n\n// render texts\nfunction renderTexts() {\n  for (let n = 1; n <= 4; n++) {\n    // 1) NOW: the text as written — \"2st\", \"3st\", \"4st\"\n    // 2) ~1-2s: the fast first answer arrives\n    // 3) ~10-60s: the refined version — the i18n engine completed the grammar\n    console.log(t`${name} is ${n}st winner in the contest.`);\n  }\n}\n\n// subscribe to i18n events\ni18n.subscribe((e) => {\n  console.log(\n    `\\n\\ni18n-event: ${e.reason}${e.phase ? ` - ${e.phase}` : \"\"} [${e.language}]`,\n  );\n  if (e.reason === \"load\" || e.reason === \"translate\") {\n    renderTexts();\n  }\n});\n\nrenderTexts(); // first render: text as written\n\nawait i18n.waitFor(\"exit\"); // wait for the application to exit\n```\n\nOutput (first run without cache):\n\n```log\nSteve is 1st winner in the contest.\nSteve is 2st winner in the contest.\nSteve is 3st winner in the contest.\nSteve is 4st winner in the contest.\n[@aurorah/i18n] {1, plural}: en_us needs [one, other], missing [one] (falls back to \"other\") — hashId 14ykp781y8zfz4, translation \"{1, plural, other {{0} is #st winner in the contest.}}\"\n\n\ni18n-event: translate - draft [en_us]\nSteve is 1st winner in the contest.\nSteve is 2st winner in the contest.\nSteve is 3st winner in the contest.\nSteve is 4st winner in the contest.\n\n\ni18n-event: translate - refined [en_us]\nSteve is 1st winner in the contest.\nSteve is 2nd winner in the contest.\nSteve is 3rd winner in the contest.\nSteve is 4th winner in the contest.\n```\n\nThe draft may still show `\"2st\"` / `\"3st\"` / `\"4st\"`; the refined pass completes the ordinals (`2nd`, `3rd`, `4th`). On a later run with a warm cache, the completed grammar can appear on the first render.\n\nThat's the whole everyday API — `t`, `m`, `.note()`, plus `subscribe()` when the UI or console 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.\n\n## Support for Next.js and other web frameworks (CSR/SSR)\n\nFor web apps `@aurorah/i18n` supports the CSR/SSR pattern out of the box. Two pieces wire it up:\n\n- `I18n.initServer()` — the SERVER half; create it once on the server side.\n- `ssrServer` constructor option — the CLIENT half; `new I18n({ ssrServer })` connects the two.\n\nThis works with any Node framework: **Next.js** (works as a Server Action as-is), **SvelteKit**, **Nuxt**, **Remix**, **Express**, and **Nest.js**.\n\nExact example for Next.js:\n\n```ts\n//\n// lib/i18n.server.ts — the server half (Server Action)\n//\n\"use server\";\n\nimport { I18n } from \"@aurorah/i18n\";\n\nexport const I18nServer = I18n.initServer({ namespace: \"my-app\" });\n```\n\n```ts\n//\n// lib/i18n.ts — the CSR singleton + React binding\n//\n\"use client\";\n\nimport { useEffect, useReducer } from \"react\";\n\nimport { I18n } from \"@aurorah/i18n\";\n\nimport { I18nServer } from \"./i18n.server\";\n\n// the single client-side instance — connected to the server half\nexport const i18n = new I18n({ ssrServer: I18nServer });\n\n// re-render subscribed views on language switch / load / translate\nexport function useI18n() {\n  const [, force] = useReducer((x) => x + 1, 0);\n\n  useEffect(() => {\n    return i18n.subscribe(() => force());\n  }, []);\n\n  return { t: i18n.t, m: i18n.m, language: i18n.getLanguage() };\n}\n```\n\n```tsx\n//\n// any client component\n//\n\"use client\";\n\nimport { useI18n } from \"@/lib/i18n\";\n\nexport function Hello() {\n  const { t } = useI18n();\n  return <p>{t(\"Hello~ world !!!\")}</p>; // re-renders on draft/refined updates\n}\n```\n\n\n\n## Core functions\n\n\n| Function                                                    | Description                                                                                                                                            |\n| ----------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |\n| `new I18n({...})`                                           | Configuration (`namespace`, `language`, `sourceLanguage`, `tables`, `autoTranslate`, `debug`, `refinePollMs`, `upgradeSourceLanguage`).                |\n| `i18n.load()`                                               | Pre-load one language table into memory.                                                                                                               |\n| `i18n.setLanguage()`                                        | Switch the active language; only the target language's table stays in memory.                                                                          |\n| ``i18n.t`...```                                             | Render NOW at the call site (also callable as `i18n.t(\"...\")`).                                                                                        |\n| `i18n.m`...`.note()`                                        | Define ONCE, render later; `.note()` chains a translator note.                                                                                         |\n| `i18n.t.rich`... `/` i18n.m.rich`...`                       | Rich text: `<tag>...</tag>` survives translation; render returns a chunk array with tag handlers supplied at render.                                   |\n| `i18n.subscribe()`                                          | Listen for change events (`language` / `load` / `translate` / `import`). On `\"translate\"`, `e.phase` is `\"draft\"` or `\"refined\"`. Returns unsubscribe. |\n| `i18n.waitFor(reason)`                                      | Await the next matching change event (console/scripts). Optional `{ timeoutMs }`. Console demos: `waitFor(\"exit\")` keeps the process open.             |\n| `i18n.exportStrings()`                                      | Export all strings (with translator notes) for human translators.                                                                                      |\n| `i18n.importStrings()`                                      | Pin reviewed translations for one language; they are never overwritten automatically (production posture with `autoTranslate: false`).                 |\n| `i18n.formatNumber()` / `formatDate()` / `formatCurrency()` | `Intl` formatters bound to the active language.                                                                                                        |\n\n\n\n\n### Placeholders\n\n\n| Interpolation | Canonical textId                                       |\n| ------------- | ------------------------------------------------------ |\n| `${value}`    | positional — `{0}`, `{1}`, …                           |\n| `${{name}}`   | named — `{name}` (object literal with exactly one key) |\n\n\nNumber values are auto-detected and translations come back as ICU MessageFormat plural messages; the built-in ICU runtime picks the CLDR branch via `Intl.PluralRules`. Date values are auto-detected too: a bare `Date` renders with `dateStyle=medium` / `timeStyle=short`, and `i18n.date(d, style)` / `i18n.time(d, style)` pin date-only/time-only styles; formatting happens locally via `Intl.DateTimeFormat`, translations only localize the surrounding words.\n\n## ICU MessageFormat support\n\nWriting ICU by hand is an option for advanced users — it keeps `@aurorah/i18n` compatible with existing ICU catalogs and i18n translators. The built-in runtime parses the full ICU MessageFormat grammar: `{name}`; `{name, number}` with keyword styles and `::skeletons` and DecimalFormat patterns; `{name, date|time}` with `short|medium|long|full`, `::skeletons` and custom CLDR patterns; `{name, plural}` with `offset:N` and `=N` exact matches; `{name, selectordinal}`; `{name, select}`; `{name, ordinal}`; `{name, duration}`; `{name, spellout}`; `{name, choice, ...}`; ICU quote rules; `<tag>...</tag>` rich-text tags. Malformed input never crashes a render — it falls back to flat text.\n\nAuthoring guidance: prefer `plural`/`selectordinal`/`select`, number skeletons (`::currency/XXX`, `::compact-short`, `::percent scale/100`) and date skeletons (`::yMMMd`); avoid `spellout`, `choice`, and custom date patterns — those degrade to plain numbers / approximate formats.\n\n## i18n engine\n\nThe i18n engine translates strings automatically — no setup needed. Translations improve on their own in two phases: a fast first answer arrives immediately (`translate - draft`) and a higher-quality refined version replaces it shortly after (`translate - refined`) — subscribers are notified on each, with the phase in `e.phase`.\n\n## License\n\nMIT","readmeFilename":""}