Reference

Runtime API

The verbaly core is ~3 KB gzipped with zero dependencies, and everything builds on native Intl.

createVerbaly(options)

import { createVerbaly } from 'verbaly';

const v = createVerbaly({
  locale: 'es',
  fallback: ['en'],
  messages: { es: { home: { title: 'Inicio' } } },
});
OptionTypeDescription
localestringInitial locale; defaults to navigator.language in the browser
fallbackstring | string[]Fallback chain after BCP-47 narrowing (es-MXes)
messagesobjectLocale → tree; nested keys flatten to dots (home.title). An empty string counts as untranslated and keeps falling back, the same rule as check
loadersobjectLazy catalogs: locale → () => import('./es.json'), loaded on demand
formattersobjectCustom {v:name} formatters
onMissingfunction(key, locale): return a string to substitute; silences the default warn-once
onResolvefunction(info) on every t(): key, locale, value and status (hit / fallback / miss). Powers devtools

Instance

MemberDescription
t(key, params?)Translate; keys and params are type-checked from your messages
t`...`Tagged template: interpolates source text; the compiler upgrades it
t.id(key)`...`Readable-key opt-in: formats the inline source; the compiler rewrites it to a keyed call
setLocale(locale)Switch locale, notify subscribers, auto-loads a pending lazy catalog
loadLocale(locale)Load a lazy catalog (BCP-47 narrowing, deduped): await it before setLocale for a flash-free switch
addMessages(locale, tree)Merge messages at runtime: the lazy-loading primitive
subscribe(fn)Change listener; returns unsubscribe
has(key)Key exists in the current chain
inspect(key)Origin locale (from) + source text for a key (devtools/tooling). The field was renamed from locale in 0.17.0 (breaking) to match ResolveInfo.from
localeCurrent locale (readonly)
localesLoaded + loadable locales (readonly): feed a language switcher from one source of truth
versionChange counter that powers framework adapters

Locale helpers

import { negotiateLocale, persistLocale, resolveLocale, resolveRequestLocale, switchLocale } from 'verbaly';

resolveLocale({ supported: ['en', 'es', 'pt'] });  // storage → navigator → fallback
persistLocale('es');                             // localStorage + <html lang>

// server-side (0.16.0): match an Accept-Language header
negotiateLocale('es-PE,en;q=0.8', ['en', 'es']);   // → 'es'

// per-request (0.17.0): cookie value → header → fallback
resolveRequestLocale({ supported: ['en', 'es'], cookie, header });

// client switch for SSR setups (0.18.0): catalog → locale → cookie + <html lang>
await switchLocale(instance, 'es');

resolveLocale picks the initial locale (stored choice, then navigator.languages with BCP-47 narrowing, then fallback); persistLocale remembers a switch. Both are SSR-safe and accept a custom storageKey (false disables storage). See Plain HTML → Locale bootstrap for the full pattern.

negotiateLocale(header, supported, fallback?) (0.16.0) is the server-side counterpart: give it an Accept-Language header and your locales and it returns the best match (quality values respected, es-PE matches es, case-insensitive). It works with any server; the SvelteKit, Nuxt and Next.js integrations use it under the hood.

resolveRequestLocale(options) (0.17.0) makes the whole per-request decision in one call: a stored cookie value first, then the Accept-Language header, then your fallback. LOCALE_STORAGE_KEY exports the shared name (verbaly-locale) used by both the browser storage and the SSR cookie.

switchLocale(instance, locale, options?) (0.18.0) is the client-side switch the SSR integrations share: it loads the catalog first, then switches the locale, writes the verbaly-locale cookie and updates the lang attribute. Safe to call on the server (it does nothing there); @verbaly/sveltekit re-exports it.

Type-level safety

editor
v.t('home.title');            
v.t('home.title', { x: 1 });  ✗ no params declared
v.t('nope');                  ✗ unknown key

TypeScript parses your messages: FlatKeys flattens nested trees, ParamNames reads {param} occurrences, and TArgs makes params required exactly when the message declares them.

Low-level exports

Building tooling on top of Verbaly? The package also exports parse(message) (the message AST), flatten(tree) (nested tree → dot-keys), safeHref(href) (the unsafe-scheme guard behind named links) and, since 0.17.0, normalizeLink(link), the one link normalizer every adapter shares, plus the RichLink type. These are the same pieces the compiler uses.

Copied to clipboard