Guías
Formato de mensajes
Una sintaxis pequeña, con Intl nativo por debajo: plurales, género y formato sin aprender ICU completo.
Interpolación
Los valores se formatean solos según su tipo: números con Intl.NumberFormat, fechas con Intl.DateTimeFormat, textos tal cual. Un número se lee como lo escribe el idioma, así que 2026 sale como 2,026 en inglés: pasa String(year) cuando el valor es una etiqueta, como el año de una línea de copyright, y no una cantidad. Un param que falta muestra el hueco literal y avisa una vez nombrando el mensaje, así que se ve rápido y nunca revienta.
Formatters
Agrega :formatter (con /arg opcional) a un param:
| Sintaxis | Resultado |
|---|---|
| {n:number} | Número del locale: 1,234.56 / 1234,56 |
| {n:integer} | Redondeado, sin decimales |
| {n:percent} | 0.5 → 50% |
| {n:currency/EUR} | Moneda con código ISO |
| {d:date/long} | dateStyle: short · medium · long · full |
| {d:time/short} | Variantes de timeStyle |
Los formatters custom se conectan al crear la instancia. La firma completa es (value, locale, arg?) => string, donde locale es el idioma activo y arg es lo que sigue al / en el placeholder:
Tiempo relativo, listas y unidades
El formato cubre el resto del Intl moderno, siempre con cero dependencias:
relative: pasa unDatey la unidad se elige sola contra ahora (Intl.RelativeTimeFormatconnumeric: 'auto', así obtienes ayer, no hace 1 día); pasa un número con unidad explícita (relative/hour).list: los arrays se localizan víaIntl.ListFormat, conjunción por defecto,/orpara disyunción,/unitpara listas de unidades. Cada elemento se auto-formatea por locale.unit: cualquier id de unidad CLDR (kilometer,megabyte,liter…) víaIntl.NumberFormat.- Los datos malos nunca tumban un render, y tampoco pasan de largo. Un formato desconocido, una unidad inválida, un hueco sin su argumento, una lista que no es una lista: cada uno avisa una vez, nombrando el mensaje, y cae a
String(value).
Plurales y selects: una sola sintaxis
- Los números matchean primero los valores exactos
=N, luego las categorías deIntl.PluralRules(zero one two few many other). - Los strings matchean su variante, con fallback a
other. #renderiza el número formateado al locale. Las variantes anidan placeholders libremente.- Incluye siempre
otheren un bloque plural. Es el caso comodín, así que un bloque sin él no renderiza nada para cualquier cantidad que no liste.verbaly checkfalla por eso, y además avisa cuando un idioma necesita formas que a tu mensaje le faltan: el polaco llega afewymanydonde el inglés solo necesitaoneyother. Si un catálogo llega a tu app sin pasar por el build, desde un loader lazy o un CMS, el runtime avisa en la consola nombrando el mensaje en vez de renderizar una cadena vacía en silencio. Un bloque de género o de rol es distinto: dejarotherfuera ahí es tu decisión, y Verbaly no lo toca.
Escape-hatch ICU
¿Tienes strings ICU MessageFormat existentes, o necesitas su sintaxis exacta? Escribe ICU y Verbaly lo detecta automáticamente y lo parsea al mismo motor, con cero dependencias extra.
- Auto-detectado por mensaje: sin config, sin marcador. Mensajes nativos e ICU conviven en el mismo catálogo.
- Soporta
plural,select,selectordinal(reglas ordinales reales),{n, number/date/time, style},#,=N, anidado y quoting con'…'. - Se comprueba como cualquier otro mensaje. Una traducción que aplanó un plural de ICU a texto plano, o que le quitó un parámetro, falla en
verbaly checkigual que uno nativo. El mismo guarda corre sobre lo que devuelve una traducción de máquina y sobre lo que importas. - Prefiere la sintaxis nativa por defecto; usa ICU solo cuando lo necesites.
- Solo lo pagas si lo usas. Verbaly lee tus catálogos al construir, y el parser de ICU entra en tu aplicación solo cuando alguno de tus mensajes lo necesita. Son unos 544 bytes, y no hay nada que encender. El tiempo relativo funciona igual: otros 318 bytes que solo viajan cuando un mensaje los pide.
Escapes
| Escribes | Obtienes |
|---|---|
| {{ | { literal |
| }} | } literal |
| || | | literal dentro de variantes |
| ## | # literal dentro de variantes |
En un catálogo, una { o una } literal siempre se escribe doble. El texto escrito en tu código lo trae de serie, porque extract las duplica por ti; un catálogo escrito a mano tiene que hacerlo él mismo, traducciones incluidas.
En mensajes rich (data-verbaly-rich o <Trans>) la entidad numérica { también muestra una llave literal: se decodifica después del parseo, así que nunca toca la sintaxis de placeholders.