API
This page describes the public building blocks exported by @nano_kit/intl: context helpers, message formats, translation utilities, and common composition patterns.
Context
Section titled “Context”intl($locale, loader)
Section titled “intl($locale, loader)”Creates an internationalization context with a bound messages method.
import { intl } from '@nano_kit/intl'
const { messages, $loading, $error } = intl( $locale, loader)The loader can return full translation data or load namespaces on demand.
messages(namespace, scheme?)
Section titled “messages(namespace, scheme?)”Creates reactive messages for a namespace.
const [$t, $pending, $error] = messages('home', { title: text(), attendees: plural('count')})Basic Formats
Section titled “Basic Formats”raw(fallback?)
Section titled “raw(fallback?)”Returns the input value as-is. Use it for objects, arrays, dictionaries, or values that do not need formatting.
const [$t] = messages('home', { categories: raw<Record<string, string>>()})
$t().categories?.conferencetext(fallback?)
Section titled “text(fallback?)”Formats string messages and optional string fallbacks.
const [$t] = messages('layout', { title: text('Untitled')})
$t().title// string | 'Untitled'uppercase(format), lowercase(format), capitalize(format)
Section titled “uppercase(format), lowercase(format), capitalize(format)”Transforms text formatter output with the active locale.
const [$t] = messages('home', { title: capitalize(text())})
$t().title// 'Hello'Parameterized Messages
Section titled “Parameterized Messages”params(params, preformat?)
Section titled “params(params, preformat?)”Replaces {name} placeholders with formatted values.
const [$t] = messages('home', { greeting: params({ name: text() })})
$t().greeting({ name: 'Ada'})params can preformat another formatter before replacing placeholders. That makes it possible to share parameters across nested formats.
const translations = { home: { mailbox: { one: '{name} has {count} message', other: '{name} has {count} messages' } }}
const [$t] = messages('home', { mailbox: params({ name: text() }, plural('count', forms({ one: text(), other: text() })))})
$t().mailbox({ name: 'Ada', count: 2})// 'Ada has 2 messages'Callable Formatter Messages
Section titled “Callable Formatter Messages”format(type)
Section titled “format(type)”Use format() when the message should format an application value instead of formatting a value from translation data. It binds another format to the current locale and returns a callable message.
const [$t] = messages('home', { formatPrice: format(number({ style: 'currency', currency: 'USD' })), eventDate: format(capitalize(datetime({ dateStyle: 'medium', timeZone: 'UTC' })))})
$t().formatPrice(12)// '$12.00'
$t().eventDate(new Date('2024-01-02T00:00:00.000Z'))// 'Jan 2, 2024'This is useful for dates, numbers, and other UI values that come from app state.
Intl Formats
Section titled “Intl Formats”Intl formats can be used inside params(...) for translated templates, or wrapped with format(...) when the value comes from runtime state, API data, or a database.
number(fallback?, options?)
Section titled “number(fallback?, options?)”Formats numbers with Intl.NumberFormat.
const [$t] = messages('stats', { formatCount: format(number({ maximumFractionDigits: 1 }))})
$t().formatCount(1234.56)// '1,234.6'datetime(fallback?, options?)
Section titled “datetime(fallback?, options?)”Formats Date or timestamp values with Intl.DateTimeFormat.
const [$t] = messages('event', { formatDate: format(datetime({ dateStyle: 'medium', timeZone: 'UTC' }))})
$t().formatDate(new Date('2024-01-02T00:00:00.000Z'))// 'Jan 2, 2024'relativetime(fallback?, options?)
Section titled “relativetime(fallback?, options?)”Formats relative time values with Intl.RelativeTimeFormat.
const [$t] = messages('event', { formatStartsIn: format(relativetime({ unit: 'day', numeric: 'auto' }))})
$t().formatStartsIn(1)// 'tomorrow'duration(fallback?, options?)
Section titled “duration(fallback?, options?)”Formats duration objects with Intl.DurationFormat.
const [$t] = messages('media', { formatRuntime: format(duration({ style: 'short' }))})
$t().formatRuntime({ hours: 1, minutes: 30})// '1 hr, 30 min'list(fallback?, options?)
Section titled “list(fallback?, options?)”Formats string iterables with Intl.ListFormat.
const [$t] = messages('filters', { formatTags: format(list({ type: 'conjunction' }))})
$t().formatTags(['react', 'ssr', 'signals'])// 'react, ssr, and signals'range(format, fallback?, options?)
Section titled “range(format, fallback?, options?)”Formats ranges through an Intl formatter that supports formatRange.
const [$t] = messages('price', { formatAmount: format(range(number, { style: 'currency', currency: 'USD' }))})
$t().formatAmount([1, 5])// '$1.00 – $5.00'Match And Plural
Section titled “Match And Plural”plural(param, forms?)
Section titled “plural(param, forms?)”Matches an LDML plural form with Intl.PluralRules. If forms(...) is omitted, plural reads forms directly from the translation input.
const [$t] = messages('home', { attendees: plural('count')})
$t().attendees({ count: 3})// '3 going'
/* Also works with the value itself. */$t().attendees(3)// '3 going'Use forms(...) when nested forms need their own formatters.
const [$t] = messages('home', { attendees: plural('count', forms({ one: text('{count} attendee'), other: text('{count} attendees') }))})
$t().attendees({ count: 1})// '1 attendee'Exact numeric forms are checked before plural rules.
const [$t] = messages('home', { guests: plural('count', forms({ 0: text('No guests'), one: text('{count} guest'), other: text('{count} guests') }))})
$t().guests({ count: 0})// 'No guests'match(param, cases?)
Section titled “match(param, cases?)”Matches a case by parameter value. If cases(...) is omitted, match reads cases directly from the translation input.
const [$t] = messages('invite', { message: match('gender')})
$t().message({ gender: 'female'})// 'She invited female.'
/* Also works with the value itself. */$t().message('female')// 'She invited female.'Use cases(...) when cases need their own formatters.
const [$t] = messages('account', { status: match('status', cases({ active: text('Active {status}'), disabled: text('Disabled {status}') }))})
$t().status({ status: 'active'})// 'Active active'match can compose with plural when one case depends on another formatter.
const [$t] = messages('tasks', { message: plural('count', forms({ one: match('gender'), other: match('gender') }))})
$t().message({ gender: 'female', count: 3})// 'She has 3 tasks'match can also wrap plural when the translation shape starts with cases and nests forms inside them.
const [$t] = messages('tasks', { message: match('gender', cases({ male: plural('count', forms({ one: text('He has one task'), other: text('He has {count} tasks') })), female: plural('count', forms({ one: text('She has one task'), other: text('She has {count} tasks') })) }))})
$t().message({ gender: 'female', count: 3})// 'She has 3 tasks'Rich Text
Section titled “Rich Text”rich(tags) / rich(fallback, tags)
Section titled “rich(tags) / rich(fallback, tags)”Maps lightweight tags inside translated strings to rich chunks.
const [$t] = messages('docs', { message: rich('Read <link>the docs</link>', { link: chunks => <a href='/docs'>{chunks}</a> })})
$t().message// ['Read ', <a href='/docs'>the docs</a>]markup(tags) / markup(fallback, tags)
Section titled “markup(tags) / markup(fallback, tags)”Maps lightweight tags to strings.
const [$t] = messages('docs', { message: markup('Read <strong>the docs</strong>', { strong: chunks => `<strong>${chunks}</strong>` })})
$t().message// 'Read <strong>the docs</strong>'Utilities
Section titled “Utilities”deflat(input, depth?)
Section titled “deflat(input, depth?)”Turns dotted object keys into nested objects.
const translations = deflat({ 'layout.title': 'Event Board', 'home.title': 'Find your next frontend event', 'home.attendees.one': '{count} going', 'home.attendees.other': '{count} going'})direction($locale) and getDirection(locale)
Section titled “direction($locale) and getDirection(locale)”Use direction($locale) when the UI needs to react to left-to-right and right-to-left locales.
import { direction } from '@nano_kit/intl'
const $dir = direction($locale)
$dir()// 'ltr' or 'rtl'getDirection(locale) is available for non-reactive code.