Referencia
API del runtime
El core de verbaly pesa ~3 KB gzip con cero dependencias, y todo se construye sobre Intl nativo.
createVerbaly(options)
import { createVerbaly } from 'verbaly';
const v = createVerbaly({
locale: 'es',
fallback: ['en'],
messages: { es: { home: { title: 'Inicio' } } },
});| Opción | Tipo | Descripción |
|---|---|---|
| locale | string | Idioma inicial; por defecto navigator.language en el navegador |
| fallback | string | string[] | Cadena de fallback tras el narrowing BCP-47 (es-MX → es) |
| messages | object | Idioma → árbol; las claves anidadas se aplanan con puntos (home.title). Un string vacío cuenta como sin traducir y sigue el fallback, la misma regla que check |
| loaders | object | Catálogos lazy: locale → () => import('./es.json'), cargados a demanda. El del locale con el que arrancas se pide de entrada, y la UI se actualiza cuando llega |
| formatters | object | Formatters custom {v:name} |
| onMissing | function | (key, locale): devuelve un string para sustituir; silencia el warn-once por defecto |
| onResolve | function | (info) en cada t(): key, locale, value y status (hit / fallback / miss). Alimenta los devtools |
Instancia
| Miembro | Descripción |
|---|---|
| t(key, params?) | Traduce; las keys y los params se comprueban contra tus mensajes. Los datos malos de catálogo nunca rompen el render y tampoco pasan de largo: un param que falta, un bloque plural sin caso comodín, un hueco al que le falta su argumento, un valor que ni siquiera es texto, cada uno avisa una vez en la consola nombrando el mensaje o la ruta de la que salió |
| t`...` | Tagged template: interpola texto fuente; el compilador lo mejora |
| t.id(key)`...` | Claves legibles opt-in: formatea el texto inline; el compilador lo reescribe a una llamada con clave |
| setLocale(locale) | Cambia el idioma y notifica a los suscriptores, auto-carga un catálogo lazy pendiente |
| loadLocale(locale) | Carga un catálogo lazy (narrowing BCP-47, deduplicado): haz await antes de setLocale para cambiar sin flash |
| addMessages(locale, tree) | Fusiona mensajes en runtime: la primitiva del lazy-loading |
| subscribe(fn) | Listener de cambios; devuelve unsubscribe |
| has(key) | La clave existe en la cadena actual |
| inspect(key) | Locale de origen (from) + texto fuente de una key (devtools/herramientas) |
| locale | Idioma actual (readonly) |
| locales | Idiomas cargados + cargables (readonly): alimenta un switcher desde una sola fuente de verdad |
| version | Contador de cambios que impulsa los adapters de frameworks |
Helpers de idioma
import { localeDirection, localeFromPath, localeName, localePath, negotiateLocale, persistLocale, resolveLocale, resolveRequestLocale, switchLocale } from 'verbaly';
// which language is this page? a fact of the url, undefined when it carries no prefix
localeFromPath({ supported: ['en', 'es', 'pt'] }) ?? 'en';
// which language does this visitor want? url prefix → storage → navigator → fallback
resolveLocale({ supported: ['en', 'es', 'pt'] });
persistLocale('es'); // localStorage + <html lang> + <html dir>
// server-side: match an Accept-Language header
negotiateLocale('es-PE,en;q=0.8', ['en', 'es']); // → 'es'
// per-request: cookie value → header → fallback
resolveRequestLocale({ supported: ['en', 'es'], cookie, header });
// client switch for SSR setups: catalog → locale → cookie + <html lang> + <html dir>
await switchLocale(instance, 'es');
// language switchers: names and direction from Intl, no tables
localeName('es'); // → 'español'
localeName('de', 'en'); // → 'German'
localeDirection('ar'); // → 'rtl'
// pre-rendered sites: the same page in another language, for a switcher that navigates
localePath('pt', { supported: ['en', 'es', 'pt'], sourceLocale: 'en' }); // /es/docs → /pt/docslocaleFromPath y resolveLocale responden dos preguntas distintas, y elegir la equivocada es el error clásico. localeFromPath(options) pregunta en qué idioma está esta página: lee el prefijo /{locale}/ y devuelve undefined cuando la url no lleva ninguno, así que en un sitio con un árbol por idioma escribes localeFromPath(...) ?? sourceLocale y nada puede contradecir a la url. resolveLocale(options) pregunta qué idioma quiere este visitante: prefijo de la url, luego la elección guardada, luego navigator.languages con narrowing BCP-47, luego fallback (path: false apaga la url). Úsala para decidir a dónde mandar a alguien, nunca para decidir qué dice ya una página. persistLocale recuerda un cambio. Las tres son seguras en SSR, las dos primeras aceptan un base para un sitio servido desde una subcarpeta, y las de almacenamiento aceptan un storageKey propio (false lo desactiva). Mira HTML plano → Bootstrap de idioma para el patrón completo.
negotiateLocale(header, supported, fallback?) es la contraparte del lado servidor: dale un header Accept-Language y tus locales y devuelve la mejor coincidencia (respeta los q-values, es-PE coincide con es, sin distinguir mayúsculas). Funciona con cualquier servidor; las integraciones de SvelteKit, Nuxt y Next.js la usan por debajo.
resolveRequestLocale(options) toma toda la decisión por request en una sola llamada: primero el valor de una cookie guardada, luego el header Accept-Language, luego tu fallback. LOCALE_STORAGE_KEY exporta el nombre compartido (verbaly-locale) que usan el storage del browser y la cookie SSR.
switchLocale(instance, locale, options?) es el cambio de idioma del lado cliente que comparten las integraciones SSR: carga el catálogo primero, luego cambia el locale, escribe la cookie verbaly-locale y actualiza los atributos lang y dir. Es seguro llamarlo en el servidor (ahí no hace nada); @verbaly/sveltekit lo re-exporta.
localeName(locale, displayIn?) y localeDirection(locale) alimentan un selector de idioma sin tablas hardcodeadas: nombres de idioma reales vía Intl.DisplayNames (por defecto el nombre en su propio idioma) y la dirección de escritura de cualquier locale. Los idiomas de derecha a izquierda no piden trabajo extra: switchLocale, persistLocale, las integraciones SSR y verbaly render ya mantienen el dir correcto por su cuenta.
Seguridad a nivel de tipos
v.t('home.title'); ✓
v.t('home.title', { x: 1 }); ✗ no params declared
v.t('nope'); ✗ unknown keyTypeScript parsea tus mensajes: FlatKeys aplana árboles anidados, ParamNames lee las apariciones de {param}, y TArgs exige los params exactamente cuando el mensaje los declara.
Exports de bajo nivel
¿Construyes herramientas sobre Verbaly? El paquete también exporta parse(message) (el AST del mensaje), parseTags(text) con RICH_TAGS (el tokenizador de texto enriquecido y su whitelist de tags, lo que necesita un renderer propio), flatten(tree) (árbol anidado → keys con puntos), safeHref(href) y safeAttribute(name, value) (los guardas de URL y de atributos), y normalizeLink(link), el único normalizador de enlaces que comparten todos los adapters, más el tipo RichLink. Son las mismas piezas que usa el compilador.