Saltar al contenido

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ónTipoDescripción
localestringIdioma inicial; por defecto navigator.language en el navegador
fallbackstring | string[]Cadena de fallback tras el narrowing BCP-47 (es-MX → es)
messagesobjectIdioma → á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
loadersobjectCatá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
formattersobjectFormatters custom {v:name}
partialstring[]Idiomas cuyos mensajes llegaron con la página: no se pide nada para ellos, y un mensaje que la página no mostró carga el resto una vez en lugar de caer al fallback sin decir nada
onMissingfunction(key, locale): devuelve un string para sustituir; silencia el warn-once por defecto
onResolvefunction(info) en cada t(): key, locale, value y status (hit / fallback / miss). Alimenta los devtools

Instancia

MiembroDescripción
t(key, params?)Traduce. Las claves y los params se comprueban de tipo contra tus mensajes. Los datos malos nunca tumban un render, y tampoco pasan desapercibidos: un param que falta, un bloque de plural sin caso general, un hueco sin argumento, un valor que no es texto, un código de idioma que el navegador no sabe leer. Cada uno avisa una vez en la consola, nombrando el mensaje, la ruta o el código del que vino
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, options?)Mezcla mensajes en caliente: la primitiva de la carga diferida. Pasa { partial: true } cuando los mensajes son los de una sola página, para que el idioma siga incompleto y el siguiente mensaje que falte vaya a por el resto
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)
localeIdioma actual (readonly)
localesIdiomas cargados + cargables (readonly): alimenta un switcher desde una sola fuente de verdad
versionContador de cambios que impulsa los adapters de frameworks

Helpers de idioma

import { alternateLinks, inlineMessages, localeDirection, localeFromPath, localeName, localePath, negotiateLocale, persistLocale, resolveLocale, resolveRequestLocale, stripLocalePath, 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: url (when it carries the language) → cookie → header → fallback
resolveRequestLocale({ supported: ['en', 'es'], path, routing, 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?q=1#faq → /pt/docs?q=1#faq

// the route behind a localized url, for a router that keeps one tree
stripLocalePath({ supported: ['en', 'es'], path: '/es/docs' });   // → '/docs'

// this page's address in every language, for the head of a server-rendered page
alternateLinks({ supported, sourceLocale: 'en', path: '/docs', baseUrl });

// the messages `verbaly render` wrote into this page, when you asked it to
inlineMessages();   // → { 'home.title': 'El gate del build' } or undefined

localeFromPath y resolveLocale responden a dos preguntas distintas, y elegir la que no es el bug 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: primero el prefijo de la url, luego la elección guardada, luego navigator.languages con estrechamiento BCP-47, y luego tu fallback (path: false apaga la url). Úsalo para decidir a dónde mandar a alguien, nunca para decidir qué dice ya una página. persistLocale recuerda un cambio. Todos son seguros en SSR. Los dos primeros aceptan un base para un sitio servido desde una subcarpeta, y los de almacenamiento aceptan un storageKey propio (false apaga el almacenamiento). 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 y no distingue mayúsculas. Cada idioma encuentra el locale soportado más cercano: es-PE coincide con es, zh-HK con zh-Hant, y pt o pt-PT con pt-BR, pero nunca con uno de otra escritura (zh-HK nunca recibe zh-Hans). Funciona con cualquier servidor; las integraciones de SvelteKit, Nuxt y Next.js la usan por debajo.

resolveRequestLocale(options) toma toda la decisión por petición en una sola llamada. Dale el path y tu routing y decide la dirección: una página con prefijo es ese idioma para todo el mundo, y una sin prefijo es tu idioma fuente, así que una elección guardada no puede traducirla sin querer. Deja routing fuera y el orden es primero la cookie guardada, luego la cabecera Accept-Language, luego tu fallback. LOCALE_STORAGE_KEY exporta el nombre compartido (verbaly-locale) que usan tanto el almacenamiento del navegador como la cookie de SSR.

inlineMessages(root?) lee los mensajes que verbaly render escribió en la página cuando render.inlineCatalog está encendido, así que una página pre-traducida no necesita catálogo por red. Rara vez la llamas: el módulo generado ya lo hace, y pasa el resultado como partial para que un mensaje que la página no mostró se cargue igualmente cuando haga falta en vez de caer al fallback en silencio.

stripLocalePath(options) es la inversa de localePath: da la ruta que hay detrás de una dirección con idioma, así que un router puede casar un solo árbol en vez de uno por idioma. alternateLinks(options) devuelve la dirección de esta página en todos los idiomas más un x-default, lista para las etiquetas link rel="alternate" de una cabecera renderizada en servidor. Es el mismo conjunto que verbaly render escribe en un sitio estático, y está vacío cuando tus direcciones no llevan idioma, porque una sola dirección no puede decir honestamente que es todos los idiomas a la vez.

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

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

TypeScript parsea tus mensajes: FlatKeys aplana árboles anidados, ParamNames lee las apariciones de {param}, y TArgs exige los params exactamente cuando el mensaje los declara.

Con el compilador, los tipos generados rellenan Register, así que Translate es el t de tu proyecto y Locale la unión de tus idiomas: cada t que entrega un adaptador (useT(), getT()) y cada instancia del módulo generado comprueban claves y params contra tu catálogo. Una instancia que construyes desde un diccionario conserva los tipos de ese diccionario. Los tres son solo tipos, así que no cuestan nada en tiempo de ejecución.

Exports de bajo nivel

¿Construyes herramientas encima de Verbaly? El paquete exporta las mismas piezas que usa el compilador. parse(message) te da el AST del mensaje, y parseTags(text) con RICH_TAGS es el tokenizador de texto rico y su whitelist de etiquetas, que es lo que necesita un renderizador propio. flatten(tree) convierte un árbol anidado en claves con puntos. safeHref(href) y safeAttribute(name, value) son los guardas de url y de atributos, y normalizeLink(link) es el único normalizador de enlaces que comparten todos los adaptadores, junto con el tipo RichLink.

↑↓para moverteEnterpara abrir
Copiado en el portapapeles