Verbaly deja de traer esbuild a tu proyecto, lo que hacía fallar pnpm add por un script de build que nadie pidió. verbaly.lock se une solo después de que dos ramas traduzcan en paralelo, <br> funciona lo escribas como lo escribas, y un visitante recibe el idioma que lee aunque tu lista solo tenga una variante cercana. Breaking: Verbaly ahora necesita Node 22.12 o más reciente.
Verbaly deja de traer esbuild a tu proyecto, lo que hacía fallar pnpm add por un script de build que nadie pidió. verbaly.lock se une solo después de que dos ramas traduzcan en paralelo, <br> funciona lo escribas como lo escribas, y un visitante recibe el idioma que lee aunque tu lista solo tenga una variante cercana. Breaking: Verbaly ahora necesita Node 22.12 o más reciente.
Destacados
Instalar con pnpm funciona a la primera. Verbaly ya no trae esbuild, así que pnpm add @verbaly/vite deja de fallar por un script de build que nunca pediste, y la instalación pesa unos 12 MB menos. Un verbaly.config.ts sigue funcionando, y también sus imports escritos sin extensión.
Un merge ya no rompe verbaly.lock. Cuando dos ramas tradujeron en paralelo, Verbaly lee los dos lados del conflicto, conserva todos los borradores de cada lado, y el siguiente extract escribe el archivo entero. Añádelo al commit que resuelve el merge.
<br> funciona lo escribas como lo escribas.<br>, <br/> y <br /> son todos saltos de línea en los mensajes rich, en los archivos que mandas a traducir y en verbaly check. Antes, <br> desaparecía y <br /> salía como texto.
Cada visitante recibe el idioma que lee. Un navegador o una request en zh-HK ahora recibe tu chino tradicional, y pt o pt-PT recibe tu pt-BR, en vez de caer al inglés.
Builds más rápidos. El plugin de Vite y @verbaly/unplugin leen cada archivo una vez en lugar de dos.
Breaking: Node 22.12 o más reciente. Node 20 llegó al fin de su vida en abril de 2026, y los frameworks que Verbaly soporta ya piden Node 22.
Cambiado · 5
A TypeScript config loads with jiti, so esbuild leaves the install
Every package needs Node 22.12 or newer
verbaly.lock reads both sides of a merge conflict, and the next command that writes it writes both
resolveLocale and negotiateLocale match by likely script and region, then by a sibling in the same script
The Vite plugin and unplugin parse each file once per build
Corregido · 2
<br> and <br /> are line breaks in rich messages, in check and in the XLIFF export
The compiler README and the Agent Skill still named the old state file
Lo que Verbaly recuerda de tus traducciones (para qué texto fuente se hizo cada una y cuáles son borradores) sale de tu carpeta de catálogos hacia verbaly.lock, junto a tu config, y se muda solo. Una clave escrita con dos textos distintos ahora hace fallar verbaly check, porque uno de sus sitios muestra el texto del otro. Breaking: un proyecto con una clave así ve fallar check y doctor, y failOnMissing: true para el build por ella.
Destacados
Tu carpeta de catálogos tiene solo tus catálogos. Lo que Verbaly recuerda de tus traducciones ahora vive en verbaly.lock, junto a verbaly.config.ts. Trátalo como el lockfile de un gestor de paquetes: Verbaly lo escribe, tú lo committeas y nadie lo edita.
El archivo viejo se muda solo. El siguiente extract o arranque del servidor de dev mueve .verbaly-state.json a verbaly.lock y lo dice una vez. No hay nada que configurar.
Breaking: una clave con dos textos hace fallar verbaly check. Cuando t.id('title') se escribe dos veces con palabras distintas, un sitio muestra el texto del otro. Cada build ahora la lista junto a las traducciones rotas, y check se para en ella, nombrando los dos sitios.
Cosas pequeñas: el primer ejemplo del README muestra la clave que el compilador escribe de verdad, y el repositorio tiene un AGENTS.md para quien contribuye y sus agentes de código.
Cambiado · 3
The review state lives in verbaly.lock at the project root, and the old .verbaly-state.json moves into it on its own
The first command that writes verbaly.lock says so, in the CLI, both dev servers and the MCP server
A key written with two texts fails check, doctor and a build with failOnMissing: true
Corregido · 1
The README example showed a key the compiler never writes
Una traducción que falta o está rota, o una clave que ningún catálogo tiene, ahora aparece al final de cada build, y el build termina: lo que para una versión es verbaly check en tu CI. Para probar la entrada, pasamos Memos, una app de código abierto con 46 idiomas, de react-i18next a Verbaly y arreglamos todo lo que encontró. Breaking: el build ya no se para solo, y una clave escrita entre comillas invertidas que ningún catálogo tiene ahora hace fallar check.
Destacados
Un texto nunca para tu build. Cada build lista lo que falta o está roto, primero las traducciones rotas, y termina. El control es npx verbaly check, en tu CI. Breaking: para que el build siga fallando, pon failOnMissing: true en las opciones del plugin.
Probado en una migración real. Pasamos Memos (46 idiomas, 879 claves) de react-i18next a Verbaly: 20 archivos cambiados a mano, el mismo build y los mismos tests en verde, y unos 11 KB menos para cada visitante. init ahora conserva los catálogos que ya tienes, y migrate deja texto como {timestamp} como texto y nunca convierte un idioma dos veces.
Builds más rápidos con Vite. El plugin de Vite frenaba los builds grandes: Memos tardaba 37 segundos en compilar, y ahora tarda 17 (13 sin Verbaly).
Sin provider en el navegador. En una app de una sola página y en los tests de componentes, useT() funciona sin <VerbalyProvider>, el plugin de Vue ni provideVerbaly, como en react-i18next.
Menos errores de tipos al migrar. Una clave guardada en una variable tipada como un conjunto de claves pasa con sus parámetros, y los tipos generados ya no molestan a Biome ni a Prettier.
Cosas pequeñas:doctor dice "setup works, with 1 warning to read" en vez de "healthy" después de un aviso, y el servidor MCP vuelve a leer un config CommonJS. Breaking: una clave escrita entre comillas invertidas, o como {'…'} en JSX, se comprueba como una entre comillas.
Cambiado · 8
A missing, broken or unknown key is listed by every build, which finishes; failOnMissing: true stops it instead
A key written in backticks, as {'key'} in JSX or through a renamed t is checked like a quoted one
A variable typed as a union of keys passes, with its params left open
init keeps the catalogs a project already has and points the config at them
migrate doubles the braces i18next showed as text and remembers which languages it converted
useT() and useLocale() read the app instance when no provider is above them, in the browser
The generated verbaly.d.ts is left alone by Biome and Prettier
doctor counts the warnings instead of calling the setup healthy
Corregido · 2
The Vite plugin slowed large builds: its hooks now filter the modules they receive
A CommonJS verbaly.config.js stayed cached in long-lived processes like the MCP server
La 0.69.0 tipó cada t, y con ello rechazó cualquier clave guardada en un string cualquiera, así que el código que lee sus claves de datos, y cada migración desde otra librería, empezaba lleno de errores. Ahora una clave que escribes a mano se sigue comprobando contra tu catálogo, con sus params, y una clave que viene de datos compila tal cual. No hay nada que cambiar en tu código.
Destacados
Las claves que vienen de datos vuelven a compilar.t(item.key) y t(`status.${s}`) funcionan en cada t sin cast, mientras que una clave que escribes a mano se sigue comprobando contra tu catálogo, params incluidos, y tu editor la sigue completando.
La misma regla para una instancia que construyes tú. En createVerbaly({ messages }), una errata nombra ahora la clave que falló, y una clave que viene de datos también pasa ahí.
Cambiado · 1
A key held in a string, or a template with a part from data, passes in every t; a key written by hand is still checked, params included
Corregido · 1
A typo on an instance built from a dictionary names the key instead of the tagged template
Cada t de tu proyecto comprueba ahora sus claves contra tu catálogo, y los tipos generados quedan donde tu framework ya los lee. Esta versión arregla lo que una auditoría de la 0.68.0 encontró en lo primero que tocas: los tipos, el servidor de Next.js, las líneas de instalación y el cambio de idioma. Next.js gana además traducciones sin petición y texto enriquecido en Server Components. Breaking, solo en tipos: una clave guardada en un string cualquiera ya no compila.
Destacados
Claves tipadas en cada t.useT() en React, Vue y Svelte, getT() en Next.js y el t de cualquier instancia comprueban cada clave y sus params contra tu catálogo. Una errata hace fallar tsc, y el error nombra la clave. Breaking: una clave guardada en un string cualquiera ya no compila: declárala con defineKeys, o márcala as VerbalyKey.
Los tipos se escriben donde mira tu framework.src/verbaly.d.ts en apps de Vite y SvelteKit, .verbaly/types.d.ts en Next.js (Verbaly lo añade a tu tsconfig.json), y las carpetas que Astro y Nuxt reservan para esto. La CLI y el servidor de desarrollo escriben el mismo fichero, el viejo de la raíz se va solo, y npx verbaly typegen los escribe en CI.
Next.js: traducciones sin petición, y texto enriquecido en el servidor.getT({ locale: 'es' }) funciona en una imagen OG, un sitemap o un email y deja la ruta estática, y <Trans> se pinta en Server Components desde @verbaly/next/server.
El texto que Verbaly no ve ya no calla. Cuando arranca un build o un servidor de desarrollo, Verbaly nombra cada fichero fuera de tu include que escribe t`…`, como components/ en una app de Next.js sin src/, con el patrón que hay que añadir. doctor también los lista.
Cambiar de idioma conserva el resto de la dirección.switchLocale conserva el ?q= y el #anchor.
Detalles que antes molestaban:init y doctor imprimen la línea de instalación del README con tu propio gestor de paquetes, un catálogo con saltos de línea de Windows ya no se reescribe cuando nada cambió, y el servidor MCP ve un verbaly.config.mjs editado sin reiniciarse.
Añadido · 6
Register, Translate and Locale in verbaly: the types every t of your project reads
npx verbaly typegen writes the types alone, for CI before tsc
getT({ locale }) in Next.js, for an OG image, a sitemap or an email
A <Trans> for Server Components in @verbaly/next/server, and @verbaly/react/server for any React Server Component
Builds and dev servers name each file outside include that writes a t`…`, and doctor lists them
doctor checks the types where your framework reads them, and the tsconfig line in Next.js
Cambiado · 4
Breaking, in types: useT, getT and an instance t take only your keys, so a key held in a plain string needs defineKeys or as VerbalyKey
The types move to src/verbaly.d.ts, to .verbaly/types.d.ts in Next.js, or to Astro's and Nuxt's own folder, and the old root file is removed
Breaking for code that builds one: ResolvedConfig.dts is never undefined
init and doctor print the install line from the README, in your package manager
Corregido · 11
An instance from the generated module no longer takes any key
The types reach a Vite or SvelteKit app, whose tsconfig never read the project root
extract no longer declares the types twice in Astro and Nuxt
A typo in a key names the key instead of the tagged template
The <Trans> written with children compiles in TSX
The order of the generated types no longer depends on the machine
The MCP server sees an edited verbaly.config.mjs without a restart
A catalog with Windows line endings is no longer rewritten when nothing changed
The MCP catalog resource, pseudo and render read the text that ships
switchLocale keeps the query and the hash
A broken config no longer ends an unplugin build with a raw stack
La 0.67.0 prometió que nada cambia a tus espaldas. Una auditoría encontró los sitios donde eso todavía fallaba, y esta versión los cierra de raíz. Una traducción desactualizada se detecta la escriba quien la escriba, cada comando lee el texto que de verdad se publica, una clave muestra siempre las mismas palabras, nada del estado de revisión se pierde en silencio, y los servidores de desarrollo dicen lo importante una vez. Nada nuevo rompe tu build.
Destacados
Una traducción desactualizada se detecta la escriba quien la escriba.translate, import, Studio y el verbaly_write_drafts de un agente registran para qué texto fuente se escribió una traducción en el momento en que la escriben, e import lo toma del archivo del traductor. Un texto fuente que cambia antes del siguiente extract, o un archivo hecho para un texto anterior, queda marcado en vez de aceptarse en silencio.
Cada comando lee el texto que se publica.check --outdated, status, review, translate, export, import, Studio y el servidor MCP leen el texto de tu código allí donde el código es su dueño, así que el CI ya no deja pasar una traducción que el build publica junto a un texto más nuevo.
Una clave, las mismas palabras en todas partes. Cuando una clave tiene dos textos, gana el que se escribió primero por ruta de archivo, en extract, en cada build y en los dos servidores de desarrollo. Antes dependía del orden en que se leían los archivos, que cambiaba de una ejecución a otra.
Nada del estado de revisión se pierde en silencio.extract --locales es deja en paz los demás idiomas, el archivo de borradores viejo de un compañero se fusiona en vez de descartarse, y un archivo de estado ilegible detiene los comandos que escriben borradores antes de que escriban nada.
--prune conserva las traducciones que lee un t con otro nombre, también en archivos .vue, .svelte y .astro, y ahora el aviso aparece ahí.
Los servidores de desarrollo dicen lo importante, una vez. Vite dev nunca devuelve un texto viejo encima de un checkout, un catálogo guardado a medio escribir es una pausa de una línea en los dos servidores, y un aviso vuelve si su problema vuelve.
Añadido · 4
syncProject, the one path from your code to the catalogs, used by the CLI, the MCP server, Studio and the dev servers
review shows each translation next to its source text, and --approve takes keys to accept only those
verbaly_extract returns replaced texts, nameless values and escaped blocks, and status and missing report a broken state file
Vite dev reports outdated translations and a t under another name
Cambiado · 4
Breaking for code that reads it: syncCatalogs().replaced lists each key with the text it had, and added no longer counts a replaced text
extract prints one line per text that followed the code, with the text it had, instead of a warning
A t under another name counts only when it comes from Verbaly
Every line in a report comes from the parse, so it matches the code that was read
Corregido · 9
extract --locales no longer erases the review state of the other languages
The text that wins when one key has two texts no longer depends on file order
A translation is stamped when it is written, so a later source change is never missed
--prune keeps the translations read by a t under another name, in components too
Vite dev never writes an old text back after a checkout, and survives a broken catalog
check --outdated reads the text that ships, and lists outdated translations with the failures
A broken state file warns where it is only read, and stops writes before they happen
Dev warnings come back when their problem does, a few at a time
next typegen works under Yarn PnP, and a failed gate prints its report once
Verbaly deja de esconderte cosas. Una traducción escrita para un texto que ya cambió queda marcada, dos textos con la misma clave se reportan con los dos sitios, y dev y build muestran siempre las mismas palabras. En Next.js, next typegen ya no se detiene por traducciones que faltan, y los agentes pueden guardar sus propias traducciones como borradores. Nada nuevo rompe tu build.
Destacados
Las traducciones desactualizadas quedan marcadas. Cambia un texto fuente y las traducciones escritas para el anterior aparecen en status, check y doctor hasta que alguien las actualice o las mantenga con verbaly review --approve. check --outdated puede hacer que eso falle en tu CI.
Dev y build muestran siempre el mismo texto. Un texto escrito en tu código gana a una edición a mano del catálogo, en dev y en el build por igual, y Verbaly te dice dónde vive ese texto en lugar de reemplazar tu edición sin avisar.
Dos textos con la misma clave se reportan, con los dos sitios. Antes el segundo desaparecía sin aviso.
next typegen funciona aunque falten traducciones. El gate de Next.js ahora corre cuando el build ya compiló, no al cargar la config, y next experimental-analyze ya no necesita un next dev antes.
extract --prune también limpia los borradores, y los agentes guardan sus propias traducciones como borradores con la nueva herramienta MCP verbaly_write_drafts, así nadie edita a mano el archivo de borradores. Ese archivo ahora es .verbaly-state.json, y el antiguo se migra solo.
En React, useT() te da un t nuevo cuando cambia el idioma, así useMemo(…, [t]) lo sigue.
Añadido · 5
Outdated translations: status counts them, check warns, check --outdated fails, review lists them and review --approve keeps them
verbaly_write_drafts, the MCP tool an agent saves its own translations through, as drafts
import --draft keeps what a file brings as drafts
extract and doctor name a key written with two texts, a t under another name and a value a translator would see as {_0}
verbaly_extract, verbaly_missing and verbaly_status return the new findings as data
Cambiado · 4
The drafts file is now .verbaly-state.json, shared with the fingerprints, and the old one moves over on its own
Breaking for code that imports it: DRAFTS_FILE is now STATE_FILE in @verbaly/compiler
A text written in your code wins over the catalog in the build too, and check validates translations against it
The Next.js gate runs after the compile
Corregido · 8
extract --prune takes the drafts of the keys it removes
Two t.id texts under one key are reported with both places
A translation older than its source text is no longer silent
Dev and build no longer show different text after a hand edit of a text the code owns
next typegen no longer fails on missing translations
next experimental-analyze works without a previous next dev
useT() in React returns a new t when the language changes
A t under another name is reported instead of skipped in silence
Un archivo con un error ya no deja que extract --prune borre traducciones, las claves con nombres como toString o constructor ya no tumban el gate, y la traducción automática funciona con cualquier modelo actual de Claude, con Sonnet 5.5 por defecto. Next.js deja de descargar un catálogo dos veces, y tu build ahora dice en una línea cuándo check tiene advertencias. Nada nuevo hace fallar tu build.
Destacados
extract --prune ya no borra una traducción porque un archivo tenga un error. Mientras algún archivo fuente no se pueda leer, prune espera y nombra el archivo. Antes, cada clave que solo usaba ese archivo perdía su traducción en todos los idiomas.
Las claves con nombres como toString o constructor ya no pueden tumbar check, doctor ni tu build. Una traducción que llevaba una paraba el gate con un error sobre matchAll. Ahora es una clave como cualquier otra.
La traducción automática usa Claude Sonnet 5.5 por defecto, y funciona con cualquier modelo actual de Claude. Elegir Sonnet 5.5 u Opus 5.5 con --model hacía fallar cada batch. Un batch que el modelo rechaza ahora pasa a un modelo de respaldo en vez de volver sin traducir.
Next.js descarga cada catálogo una sola vez. La página ya no vuelve a pedir el catálogo que el servidor ya mandó, y el build solo reescribe los archivos que lee verbaly extract.
Tu build ahora avisa cuando check tiene advertencias, en una sola línea, y sigue pasando.
El árabe o el hebreo escritos como ar_EG o he_IL se leen de derecha a izquierda, y un almacenamiento del navegador lleno ya no bloquea un cambio de idioma.
Añadido · 4
One line in the build when check has warnings
doctor warns on routing prefix-all next to a render section, and on a catalog file that is not an object of messages
A batch the model declines goes to a fallback model, and a decline names its category
verbaly_extract returns the files it could not read
Cambiado · 3
The default translation model is claude-sonnet-5-5, at the same price
writeDts follows the dts option of your config when no file is named
Supported: SvelteKit 2 and 3, Preact 10 and 11
Corregido · 10
extract --prune no longer deletes translations while a source file does not parse
Keys named like toString, constructor or __proto__ no longer crash check, doctor or the build
doctor reads a catalog with a byte order mark the way the build does
A catalog file that is JSON but not an object is reported instead of overwritten
The Claude provider works with Sonnet 5.5, Opus 5.5 and Fable 5.1
The Next.js client no longer downloads the catalog the server already sent
The Next.js loader only rewrites the files verbaly extract reads, and dts: false writes no types file
A directory the watcher cannot open no longer stops next dev or extract --watch
Arabic and Hebrew written with an underscore read right to left
A full browser storage no longer blocks a language switch
La traducción automática ya no pisa un mensaje que arreglaste mientras corría, los nombres que los objetos de JavaScript ya traen no pueden romper una página, y un idioma escrito como pt_BR deja de romper números y fechas. Tres problemas que pasaban desapercibidos ahora traen un aviso, y ninguno hace fallar tu build. Si usas mensajes ICU con TypeScript, sus parámetros ahora tienen tipo, así que una llamada que los omitía aparece como error de tipos.
Destacados
La traducción automática nunca pisa tu trabajo. Si arreglas un mensaje mientras verbaly translate, Studio o un agente están traduciendo, tu texto se queda, y la ejecución te dice cuántos respetó.
Los nombres que los objetos de JavaScript ya tienen ya no pueden romper una página. Un idioma, un formato, una etiqueta rich o un grupo del catálogo llamado constructor, toString o __proto__ podía tumbar un render o convertirse en un enlace vacío. Ahora es un nombre como cualquier otro.
Un idioma escrito como pt_BR ya no rompe números, fechas y plurales. Lo leen como pt-BR y lo dicen una vez. doctor lo señala, init lo escribe bien, e import llena tu pt-BR desde un archivo pt_BR de gettext.
Tres problemas silenciosos ahora traen un aviso, y ninguno hace fallar tu build: una clave que solo tiene una traducción (Studio también la muestra en su resumen), un nombre de idioma que el navegador no sabe leer, y una app de Next.js cuyas URLs llevan el idioma mientras su config dice que no.
En Next.js, cambiar de idioma sigue a tus URLs. Con routing: 'prefix-all' en tu config, useSwitchLocale() va a la misma página en el idioma nuevo, sin opciones que pasar.
Vigilar cambios en Linux pesa mucho menos.next dev y verbaly extract --watch dejan de recorrer node_modules, que podía agotar el límite de archivos vigilados del sistema antes de llegar a tu código.
Añadido · 4
A warning for a key that only one translation has, in check, doctor, the MCP server and Studio's overview
doctor names a language code the browser cannot read, and init writes a typed pt_BR as pt-BR
import reads a file's pt_BR as your pt-BR, and starts a new language under its proper code
useSwitchLocale follows the routing in your config, and setRequestLocale warns when that routing contradicts a [locale] segment
Cambiado · 2
Two runtime warnings got shorter, and their explanation moved to the troubleshooting page
The runtime is 3.31 KB, and a site with no framework ships 6.09 KB
Corregido · 10
A translate run no longer overwrites a message someone saved while it ran
Names like constructor, toString or __proto__ in languages, formats, loaders, rich tags and catalog groups
A language code Intl cannot read no longer throws when formatting numbers, dates or plurals
The tagged template no longer throws on a value that cannot become text
switchLocale without supported builds the right URL when the language is in the URL
A page's own catalog can no longer swallow the rest of the page
extract --prune no longer deletes the translations of keys written in backticks
ICU messages get their parameter types
A batch the model declined is reported as declined, and not retried
next dev and extract --watch no longer walk node_modules on Linux
Un parámetro que JavaScript se niega a convertir en texto hacía que Verbaly lanzara, y el camino que debía absorberlo lanzaba también. Ahora el mensaje se pinta sin él y Verbaly lo dice una vez.
Destacados
Un parámetro malo ya no puede tumbar un render. Si un valor no se puede convertir en texto, el mensaje se pinta sin él y Verbaly lo avisa una vez, igual que hace con cualquier otro valor malo.
Esa forma te la da JSON gratis. Un payload con un campo llamado toString que contiene un objeto es JSON válido, así que un CMS o un catálogo cargado en caliente podía traerlo hasta tu página.
Nada cambia en un proyecto que estaba bien. La comprobación solo corre donde un parámetro ya era inservible.
Corregido · 1
A parameter that cannot be turned into text no longer throws, in every message shape: plain interpolation, the numeric formats, date, time, list, relative time and plurals
Cada fila de mensaje en Studio tiene ahora su menú. Copia la clave, copia el texto original, edita ahí mismo, mira qué ficheros usan un mensaje, y mira el commit que lo cambió por última vez, que es lo único que el panel nunca había sabido contestar.
Destacados
Cada fila de mensaje tiene su menú. Copia su clave, copia el texto original o empieza a editar, sin salir de la tabla.
Mira el commit que cambió un mensaje por última vez. Studio lee el historial de tus catálogos y te dice el commit, quién lo escribió y qué decía. Al pulsarlo se copia el sha corto, así que un git show en tu terminal queda a un pegado.
Mira qué ficheros usan un mensaje, desde la propia fila. Cuando es más de uno te dice cuántos, y se despliega en la lista entera.
Cuando no hay respuesta, el menú dice de qué tipo es ese no. Un mensaje que aún no has commiteado, uno más viejo que el historial que Studio leyó, y un proyecto sin git son tres cosas distintas, y se leen como tres cosas distintas.
En el menú no hay ningún botón que no haga nada. La entrada de IA que pide el diseño no está, porque el asistente al que llamaría todavía no existe.
Añadido · 2
A menu on every message row: edit here, copy the key, copy the source text, the commit that changed it, and where it is used
An address that answers which commit last changed one message, or why there is none
Cambiado · 1
Where it is used tells a config that turned scanning off apart from a key no file in your code mentions
Marca un solo idioma y Studio deja de comparar. Te dice que el trabajo ya salió y que nada está roto, y pone arriba los pocos que merecen una lectura, con el motivo escrito al lado de cada uno.
Destacados
Marcas un idioma y tienes ese idioma. Una barra sobre la tabla dice cuántos mensajes tiene, que ya salen en tu web, y que la comprobación que corre tu build pasa. La tranquilidad va primero porque es la parte cierta.
Cada fila que merece un vistazo dice por qué, con palabras, justo al lado. No que cambió un enlace, sino cuál. No que se movieron los números, sino que el fuente dice 20 y este dice 22.
Das por bueno un idioma entero con un clic, y se deshace. No hay diálogo de confirmación, porque sigues editando en la fila y el deshacer te espera en el raíl.
Un idioma en el raíl enseña todo lo que queda, no solo lo peor. Antes leías una rompe tu web para un idioma al que además le faltaba una, así que la segunda no se veía.
Añadido · 2
A view for a single language: its own bar, three filter chips and a reason beside each flagged row
A nothing odd filter, the exact opposite of worth a look, so the two together are the whole language
Cambiado · 2
A reason names its evidence: the language and both renderings, the sibling key, the number or the link that moved
A language is called by its own name in the rail and in the bar, with its tag kept beside it
Tres comprobaciones que no eran tan estrictas como parecían
Una revisión de la propia red de seguridad de Verbaly encontró tres cosas que prometía y no cumplía del todo: una comprobación de URL que el navegador se salta sin enterarse, una búsqueda de mensaje que podía responder con algo que no es un mensaje, y formatos de número que imprimen NaN sin decir nada. Las tres están cerradas.
Destacados
Un enlace que viene de tu catálogo ya no puede colar una URL javascript: en tu página. La comprobación leía el texto tal cual, y el navegador borra tabulaciones y saltos de línea de una URL antes de leerla, así que un enlace podía pasar. Ahora se comprueba como la lee el navegador.
Un mensaje que se llama toString o constructor falta, no está vacío. Esos nombres son de cualquier objeto de JavaScript, así que la búsqueda encontraba el del objeto en vez del tuyo, y te devolvía una cadena vacía sin decir nada.
Un formato de número al que le llega algo que no es un número lo dice. Un precio con un valor de texto pintaba NaN, y un precio sin valor pintaba un 0,00 con toda confianza. Los dos enseñan ahora el valor tal cual y nombran el mensaje del que salieron.
Nada cambia en cómo escribes tus mensajes. Todo esto solo corre cuando algo ya va mal, así que un proyecto que estaba bien se queda exactamente igual.
Cambiado · 2
The blocked-href warning names the scheme instead of the URL, so its dedupe set stays bounded
A malformed data-verbaly-attr names itself in the warning instead of naming data-verbaly-args
Corregido · 4
safeHref checks the URL the way a browser reads it: tab, line feed and carriage return removed from anywhere, leading control characters trimmed
flatten hands back a null-prototype map, and addMessages no longer undoes it
The five numeric formats validate their argument and degrade with a warning instead of rendering NaN
An auto-formatted NaN warns too, which covers the number inside a plural
El buscador solo miraba los idiomas que tenías marcados, así que una palabra española con español sin marcar no encontraba nada y nunca decía por qué. Y los grupos que tus claves ya declaran no estaban en ninguna pantalla. Los dos están arreglados, y funcionan juntos.
Destacados
El buscador encuentra una palabra en cualquier idioma, no solo en los que marcaste. Antes el campo filtraba la tabla, así que solo veía lo que la tabla enseñaba. Ahora lee tu catálogo entero.
Cada resultado dice en qué idioma lo encontró. Eso es justo lo que una tabla filtrada no puede decirte: la misma palabra suele existir en uno solo de tus idiomas.
Command K lo abre, las flechas mueven, enter te lleva al mensaje. Baja hasta la fila esté donde esté, aunque quede muy por debajo de lo que la pantalla había cargado.
Tus claves ya tenían grupos, y ahora el panel los enseña. Eliges uno en el raíl y la tabla, sus cuentas y los comandos se estrechan a él. Un resultado de búsqueda dice en qué grupo vive.
Añadido · 3
Groups: the first segment of a key, as a filter in the rail and a label on a search result
A search sheet that reads every language and marks what you typed
Keyboard throughout: Command K to focus, arrows to move, enter to open, esc to close
Cambiado · 1
The box in the bar opens the sheet instead of filtering the table
Antes abría en la tabla, sin decirte cuáles de tus mensajes estaban esperando ni si tu proyecto estaba sano. Ahora la primera pantalla te lo dice, y lo último que enseña es real: los commits que tocaron tus catálogos.
Destacados
Studio abre en un resumen, no en una tabla. Lo que te está esperando, una línea cada cosa con su propio botón, y si el proyecto está sano. Pulsar una te lleva exactamente a esos mensajes.
Los últimos cambios de tus catálogos, desde git. Commits de verdad sobre tu directorio de catálogos, con quién y cuándo. Un proyecto sin git simplemente no enseña la tarjeta, en vez de enseñarla vacía.
Una clave que tu código llama y que ningún catálogo define por fin se ve. Ya hacía fallar tu build, y hasta ahora el único sitio donde podías verla era la línea de comandos.
Añadido · 3
An overview as the panel's first screen, with what is waiting for you and the project's health
The recent commits over your catalog directory, read from git
A rail you can navigate: Overview, Messages and Health
Tu catálogo conserva el orden en el que lo escribiste
Dos sitios donde Verbaly hacía en silencio algo distinto de lo que decía. Reescribía tu catálogo en orden alfabético sin mencionarlo, y te decía que una traducción estaba comprobada sin haber mirado nunca dentro de un mensaje ICU. Los dos están arreglados en su origen.
Destacados
Tu catálogo conserva el orden en el que lo escribiste. Todo lo que escribía un catálogo reordenaba el fichero entero alfabéticamente, así que el primer guardado convertía un fichero ordenado a mano en un diff de todas sus líneas. En el catálogo de este mismo sitio eran 1817 de 1823 líneas, y ahora son cero.
Las comprobaciones por fin miran dentro de un mensaje ICU. Una traducción que aplanaba un plural ICU a texto plano, o que le quitaba un parámetro, salía limpia. Ahora se caza, en verbaly check, en lo que puede devolver una traducción de máquina y en lo que puede traer un fichero importado.
El preview en vivo de Studio dice lo mismo que dice el build, ICU incluido. Antes se ponía verde sobre un mensaje que el servidor luego rechazaba.
Corregido · 4
A catalog is written back in the order its file already had, deciding level by level
verbaly migrate stops reformatting the very file it is migrating
The checks read an ICU message with the ICU parser, so its parameters and plurals are compared
verbaly check no longer tells you to pass an icu option to createVerbaly
Hace tres versiones Studio era una sola cosa que ejecutas. Luego ganó tres puntos de entrada, un peer opcional y un capítulo de su README sobre pintar el panel a mano, y todo eso existía para que una página de este sitio pudiera importarlo. Ya no está, y lo que instalas vuelve a ser un comando.
Destacados
Instalas @verbaly/studio y no hay nada que importar. Un comando, ningún peer que añadir, y ningún .svelte que tenga que compilar tu bundler. El paquete vuelve a tener una sola entrada.
Ruptura: se quitan tres puntos de entrada, y svelte deja de ser una dependencia peer. Salieron en la 0.56.0 y tenían un solo consumidor en el mundo, la página de demo de este sitio.
Nada cambió en cómo se ejecuta Studio.npx verbaly-studio abre la misma pantalla, sobre los mismos ficheros, con los mismos comandos.
Corregido · 2
The panel was see-through inside another page, so you read your own site through it
The README and the API quoted a catalog size that ages with every release
Studio abría sobre un proyecto todavía sin configurar y te decía que marcaras un idioma que no existía. Ahora cada pantalla vacía y cada botón apagado dice lo que de verdad pasa, que es la parte de levantarlo que una persona realmente lee.
Destacados
La tabla vacía dejó de pedir lo imposible. Con un solo idioma en tu config no hay nada que marcar, así que te dice que tienes un idioma y dónde añadir el siguiente.
Cuatro pantallas vacías, una frase cada una: sin catálogos, un solo idioma, ninguno marcado, y un filtro que no encontró nada. Cada una nombra un hecho, no un botón.
Un comando que no puede correr está apagado, y dice por qué. Con el escaneo de código desactivado en tu config, Buscar texto nuevo ya no espera a que lo pulses para devolverte un error.
Corregido · 3
The empty table told you to tick a language when there was none to tick
Find new text was offered on projects whose config turns scanning off
Translate what is missing gave the wrong reason when a project has one language
A Studio se le entrega aquello con lo que habla en vez de ir a buscarlo, y eso es lo que convierte el panel de una página que sirve el comando en algo que puedes poner en la tuya. Y por fin abre en oscuro en una máquina oscura.
Destacados
El panel abre en el tema de tu máquina. Tenía paleta oscura y ninguna forma de llegar a ella, así que npx verbaly-studio abría en claro en un escritorio oscuro.
Ya nada dentro del panel sabe de la red. Se le entrega a quién llamar, así que los mismos componentes corren sobre un servidor de verdad o sobre cualquier otra cosa que conteste igual.
El panel se puede pintar en tu propia página, si tu página compila Svelte 5: los componentes de verdad, sobre cualquier cosa que responda como responde el servidor.
Todo lo que pinta vive en una sola clase. Sus colores y su modelo de caja ya no pueden llegar a tu página, que es lo que hizo posible meterlo dentro de otro sitio.
Instalarlo no cambió en nada.npx verbaly-studio no se trae Svelte ni pide nada nuevo: el panel que sirve ya viene construido.
Añadido · 2
The panel components, for a page that compiles Svelte 5
The panel stylesheet, on the single class verbaly-studio
Corregido · 3
The panel had a dark palette that nothing ever turned on
Its stylesheet painted the host page rather than only itself
Its shell measured itself against the window instead of the box it was given
Studio publicó su servidor hace dos versiones y decía, en su propio README, que faltaba la pantalla. Esta es la pantalla. Abre con tus mensajes, te deja corregir uno donde lo estás leyendo, y corre los dos comandos que cambian tus catálogos, con el presupuesto delante del que cuesta dinero.
Destacados
La pantalla que prometió la versión anterior de Studio.npx verbaly-studio abre ahora una página con todos los mensajes de tu proyecto, en los idiomas que marques, y el estado de cada uno.
Corriges una traducción en la propia fila donde vive. No se abre nada: la línea que estás leyendo se convierte en el campo donde escribes, y te dice qué está mal mientras escribes.
Los estados se llaman por lo que significan para ti: listo, falta, lo escribió una máquina y nadie lo ha leído, o rompe tu web. Cada uno lleva su propia forma además de su color.
Desde la página corren dos comandos. Uno lee tu código y añade el texto que tus catálogos no tienen. El otro traduce lo que falta, y como ese gasta dinero te enseña el presupuesto antes de llamar a nada.
Una tirada de traducción es algo de lo que puedes irte. Recarga la página y la barra de progreso vuelve, porque le pregunta a la tirada cómo va en vez de quedarse escuchándola.
Lo que escribe una máquina sigue siendo un borrador. Una tirada terminada te deja una lista que leer, no un trabajo dado por bueno.
Añadido · 4
The Studio panel: the list, editing in place, drafts, health and filters
Find new text and Translate what is missing, run from the page
The plan is its own address, so seeing the cost cannot start the spending
A run in progress survives a reload of the page
Corregido · 3
Progress counted batches, which run in parallel, so the bar could walk backwards
The panel translated with different settings than the command uses
Finding new text reported a count that summed every language
Alguien preguntó cuánto hay que aprender antes de que Verbaly sirva, y la respuesta resultó ser un problema de documentación y no de producto. Tres de las cuatro cosas que nuestra página de migración pedía nunca hicieron falta: un catálogo tal cual sale de i18next ya pasa, con sus grupos y todo. Esta versión borra ese trabajo de la página y le pasa el único cambio real a un comando.
Destacados
Un comando nuevo: npx verbaly migrate. Apúntalo a un proyecto que use otra librería de i18n y te pasa los catálogos que ya tienes. Primero te enseña qué haría, y --write lo aplica.
Tus ficheros conservan su forma. Los grupos siguen siendo grupos, y el comando devuelve exactamente la disposición que tu fichero ya tenía. No se reformatea nada.
Una sola cosa tiene que cambiar de verdad, y ya no la haces a mano: {{name}} pasa a {name}. Una llave doblada es como Verbaly escribe una llave literal, así que las dos formas no pueden significar lo mismo.
Lo que no va a adivinar, te lo dice. Un formato dentro de las llaves, un valor sin escapar, o una fusión que pisaría una key que usas se listan con el motivo, y esos mensajes se quedan exactamente como estaban.
Fusionar tus keys de plural es opcional.--plurals junta _one y _other en un solo mensaje, que es lo que enciende la selección de plural automática, y entonces el número se imprime ya localizado. Déjalo apagado y esas keys siguen funcionando.
Nuestra página de migración dejó de pedir trabajo que nunca hizo falta. Antes te decía que aplanaras tus grupos, fusionaras tus keys de plural y renombraras tus namespaces. Ninguna de las tres fue nunca obligatoria.
Añadido · 2
verbaly migrate: ports catalogs from i18next, vue-i18n, react-intl and friends
It reads both i18next plural spellings, including the one used before v21
Corregido · 3
The migration guide asked for three transformations that were never required
The same wrong claim lived in eight places across the docs and the package READMEs
The editor tip recommended one catalog style to everyone; it now tells you to match your own
Ya puedes ver tu proyecto entero en una dirección de tu propia máquina. npx verbaly-studio lee todos tus catálogos de una vez y te deja escribir una traducción de vuelta, revisada igual que la revisa tu build. Tus ficheros JSON son la base de datos y git es el historial, así que no puede desincronizarse de tu código: no es dueño de nada. Esta primera versión trae la parte de abajo, no la pantalla de arriba, y lo dice ella misma.
Destacados
Un paquete nuevo: @verbaly/studio. Ejecuta npx verbaly-studio dentro de tu proyecto y te sirve todo lo que Verbaly sabe de él en tu propia máquina: cada catálogo, lo que falta, lo que está roto, lo que escribió una máquina y en qué parte de tu código se usa cada mensaje.
Esta versión es el servidor, no la pantalla. Todavía no hay panel. Lo que funciona hoy es la dirección que sirve y --json, que imprime lo mismo que leerá la pantalla, así que ya puedes pasarlo por un script. La pantalla viene después.
Nunca toca tu texto fuente. Ese texto vive en tu código y la key sale de él, así que escribir encima aquí se desharía la próxima vez que extraigas. Guardar en tu idioma fuente se rechaza, y también una key que tu fuente no tiene.
Una traducción que guardas se revisa antes de entrar. Pasa las dos mismas comprobaciones que pasa tu CI, así que un {name} perdido, una etiqueta perdida o un plural sin su caso general se rechaza con el motivo, en vez de colar aquí y romperte el build después.
Nada de lo que lee puede tumbarlo. Un catálogo que no puede leer, un fichero de borradores que no puede leer, un fichero de código que no compila, o apuntarlo a la carpeta equivocada se avisan por su nombre, y el resto carga igual. Es lo que abres para arreglar esos ficheros, así que tiene que abrir.
Te dice qué traducciones automáticas merecen leerse primero. Añadir un idioma escribe cientos de golpe, y leerlas una a una es un muro. Seis señales apuntan a las pocas que piden tus ojos: en nuestro propio sitio son 18 mensajes de 1425 por idioma.
Añadido · 4
A new package, @verbaly/studio, with the verbaly-studio command
Four addresses it answers on: the whole project, its health, writing one translation, and approving drafts
Three defences on by default: it listens only to your own machine, it checks who is asking, and it mints a fresh key every time it starts
Triage: six signals that point at the machine translations worth reading first
Cambiado · 2
The compiler hands out seven more pieces, each one because the new package needs it
Its API table and what it actually exports are now checked against each other, and six names had been missing
Corregido · 1
A sentence in one of our own error messages did not agree with itself
Un enlace dentro de una frase te deja en tu idioma
En un sitio publicado en varios idiomas, todos los enlaces escritos a mano ya llevaban el idioma del lector, y los escritos dentro de una frase traducida no. Pulsar uno dejaba a un lector en español en las páginas en inglés, y por eso resultaba tan raro: el resto de la página estaba bien. Llevaba así desde que existen los enlaces dentro de mensajes, escondido detrás de dos tests que lo daban por bueno.
Destacados
Pulsa un enlace dentro de una frase traducida y te quedas en tu idioma. Hasta ahora esos enlaces apuntaban a tu idioma de origen, así que quien pulsaba "mira la guía" a mitad de un párrafo en español aterrizaba en la página en inglés.
Estaba escondido detrás de dos tests que lo afirmaban. Uno de ellos recorre el sitio traducido entero con un visitante, así que la suite seguía verde mientras el problema se publicaba. Los dos esperan ahora la dirección correcta.
Una página traducida lleva sus propios enlaces. Ya no necesita la lista de enlaces de tu configuración cuando llega al navegador, así que nada puede reconstruir un enlace sin su idioma.
Los formularios van con ellos. Enviar es navegar, así que un formulario que apunta a una página que publicas en todos los idiomas se queda dentro de ese idioma.
Cambiado · 1
Form targets get the same treatment as links, and what must never be rewritten is now pinned by a test
Corregido · 2
Links written inside a translated sentence sent the reader to your source language
And the browser rebuilt them without the language even when the page had them right
La versión anterior hizo que cada página llevara sus propios mensajes, y se equivocó en la segunda: en un sitio con routing de cliente, al pulsar un enlace el texto salía en tu idioma de origen hasta que el visitante recargaba. No era un fallo sino tres, que es por lo que se rompía tan del todo. Los tres están arreglados, y en Astro no tienes que escribir nada.
Destacados
La regresión de la 0.50.0 se acabó. Leer un sitio en español y navegarlo se queda en español: de página a página, de una sección al inicio, y al cambiar de idioma.
Eran tres fallos, no uno. Los mensajes que llevaba una página se leían una vez y nunca más, el runtime no distinguía "esta página no traía la palabra" de "este idioma no tiene palabra para eso", y el intérprete de texto trataba la página que metía el router como texto escrito por él.
En Astro se arregla solo. Encender inlineCatalog ahora cablea también el router de cliente, así que no escribes nada. Cualquier otro montaje tiene una llamada, adoptPage, desde su hook de navegación.
Cambiar de idioma salió más barato de paso. Pasar de español a portugués se descargaba el catálogo portugués entero; ahora la página a la que llegas ya trae lo que muestra, así que el cambio no cuesta nada extra.
Añadido · 2
A way to hand the runtime the next page’s messages when your router swaps the document
An option on the runtime merge so a page’s worth of messages does not count as the whole language
Cambiado · 1
Dependencies to their latest stable versions in both repositories, pnpm included, with the whole suite green
Corregido · 3
A page’s messages were read once per visit instead of once per page, so every page after the first read in your source language
A message the page did not carry looked like an ordinary fallback, so the rest of the language was never fetched
Text rendered by the server and swapped in by a router was overwritten with a fallback
Una página traducida deja de bajarse el diccionario entero
Hasta ahora, abrir una página en español descargaba todos los mensajes en español que tiene tu sitio, y la mayoría esa página no los muestra nunca. Ahora el mirror escribe en cada página exactamente los mensajes que renderiza, así que la página no necesita nada más. En este sitio, una primera visita a la home deja de bajarse unos 50 KB, que son unas nueve veces lo que cuesta el runtime entero de Verbaly.
Destacados
Una primera visita pesa mucho menos. Un visitante se bajaba 53 KB de mensajes en español para leer una página que usa 2.8 KB de ellos. Ahora recibe los 2.8 KB, y le llegan con la página en vez de como una segunda petición.
El ahorro es por página, y siempre es casi todo el fichero. Medido en este sitio: 50 KB menos en la home, 49 en una página de docs, 32 en novedades, que es la página más pesada de aquí.
No falta nada cuando alguien interactúa. Si tu página pide después un mensaje que no mostró, Verbaly se trae el resto una vez y actualiza, en vez de enseñar tu idioma de origen sin decir nada.
Es una línea y viene apagado. Añade inlineCatalog a tus ajustes de render. Un proyecto que no lo enciende se construye exactamente igual que antes.
Añadido · 2
A render setting that writes into each page the messages it renders, so it downloads no others
A way to tell the runtime that a language arrived with the page, so it fetches nothing until something is actually missing
Cambiado · 1
Dependencies to their latest stable versions, including three major ones, with the whole suite green
El servidor MCP podía contar tus mensajes, decirte cuáles faltaban y traducirlos, y no podía enseñarte ni una frase. Eso hacía imposible una cosa: revisar una traducción máquina a través de un agente, porque te daban una clave y te pedían aprobarla. Ahora un agente puede leer tus ficheros de idioma y tus ajustes directamente, y cada traducción que te espera viene con la frase de la que salió.
Destacados
Un agente ya puede leer lo que dice de verdad un mensaje. Antes te podía decir que faltaban catorce traducciones y no enseñarte ninguna. Tus ficheros de idioma y tus ajustes se leen directamente, y mirarlos no le gasta una de sus acciones.
Revisar una traducción máquina por fin funciona a través de un agente. Recibes cada una que te espera junto a la frase original, así que puedes leer las dos y decidir. Aceptar sigue siendo solo tuyo, a propósito: ninguna herramienta de aquí puede aprobar una traducción, y ninguna lo hará nunca.
Un agente puede montar Verbaly en un proyecto desde cero. Crea tu fichero de ajustes y tus ficheros de idioma, averigua qué bundler o framework usas, y te dice la única línea que aún tienes que añadir tú.
No cambia nada de lo que ya usas. Todas las herramientas responden exactamente igual que antes.
Añadido · 3
Two addresses an agent can read: your settings, and every message of one language
A list of the machine translations waiting for you, each next to the sentence it came from
A way for an agent to set Verbaly up in a project from nothing
Cambiado · 2
The tools are handed over in the order you meet a project, starting with setting it up
The dependency tree carries no known vulnerabilities: three were reported and all three are resolved
Cuando tus páginas viven en direcciones como /es/dashboard, esa dirección es la que dice en qué idioma está la página. Hasta ahora las integraciones de servidor de Verbaly nunca la miraban: preguntaban por la elección guardada del visitante y por su navegador, así que el mismo enlace podía llegar en inglés para una persona y en español para otra. Ahora manda la dirección, porque una dirección se comparte, se enlaza y se cachea mientras que una elección guardada es de una sola persona.
Destacados
Una página en /es/dashboard ahora llega en español, siempre. Antes Verbaly preguntaba por la elección guardada y por el navegador en vez de leer la dirección, así que el mismo enlace podía mostrar dos idiomas distintos a dos personas.
Tus páginas en inglés dejan de traducirse por accidente. Quien eligió portugués una vez recibía portugués también en tus páginas sin prefijo. Una dirección a secas ahora significa tu idioma de origen, para todo el mundo.
Las páginas de Next.js vuelven a poder construirse por adelantado. Verbaly leía los headers de la petición en cada página, lo que convertía en silencio cada página traducida en una que se reconstruye en cada visita. Pásale el idioma desde el nombre de tu carpeta y la página vuelve a construirse una sola vez.
Tus páginas traducidas también pueden contarse entre ellas a los buscadores desde un servidor. Los enlaces de idioma y la dirección canónica existían solo para sitios construidos estáticamente, y el mismo conjunto está ahora disponible donde sea que rendericen.
Las rutas de SvelteKit se quedan simples. Una línea y /es/docs lo sirve tu ruta /docs, así que no duplicas tus carpetas de rutas por idioma.
No cambia nada si tus direcciones no llevan idioma. Todo esto se activa con el ajuste de URL que ya tienes.
Añadido · 4
A way to tell your server integration that the address carries the language, so the address decides
A hook for SvelteKit that serves every language from a single route tree
A way to hand Next.js the language from your folder name, which is what keeps the page built ahead of time
A helper that gives you one page’s address in every language, for the head of a server-rendered page
Cambiado · 2
Nuxt reads the address on its own, with nothing extra for you to write
A statically built site and a server-rendered page now get their language links from the same place, so they cannot disagree
Corregido · 3
A page with the language in its address could be served in another language, if the visitor had picked one before
Every translated Next.js page was being rebuilt on each visit instead of built once
Two setup examples in the package guides did not work as written
Lo normal en Verbaly es que escribas texto y nunca veas una key, y eso no cambia. Esta versión cubre el otro caso real: keys que son un contrato con algo de fuera de tu app, un cliente móvil o una memoria de traducción, escritas una vez en un módulo y leídas desde todas partes. Hasta ahora Verbaly no veía esas keys, así que limpiar los mensajes sin usar te las borraba y nada comprobaba que existieran. La segunda mitad es el sitemap, que publicaba redirecciones y páginas de error como si fueran resultados.
Destacados
Puedes tener tus keys en un módulo y Verbaly las sigue comprobando. Un fichero que asocia nombres a keys es una forma que muchos proyectos ya escriben. Antes Verbaly no veía esas keys: limpiar los mensajes sin usar te las borraba, y nada verificaba que existieran.
Una key que ningún catálogo tiene es un error donde la declaras, no un hueco que descubres en la interfaz más tarde. Tu editor autocompleta desde tu propio catálogo mientras escribes el módulo.
Tu sitemap deja de listar páginas que piden no ser indexadas. Las redirecciones y las páginas de error se publicaban como si fueran resultados. En este sitio eran 21 direcciones de 81, y la lista son ahora solo las 60 páginas reales.
Esas páginas se siguen traduciendo y publicando igual que antes. Lo único que cambió es la lista de direcciones, así que quien aterrice en una la sigue recibiendo en su idioma.
Nada cambia si no usas ninguna de las dos. Son añadidos, y un proyecto que no escriba ninguna se comporta exactamente como antes.
Añadido · 2
A way to declare your keys in a module, so Verbaly checks them and your editor completes them
A setting to leave a page out of the sitemap by name
Cambiado · 1
The sitemap lists only pages a search engine should index, while every page is still translated and published
Corregido · 1
An unknown key was reported with a long absolute path instead of the file you edit
Todo lo que se arregla aquí está en el camino que recorres una sola vez, cuando añades Verbaly a un proyecto que ya existe: instalarlo, crear su estructura, pasar la revisión, envolver tus textos y extraerlos. Nada de ello está en el camino que recorres a diario, y por eso más de mil pruebas nunca lo vieron. El comando que crea la estructura ignoraba la configuración que ya habías escrito y creaba un segundo juego de catálogos. Un fichero suelto en tu carpeta de catálogos se convertía en un idioma que no podías quitar. El comando que envuelve tus textos podía dejar un proyecto sin compilar. Todo eso queda arreglado, y una comprobación nueva hace una instalación real con dos gestores de paquetes para que siga arreglado.
Destacados
verbaly init ahora lee la configuración que ya escribiste. Antes la ignoraba y creaba un segundo juego de catálogos en la carpeta por defecto y en el idioma por defecto. Si tu configuración decía español en locale/, te salía inglés en locales/.
Cambio importante: los idiomas que listas en tu configuración son ahora la lista entera. Un fichero JSON olvidado en tu carpeta de catálogos se convertía en un idioma que no podías quitar nunca, y bloqueaba compilaciones pidiendo traducciones que nunca quisiste. Si no los listas, los ficheros de la carpeta siguen decidiendo, igual que antes.
verbaly wrap --write ya no deja tu proyecto sin compilar. Antes envolvía texto en ficheros donde t no estaba disponible. Ahora se salta esos ficheros, te dice cuáles son y cuántos textos esperan en cada uno, y si son componentes de cliente para que sepas qué línea añadir.
El envoltorio deja de recoger cosas que no son texto de interfaz: el contenido de los bloques de código, palabras sueltas en minúscula como usuarios de demo y nombres de fichero, y frases que empiezan por un valor en vez de por palabras.
Cuando el comando verbaly no está instalado, Verbaly lo dice en vez de pedirte que lo ejecutes. La compilación nombra el paquete que hay que añadir, y la revisión tiene una línea para ello.
Añadido · 2
A health check that tells you whether the verbaly command is actually available in your project
A list of the files wrap refused to touch, with how many texts wait in each and whether it is a client component
Cambiado · 5
Breaking: the languages listed in your config are the whole list, and a stray catalog file is no longer adopted
Scaffolding reads the config file you already wrote instead of falling back to its defaults
Wrapping writes only into files where t is already available, so the project always still compiles
Wrapping leaves code blocks, single lowercase words and sentences that open with a value alone
A build that blocks on missing translations names the package to install when the command it suggests is not there
Corregido · 2
Asking for help with no command reported a failure
A catalog handed to the provider for the source language was quietly ignored
Un catálogo solo crece, y todas las páginas pagaban el catálogo entero
El texto que vive en una sola página, una lista de versiones, un archivo de blog, una lista larga de preguntas, lo descargaba cada visitante en todas las páginas. Esta versión te deja nombrar esos grupos de mensajes y dejarlos en el build. Las páginas que los usan siguen saliendo traducidas, porque ese texto ya está escrito en el HTML, y el navegador deja de bajarlo. Buscar la prueba destapó además un defecto real por debajo. Tras su primera pasada, Verbaly podía sustituir texto traducido correcto de la página por el nombre interno del mensaje cuando ese mensaje faltaba. Queda arreglado aquí, y le importa a cualquiera que pre-renderice su sitio.
Destacados
El texto que solo necesita una página deja de viajar a todas. Nombra ese grupo de mensajes en tu configuración y el navegador ya no lo descarga. Las páginas que lo usan siguen saliendo traducidas, porque ese texto ya está en el HTML.
El texto pre-renderizado ya no se destruye. Si falta un mensaje, Verbaly conserva el texto que ya está en la página en vez de sustituirlo por el nombre interno del mensaje. Antes eso podía pasar un instante después de cargar la página.
Medido en este sitio: el fichero que carga cada página pasó de 46,3 KB a 33,5 KB comprimido, y los árboles en español y portugués bajaron unos 14 KB cada uno. No se perdió nada en pantalla.
Nada cambia si no lo activas. Es una opción nueva, y un proyecto que no la escriba se comporta exactamente igual que antes.
El build te avisa cuando algo pinta mal, nombrando un grupo que no coincide con nada de tu catálogo, o uno que tu código lee directamente.
Añadido · 1
An option that keeps the groups of messages only the build needs out of the browser
Corregido · 1
Pre-rendered text is no longer replaced by a message’s internal name when that message is missing
Nuestros propios chequeos medían la máquina, no el código
El job que comprueba nuestra cobertura de tests fallaba al azar, y la causa no era un test lento. Un test que tarda 22 milisegundos lo tumbaba un límite de 5 segundos, porque ese límite cuenta tiempo real y nuestro comando de tests levanta doce procesos sobre los mismos núcleos. Dos veces eso bloqueó una publicación. Esta versión lo arregla de raíz, y dedica el resto a la misma idea aplicada a nuestra cadena de suministro: qué puede hacer nuestro build, qué herramientas corre exactamente, y código que no podía ejecutarse nunca. Nada del paquete cambia.
Destacados
Nada de lo que instalas cambia. El runtime es idéntico al de la 0.43.0 y toda la API publicada se comporta igual. Esta versión va de la maquinaria que nos revisa.
Nuestro build dejó de fallar al azar. La suite podía tumbar un test perfectamente sano solo porque la máquina estaba ocupada, y dos veces eso bloqueó una publicación.
Cada herramienta que corre nuestro build está fijada a un commit exacto. Una etiqueta la puede mover quien la controla, pero un commit no, así que nada puede cambiar en silencio lo que construye tu paquete.
El job que publica las versiones pide permiso de escritura solo donde lo necesita, en vez de dárselo a todo el flujo.
El análisis estático corre en cada cambio, y su configuración vive ahora en nuestro repositorio, fijada y legible, en vez de ser un ajuste que solo veíamos nosotros.
Añadido · 1
A CodeQL workflow, so the configuration of our static analysis lives in the repository instead of in a setting
Cambiado · 4
The test suite no longer fails a healthy test when the machine is busy
Every GitHub Action our build uses is pinned to an exact commit
The release job holds write access only on the job that needs it
Three branches of code that could never execute were removed
Todo lo que decimos de nosotros vuelve a ser cierto
El badge de nuestro README dice ~3KB desde el principio, mientras el runtime pesaba en realidad 3,68 KB. En vez de debilitar el claim, esta versión saca la última pieza que valía la pena sacar: el formato de tiempo relativo son 318 bytes que casi ninguna aplicación escribe, y ahora solo viaja cuando alguno de tus mensajes lo pide. El resto de la versión es la misma idea apuntada a todo lo demás que el proyecto dice de sí mismo.
Destacados
El runtime pesa 3,00 KB. El formato de tiempo relativo se suma a ICU como algo que solo viaja cuando alguno de tus mensajes lo usa de verdad, y no hay nada que tengas que configurar.
Un formateador que escribas tú ahora sabe para qué mensaje lo llamaron. Recibe el nombre del parámetro y la clave del mensaje, así que sus propios errores pueden decir dónde ocurrieron, cosa que antes no se podía.
La política de seguridad cubre los doce paquetes, no los siete que existían cuando se escribió, y ahora explica cómo verificar que una versión salió de verdad de nuestro CI.
Nuestra postura de seguridad es algo que puedes comprobar en vez de algo que afirmamos: una puntuación de la OpenSSF corre en cada cambio y el badge del README la lee.
Añadido · 2
A setting that ships relative time formatting anyway, for text that only reaches your app after the build
A formatter receives the parameter name and the message key it was called for
Cambiado · 3
The runtime went from 3.28 KB to 3.00 KB for a project that writes neither ICU nor relative time
The size limits were tightened again, so the saving cannot be quietly spent later
The README quotes the measured number and names the command that measures it
Un runtime más pequeño para quien nunca necesitó ICU
La sintaxis propia de Verbaly cubre plurales, selects y formatos, y la sintaxis ICU es la salida para lo que no cubre. Esa salida siempre se describió como algo opcional, y nunca lo fue: el parser entraba en toda aplicación usara ICU o no. Nuestro propio sitio de documentación no tiene ni un mensaje ICU y lo publicaba en cada página. Verbaly ya lee tus catálogos, así que ahora solo manda el parser donde algún mensaje lo necesita de verdad.
Destacados
El runtime es un 11% más pequeño para quien no usa sintaxis ICU, que son casi todos los proyectos. El parser solo viaja cuando alguno de tus mensajes lo necesita de verdad, y no hay nada que tengas que encender.
No cambia nada si sí usas ICU. Verbaly lo ve en tus catálogos y se lleva el parser, igual que antes.
Un mensaje que llega a tu aplicación después del build y usa ICU ahora muestra su propio texto y dice por qué, en vez de quedarse a medio renderizar en algo que parece casi correcto. Si tu texto viene de un CMS, un ajuste manda el parser igual.
Añadido · 1
A setting that ships the ICU parser anyway, for text that only reaches your app after the build
Cambiado · 3
The runtime went from 3.68 KB to 3.28 KB for a project whose messages do not use ICU syntax
An ICU message with no parser shows its own text and says why, instead of rendering something that looks almost right
The size limits were tightened to match, so the saving cannot be quietly spent later
Una línea para cambiar, y tu elección por fin se queda
Verbaly traía un cambiador de idioma completo para una forma de hacer las direcciones y una pieza suelta para la otra, así que todo proyecto escribía la segunda a mano, el nuestro incluido. Reescribir nuestro propio sitio sobre el helper de la librería encontró un fallo que llevaba ahí desde que el cambiador entró en el runtime: quien elegía un idioma volvía al de su navegador en la siguiente carga. La otra mitad es que Verbaly había empezado a suponer que querías el idioma en tus direcciones; ahora lee tu montaje en vez de adivinar.
Destacados
Cambiar de idioma es una línea, y es la misma línea funcionen como funcionen tus direcciones. Va a la otra dirección cuando el idioma vive ahí, y cambia el texto donde está cuando no.
Tu elección de idioma sobrevive a la siguiente carga. Elegir inglés en una página en español podía devolverte al español, porque el cambio guardaba tu elección en un sitio y la página que te recibe leía otro.
Verbaly deja de suponer que quieres el idioma en tus direcciones. Lee tu montaje: si construyes una versión de tu sitio por idioma tienes direcciones por idioma, y si no, una sola sirve para todas. Solo lo escribes para llevar la contraria.
verbaly doctor te dice si leyó tu elección o la dedujo, para que un ajuste que nadie escribió no parezca una decisión que alguien tomó.
La comprobación de tamaño ahora mide lo que una aplicación real embarca de verdad, y no solo la importación más pequeña posible y la más grande imposible.
Añadido · 2
A language switcher that works the same in both ways of handling addresses, bound to your project so there is nothing to pass
The switch remembers your choice in both places a page can read it back from, under one name
Cambiado · 4
Where the language lives in your addresses is read from your setup instead of assumed, and you only write it to disagree
verbaly doctor says whether it read that setting or worked it out
On Next.js the switch uses the app router, so a language change keeps your app alive instead of re-rendering twice
The size check measures four things now, and separates a budget from a warning light
Corregido · 2
Someone who picked a language was sent back to their browser language on the next page load
A project with a single address was told it had one address per language, which was simply not true of it
Di dónde vive el idioma, y traduce también la cabecera
Verbaly lleva mucho con dos formas de cargar el idioma y nunca nombró ninguna, así que cuál te tocaba lo decidía si te acordabas de pasar un campo opcional. Nombrarlas destapó lo que hacía que nombrarlas importara: una página podía estar traducida en todo lo que un visitante mira y aun así decirle a un buscador que está en inglés, porque el título y la descripción viven en la cabecera, donde nadie los lee. Nuestra propia documentación publicaba 27 de 27 páginas así.
Destacados
Ahora puedes decir dónde vive el idioma en tus direcciones, una vez, en la configuración: /docs y /es/docs, un prefijo en todos los idiomas, o una sola dirección para todos. Antes la respuesta dependía de qué helper llamabas y qué le pasabas.
El selector de idioma es una línea y no se puede escribir mal. Verbaly te da un helper que ya conoce tus idiomas, tu fuente y tu modo, y la misma línea sigue funcionando si cambias de idea sobre el modo.
El título y la descripción de tus páginas también se traducen. Viven en la cabecera, nadie los lee en la página y son lo que enseña un resultado de búsqueda, así que una página podía parecer traducida entera y aparecer listada en inglés.
verbaly render cuenta las páginas cuyo título todavía no cambia nunca y te lo dice una vez, con el arreglo. Nunca tumba un build: el nombre de un producto puede no traducirse.
verbaly doctor te dice en qué modo están tus direcciones, y te avisa cuando tu configuración pide dos cosas que no pueden ser ciertas a la vez.
Breaking: pedir una ruta sin nombrar tu idioma fuente ya no compila. Antes ponía prefijo a todos los idiomas incluido el tuyo, en silencio, que casi nunca era lo que alguien quería.
Añadido · 4
A routing setting with three values, so where the language sits in your addresses is said once instead of inferred
A path helper that already knows your languages, your source and your mode, so a language switcher cannot be written wrong
A routing line in verbaly doctor, including a warning when two settings ask for opposite things
verbaly render counts the pages whose title never changes between languages, and says it once with the fix
Cambiado · 3
Asking for a path now requires naming your source language wherever that decides the answer
Under one-address mode the path helper returns the address you are already on, so one switcher works in both modes
The English descriptions on this site were rewritten shorter: several ran past what a search result shows
Corregido · 3
A translated page shipped its title and description in the source language, which is most of what a search result shows
Switching language without a navigation left the page title alone, and nothing said so
Pages that only redirect were being reported for a title nobody will ever read in a search result
Traducir sobrevive a una mala conexión, y los agentes reciben el ciclo entero
Verbaly deja que un agente de código lo ejecute desde hace tiempo, pero de sus doce comandos solo cuatro llegaron a estar disponibles, y los dos que faltaban son los dos primeros que un agente necesita: averiguar qué falla en un proyecto y envolver el texto que ya está escrito en él. Mirando eso apareció algo peor al lado. verbaly translate es el único comando que cuesta dinero y minutos, y una sola respuesta mala tiraba la corrida entera: lotes ya pagados y traducidos nunca llegaban al disco.
Destacados
verbaly translate ya no pierde el trabajo que ya pagaste. Una petición que no vuelve se reintenta, y si aun así falla la corrida sigue y guarda todo lo demás. Volver a correrlo pide solo lo que falta, en vez de pagarlo todo otra vez.
Traducir es unas cuatro veces más rápido. Ahora las peticiones salen juntas en vez de una detrás de otra, y una corrida larga te va contando qué llegó mientras trabaja, en lugar de quedarse callada durante minutos.
Puedes decir qué no se traduce nunca. Pon los nombres de tu producto y cualquier forma que ya hayas decidido en translate.glossary, y cómo tratas al lector en translate.instructions, en vez de corregir la misma palabra a mano después de cada corrida.
Tu agente de código recibe las dos herramientas que le faltaban: una para averiguar por qué un proyecto no funciona, y otra para encontrar el texto escrito directamente en tus componentes y envolverlo. Llevar una aplicación existente a Verbaly ya no necesita una terminal.
Ahora los agentes leen números, no frases. Cada herramienta responde con datos simples además del texto, así que un agente deja de adivinar una redacción que puede cambiar entre versiones.
Aceptar una traducción automática sigue siendo algo que solo puede hacer una persona, y eso no ha cambiado.
Añadido · 4
Two new tools for coding agents: one that diagnoses a project end to end, and one that wraps hardcoded text so the compiler can pick it up
Every agent tool now answers with structured data alongside the sentence it always returned
translate.instructions and translate.glossary, so tone and required wordings are settings of the project rather than something you fix afterwards
translate.concurrency and translate.retries, for a provider that rate limits you or a connection that drops
Corregido · 6
One failed request threw away every request that had already succeeded, so a run that broke near the end wrote nothing at all
A request that failed for a passing reason was treated as a real failure, with no second attempt
Requests went out one at a time although nothing connected them
An answer cut short by length failed with a message about broken text instead of saying the batch was too big
A translate run said nothing at all while it worked, sometimes for minutes
verbaly doctor counted the way the rest of the tool stopped counting, and a suggested fix could end up in a different place than the problem it fixed
Las traducciones escritas por grupos ya son seguras en todas partes
Puedes guardar tus traducciones como grupos anidados en vez de una sola lista plana, y Verbaly lleva tiempo leyendo esa forma. Solo la leía la mitad: los comandos que escriben tus catálogos tomaban los nombres de grupo como si fueran mensajes. Así que extract añadía una segunda copia de cada mensaje, --prune borraba grupos enteros con todas las traducciones dentro, los exports llegaban rotos a tu traductor y translate decía que no había nada que hacer sobre una app a medio traducir. Para un catálogo plano no cambia nada.
Destacados
Las traducciones guardadas por grupos están a salvo de punta a punta. Ahora todos los comandos las leen y las escriben igual, y tu fichero conserva la forma que le diste en vez de quedar aplanado a tus espaldas.
verbaly extract --prune ya no borra traducciones. En un catálogo escrito por grupos leía el nombre de cada grupo como un mensaje, no lo reconocía y se llevaba el grupo entero con todas las traducciones dentro.
Tus ficheros para el traductor dejan de llegar rotos. El export escribía [object Object] donde tenía que ir el texto, y verbaly translate decía que no había nada que hacer mientras verbaly status decía que media app estaba sin traducir.
Un idioma que añadas después encaja con el resto. Un catálogo nuevo toma la forma de los que ya tienes, así que un proyecto nunca acaba medio por grupos y medio plano.
La herramienta cuenta en inglés de verdad. Antes decía 1 messages, 1 locales y 1 pages en nueve sitios.
Un aviso repetido se dice una sola vez. Una colisión de keys se anunciaba tres veces en un solo verbaly extract, una de ellas después de que el resumen ya se hubiera impreso.
Cambiado · 2
A catalog value that is not text is now refused, naming the file and the exact path, instead of being dropped the next time a catalog is written
Reading a message is faster on a large catalog, and stops falling off a cliff past a couple of thousand of them
Corregido · 4
Commands that write your catalogs read a group name as if it were a message, so extract duplicated, prune deleted, export broke and translate went quiet
A language added later was always written flat, even when every other catalog in the project used groups
Nine lines of output said "1 messages", "1 locales", "1 pages", "1 keys" or "1 translations"
A key collision was reported once per command instead of once per run
La versión anterior le puso nombre a las dos formas que tiene un sitio de llevar su idioma. Escribir la página que las explica destapó por qué una de ellas nunca había funcionado del todo: si tu app mantiene una sola dirección por página y carga los idiomas a demanda, Verbaly sabía qué idioma quería el visitante y sencillamente no lo pedía nunca. Una línea, y el ejemplo de nuestras propias docs lo venía enseñando desde siempre.
Destacados
El idioma guardado ya sobrevive a un F5 en un sitio de una sola dirección. Quien volvía recibía el idioma fuente otra vez, sin nada en la consola que lo explicara.
No cambia nada si tu idioma va en la dirección, que es de lo que iban las dos versiones anteriores. Esto solo estaba roto donde el idioma sale del almacenamiento o del navegador.
Un aviso que daba un consejo imposible ahora da el bueno. Cuando tu página dice un idioma y Verbaly está en otro, te decía que leyeras el idioma de la dirección. En un sitio de una sola dirección no hay nada que leer.
React, Vue y Svelte se probaron uno a uno contra un idioma que llega cuando la página ya está en pantalla. Antes era un camino raro y ahora es el normal.
Corregido · 2
A language chosen on a previous visit was recognised but never loaded, so the page stayed in the source language
The page-language warning suggested a fix that does not exist on a site without the language in the address
Verbaly publica tu sitio en cada idioma, cada uno en su propia dirección. La versión anterior dejó esas páginas bien, y esta va sobre cómo llega la gente hasta ellas, y sobre poder preguntar desde tu propio código a qué idioma pertenece una página. Las dos cosas las tenía que escribir a mano cada proyecto, este sitio dos veces en un día. También arregla una forma en la que el nombre de una página se podía confundir con un idioma.
Destacados
Preguntar en qué idioma está una página ya no necesita un ayudante tuyo. Verbaly responde con el idioma que dice la dirección, o con nada cuando la dirección no dice ninguno. Esa es una pregunta distinta a qué prefiere el visitante, y mezclar las dos es lo que dejaba que una preferencia guardada pintara el idioma equivocado sobre una página.
Verbaly ya puede mandar al visitante a su idioma. Lo activas y quien aterriza en tu página de inicio va a tu sitio en su idioma antes de que se dibuje nada. Solo la página de inicio, así que un buscador sigue llegando directo a cualquier página de tu sitio.
Un sitio servido desde una subcarpeta funciona. Le dices a Verbaly cuál es la carpeta y los enlaces dentro de cada idioma apuntan a donde deben, en vez de apuntar a la nada en silencio.
Una página llamada /es-la-guia es una página, no español. Verbaly leía el principio de una dirección con demasiadas ganas, así que una página cuyo nombre empezaba como un código de idioma se podía traducir a ese idioma. Ahora solo cuenta un código de idioma de verdad.
Lo que esta versión no cambia: una app con una sola dirección por página funciona exactamente igual que antes. Todo esto es para sitios donde cada idioma tiene su propia dirección.
Añadido · 3
A way to ask which language the address says, which answers nothing when the address says none
An option that sends someone landing on your home page to their language, before anything is drawn
Support for a site served from a subfolder, in the address helpers and in the pages Verbaly writes
Corregido · 1
A page whose name begins like a language code is no longer read as that language
Verbaly publica tu sitio estático en cada idioma, bajo /es/ y /pt/. Esta versión va sobre lo que le pasaba a quien llegaba a una de esas páginas: llegaba en español y parpadeaba a inglés antes de volver, nada le decía al runtime que ya estaba en una página en español, y todos los enlaces apuntaban hacia fuera. La página estaba bien y todo lo que había entre ella y el visitante, no.
Destacados
Una página pre-traducida deja de parpadear a inglés. Verbaly servía la página en español y luego pintaba el texto en inglés encima hasta que terminaba de cargar el catálogo español, justo en las páginas que esta función existe para mantener quietas.
Aterrizar en /es/ ya es suficiente. Verbaly lee el prefijo que él mismo escribió antes de mirar el almacenamiento o el navegador, porque la página que estás viendo ya está en ese idioma.
Cada idioma deja de ser una isla. Sus enlaces apuntaban a la raíz del sitio, así que llegar en español y hacer un clic te devolvía al inglés. Los enlaces a páginas que ese idioma también tiene ahora se quedan dentro.
Tu selector de idioma lleva a tu sitio en ese idioma. Esa es la dirección que se puede compartir y la que indexan los buscadores, y la página llega ya traducida, así que no parpadea nada. En una app con una sola dirección por página, cambiar de idioma sigue cambiando solo el texto, igual que antes.
Lo que esta versión no hace: no enruta a nadie. Quien aterriza en tu raíz está recibiendo el idioma fuente, y mandarlo a su idioma es una regla en la configuración de tu hosting.
Añadido · 3
A helper that gives you the same page in another language, which is what a language switcher needs on a mirrored site
A warning when the page language and your instance disagree, because that combination is always a flash
An end to end test that builds a site and walks a visitor through the pages it wrote
Cambiado · 3
The DOM binder no longer repaints text it did not write while the catalog for that language is still loading
Locale detection reads the url first, then storage, then the browser
Mirrored redirect pages send you to the mirrored target, not to the root one
Corregido · 3
The runtime undid the work of the pre-render on every mirrored page
Locale detection ignored the language prefix Verbaly had written itself
A link inside a translated message could stop the pre-render with an internal error
El gate del build se niega a compilar cuando una traducción no puede renderizar lo que renderiza tu fuente, pero solo ve lo que hay en tu proyecto al compilar. Esta versión va por todo lo que llega después: un catálogo que viene de un gestor de contenidos, un archivo de código que Verbaly no pudo leer y un salto de línea dentro de un mensaje con formato sobre el que la página pre-renderizada y el navegador no se ponían de acuerdo. Cuatro errores reales, cada uno una forma de publicar algo mal con todos los comandos en verde.
Destacados
Un salto de línea dentro de un mensaje con formato ya no se duplica en una página pre-renderizada. Verbaly lo escribía de una forma que los navegadores leen como dos saltos, y la página los reemplazaba por uno al cargar. Un salto visible, justo en las páginas que esa función existe para mantener quietas.
Una traducción que llega como null ya no tumba tu aplicación al arrancar. Un campo que nadie rellenó en un gestor de contenidos paraba todo antes del primer render. Ahora se salta esa clave, la aplicación funciona y la consola te dice cuál era.
Los comandos sobreviven a un archivo que no pueden leer. Un archivo con un error de sintaxis terminaba la ejecución entera con una posición y ningún nombre de archivo. Ahora se nombra el archivo, se saltan sus textos y el resto se sigue leyendo.
El texto dentro de JSX escrito en un archivo .js se encuentra igual que en cualquier otro sitio. Una aplicación React con componentes en .js no podía ni ejecutar el comando extract, porque la primera etiqueta lo terminaba.
Un aviso ya no se repite porque cambió un número. Tres de ellos imprimían el valor que los causaba, así que un contador subiendo podía llenar la memoria de copias del mismo mensaje.
Añadido · 3
One shared list of the HTML tags that take no children, read by every surface that renders rich text
A test that renders the same messages through the static output and through the browser and compares the two, the one that caught the line break bug
Files the parser cannot read are now reported by name, by the extract command, by doctor and by the build plugins
Cambiado · 4
A catalog value that is not text is skipped with a warning naming its path, instead of crashing or disappearing
Every warning names the message it came from, and none of them prints the value that caused it
Files ending in .js are read with JSX support, the same as .jsx and .tsx
Counts in the command line read as text, so nothing says "1 messages" any more
Corregido · 5
The pre-rendered page and the live page disagreed about line breaks inside rich messages
A null value in a catalog stopped the app before its first render
One unreadable file ended the extract, check and doctor commands
Three warnings could repeat forever as the value that caused them changed
The review command accepted a locale option that its help never mentioned
El paquete del compilador te dejaba importar 132 cosas distintas, y ninguna estaba escrita en ningún sitio. Se habían ido acumulando como lo que los paquetes de framework fueran necesitando, y en la versión 1.0 cada una se habría convertido en una promesa que nadie había revisado nunca. Esta versión lee la lista entera contra una regla escrita, publica las 60 que la pasan, las documenta y añade un test que falla si la lista vuelve a crecer sin querer.
Destacados
Si usas la línea de comandos o cualquiera de los paquetes de framework, esta versión no te cambia nada. Ni configuración, ni imports, nada que editar. Todo lo de aquí pasa dentro de un paquete al que solo llegan las integraciones hechas a mano.
El paquete del compilador ya documenta qué puedes importar, en dos grupos: los tipos que necesitan tu archivo de configuración y tu propio proveedor de traducción, y las piezas para construir una integración que nosotros no publicamos. Todo lo demás es interno y ahora lo dice.
La lista pasó de 132 nombres a 60. Cada uno se quedó porque lo usa una integración real, porque es una forma documentada de extender Verbaly, o porque otro lo necesita. Los 72 que no cumplían ninguna de las tres ya no están.
Un plugin de build ya no recibe el árbol interno con el que Verbaly lee tu código. Antes le llegaba entero solo para leerle dos campos, y ahora recibe los mensajes que encontró. Una cosa menos que se puede romper cuando cambie ese lector.
Los dos paquetes fallan su propio test si la lista crece, así que añadir algo público es ahora una decisión que alguien revisa, en vez de algo que pasa de camino a arreglar otra cosa.
Añadido · 3
A test in the compiler package and another in the runtime that pin the exact list of public names, each one verified to fail on an extra export before being trusted
A "Programmatic API" section in the compiler package readme: the two groups, every name by area, and a minimal plugin example
The custom-provider example now shows the type that describes it, and mentions that providers also receive the source files each text appears in
Cambiado · 3
The per-file build step hands a plugin the messages it found instead of the internal parse tree
72 names are no longer importable from the compiler package; the command line covers the same ground
Nothing in the runtime moved, and nothing about what Verbaly renders changed
Corregido · 1
Four test files were being measured against the budget for a quick test while they load the real compiler, which could fail a release at random
Verbaly ya se negaba a compilar cuando una traducción no podía renderizar lo que renderiza tu fuente. Esta versión cierra la misma historia por el otro lado: cada sitio donde Verbaly hacía lo seguro sin decir nada ahora te avisa, en el momento en que puedes actuar. Un plural que renderiza una línea vacía, un precio sin moneda, un bloque escrito donde no puede funcionar y un nombre importado de donde no existe: cuatro formas de publicar el texto equivocado con todo en verde.
Destacados
Un plural sin caso comodín ahora avisa en el navegador, no solo en el build. La comprobación del build ya lo rechazaba, pero un catálogo que llega por carga diferida o desde un gestor de contenidos nunca pasa por el build, así que la línea vacía podía llegar a una página real. Ahora nombra el mensaje y te dice qué añadir.
Un hueco al que le falta su argumento deja de renderizar un número pelado. Un precio sin código de moneda, una medida sin unidad, una fecha sin unidad de tiempo: cada uno imprimía el valor plano y se callaba. Ahora cada uno dice qué necesita y te muestra la forma del arreglo.
Escribir un plural dentro de una plantilla t ya no se publica con las llaves a la vista. En esa plantilla los valores vienen de tu código, así que un plural o un formato escrito ahí se vuelve texto que ven tus usuarios. verbaly extract y verbaly doctor ahora señalan el archivo y el texto exacto.
Importar t del paquete por fin recibe una respuesta que menciona a Verbaly. t viene de tu instancia o del módulo generado, nunca del paquete, y hasta ahora solo se quejaba el bundler, con un mensaje que no daba ninguna pista de dónde debía salir t.
Cada error de la línea de comandos imprime su nombre una vez. Antes todo fallo se leía [verbaly] [verbaly].
Añadido · 3
The runtime reports every way a message can degrade, naming the message: a plural with no catch-all case, a placeholder missing its argument, a list that is not a list
verbaly extract and verbaly doctor point at a message that kept a plural or a format as literal text
The doctor reports t imported from a verbaly package, with the line that says where it really comes from
Cambiado · 3
The doctor fails on setup faults that break a build on their own, and never on something that builds and renders
The relative-time warning names both suspects instead of blaming the time unit for what is often an invalid date
Nothing about what Verbaly renders changed: this version only adds the report
Corregido · 2
Every error from the command line printed its name twice
A test that starts an external process was being measured against the budget for a quick one, which could fail a release at random
Un comando decía que el proyecto estaba sano mientras otro bloqueaba su build por los mismos archivos. El doctor rechazaba los catálogos escritos como grupos anidados, aunque el runtime siempre los ha leído, y le decía a un proyecto Astro o Nuxt que instalara el paquete que el comando de setup le acababa de desaconsejar. El gate del build leía cualquier palabra entre signos de menor y mayor como markup, y tumbaba traducciones correctas por eso. Todo eso pasa a contar la misma historia: lo que un comando te dice es lo que tu proyecto es.
Destacados
verbaly doctor deja de contradecir a verbaly check. Una traducción que está pero está rota ahora también es un error en el doctor, así que el comando que corres cuando algo va mal ya no puede responder "el setup se ve sano" en un proyecto cuyo build falla.
Los catálogos escritos como grupos anidados funcionan en todas partes. El doctor los rechazaba como inválidos, y el gate solo comparaba los nombres de los grupos, así que un grupo con una línea sin traducir dentro contaba como traducido entero. Ahora cada línea se revisa por su cuenta, como el runtime ya la leía.
El doctor te señala el paquete que tu proyecto realmente necesita: Astro, Nuxt, Next.js, SvelteKit, Vite o el plugin de tu bundler. Es la misma respuesta que da verbaly init, porque los dos leen ahora una sola tabla.
El build deja de fallar por texto que solo parece markup. "Press <Enter> to continue" traducido como "Pulsa <Intro> para continuar" se reportaba como dos tags rotos. Un tag cuenta como estructura solo cuando el mensaje lo cierra, así que tu prosa sigue siendo prosa y un tag de énfasis o un enlace de verdad siguen protegidos.
Cada fallo nombra el paso que lo repara, en vez de un único "corre extract" que solo arreglaba las traducciones que faltaban. Y apagar la revisión de faltantes ahora significa lo que dice: puedes buildear con texto sin traducir, y Verbaly sigue negándose a publicar una traducción que renderiza mal.
Un valor que falta dice de qué mensaje viene. El aviso imprimía solo el nombre del placeholder, una vez por nombre, así que el segundo mensaje con el mismo hueco se quedaba callado.
Añadido · 3
The doctor reports translation health: broken translations as an error, plural warnings as advice, in the same place it already reported the missing ones
The doctor explains a project with source scanning off, and stops guessing about orphan keys and types it cannot know about
One table maps your framework to the package that wires it, read by both verbaly init and verbaly doctor, covering Nuxt, Next.js, SvelteKit, Astro, Vite and the bundlers
Cambiado · 5
The gate and the coverage report read every catalog the way the runtime does, so nested groups are counted line by line instead of by group name
A tag is only checked when the message closes it, so ordinary text with angle brackets is free to be translated
Turning off the missing check no longer turns off the rest of the gate: unknown keys and broken translations still block the build
Every failure prints the step that repairs it, in the build gate and in verbaly check
A missing value warns once per message, naming it, instead of once per placeholder name
Corregido · 1
A stray invisible character made one compiler source file unreadable to git, which had been hiding its diffs since it was added
El gate del build deja de confiar en una traducción llena
Hasta ahora el check hacía una sola pregunta: ¿hay un valor para cada key? Una traducción podía dejar afuera tu placeholder de nombre, perder un tag de énfasis o aplanar un bloque de plural, y el build seguía en verde mientras producción mostraba en silencio el texto equivocado, o nada. Ahora el gate lee cada traducción contra su original y falla ante todo lo que no la puede mostrar, con el motivo en palabras simples y la línea del fuente para hacer clic. El runtime deja de callarse el mismo tipo de bug, y el paquete de React queda verificado contra Preact.
Destacados
El build ahora caza traducciones que están pero están rotas. Un placeholder que falta, un tag perdido, un bloque de plural convertido en texto plano, o un plural sin caso comodín: cada uno detiene el build con una frase que dice qué pasó y por qué importa. Antes de esto, un catálogo editado a mano no tenía ninguna revisión.
El bug del texto vacío se fue. Un bloque de plural que perdía su caso comodín no mostraba nada para cada cantidad que no listaba. En polaco, un mensaje con solo "one" y "few" mostraba una cadena vacía para 5 elementos. Eso ahora falla el build en vez de publicarse.
Verbaly le dice a quien traduce qué formas plurales necesita de verdad su idioma. Un catálogo polaco o ruso con solo dos formas recibe una advertencia que nombra las que faltan. Es un consejo, no un fallo: el texto igual se muestra. Las formas que ningún contador real alcanza nunca se piden, así que el español, el francés, el portugués y el italiano quedan en silencio.
verbaly status muestra un conteo de roto por idioma, así ves el estado de un catálogo sin correr el gate.
Un valor que falta ahora avisa en la consola. Mostrar un placeholder crudo porque nadie pasó el valor era el único caso de dato malo que Verbaly no reportaba. Todos los demás ya lo hacían.
Preact funciona, y ahora está probado. El paquete de React corre su suite completa una segunda vez con React resuelto a preact/compat: hooks, cambio de idioma, texto rico y render en servidor pasan todos. Sin paquete aparte y sin cambios en tu código.
VS Code puede mostrar tus traducciones al pasar el mouse. El README del repositorio trae la configuración de i18n-ally que convierte las keys con hash en texto legible mientras programas.
Añadido · 4
Structural validation of catalogs: every translation is read against its source, and each finding is either an error that fails the build or a warning that only prints
A broken count per language in verbaly status, in the text output and in --json
Preact verification in the React package: the same suite, run again through preact/compat
The i18n-ally editor setup in the README, so hashed keys read as real text in VS Code
Cambiado · 3
Imports and machine translations are held to the same stricter standard: a flattened plural block or one that would render empty is now rejected too
t() warns once when a value is missing instead of quietly leaving the placeholder in your text
The verbaly_missing agent tool reports broken translations and warnings, not only missing ones
Tu agente de código ya puede correr todo el ciclo de traducción
Un nuevo servidor MCP le da a Claude Code, Cursor y cualquier cliente MCP cuatro herramientas: ver la cobertura, listar lo que falta, extraer texto nuevo y traducir los huecos a máquina. Todo lo que el agente traduce sigue esperando tu revisión. Una Agent Skill pública y un llms.txt en la documentación completan el canal, los proyectos Nuxt mantienen su raíz limpia y los fragmentos de código que solo se muestran ya no pueden volverse claves fantasma.
Destacados
El ciclo de traducción como herramientas de agente. Un comando (claude mcp add verbaly -- npx -y @verbaly/mcp) y tu agente de código puede revisar la cobertura, listar traducciones faltantes, extraer texto nuevo y llenar los huecos con traducción máquina. Lo que traduzca se guarda como borrador, así que nada se publica sin que una persona diga que sí.
Una Agent Skill pública le enseña Verbaly a tu agente. Instala skills/verbaly del repositorio en tu proyecto y el agente conoce el ciclo de escribir, extraer, verificar y traducir, y las reglas que lo mantienen seguro.
La documentación ahora habla LLM. Este sitio publica un índice llms.txt, así los agentes que leen documentación se ubican en Verbaly sin raspar HTML.
Los proyectos Nuxt mantienen su raíz limpia. Los tipos generados ahora viven dentro de .nuxt/ y se registran solos, igual que ya lo hacía la integración de Astro. Ningún archivo en la raíz del proyecto y ningún cambio en tsconfig.
El código que solo se muestra ya no puede volverse una clave de traducción. Las páginas que enseñan ejemplos de t como texto visible en archivos Vue, Astro o Svelte corrían el riesgo de que esos fragmentos se extrajeran como claves reales. Ahora solo cuenta el código dentro de expresiones de verdad.
Añadido · 4
@verbaly/mcp: an MCP server with verbaly_status, verbaly_missing, verbaly_extract and verbaly_translate; failures come back as actionable messages, never a crashed server
A public Agent Skill in the repository (skills/verbaly), installable into any project
An llms.txt index on the docs site, generated from the real navigation
New compiler exports collectOrigins and resolveProvider for tooling built on the cycle
Cambiado · 2
The Nuxt module writes the generated types into .nuxt/ and registers them automatically; an explicit dts option still wins
Markup extraction in .vue, .astro and .svelte files counts only expressions; plain display text is never extracted
Gettext PO, placeholders protegidos y un runtime más sólido
El kit para traductores está completo: exporta e importa archivos gettext PO, y XLIFF ahora protege tus placeholders y tags para que un traductor no pueda romperlos por accidente. El nombre custom del sitemap en render por fin funciona, un crash raro con fechas inválidas desapareció, y el runtime ya no puede crecer en silencio por encima de su presupuesto de tamaño.
Destacados
Exporta e importa archivos gettext PO. verbaly export --format po escribe un archivo .po por idioma, listo para Poedit, Weblate o cualquier editor PO. verbaly import los lee de vuelta con la misma red de seguridad que todos los formatos: una traducción que rompe un placeholder se rechaza, no se publica. Las entradas que una herramienta marca como fuzzy cuentan como sin traducir.
Los traductores ya no pueden romper tus placeholders en XLIFF. Placeholders y tags viajan como códigos protegidos con nombres con sentido, así que las herramientas de traducción los muestran como fichas intocables en vez de texto editable. Las palabras de plural y select siguen editables a propósito: esas sí se traducen.
Los nombres custom de sitemap ya funcionan. Configurar un nombre propio para el sitemap en render se ignoraba: siempre escribía sitemap-i18n.xml. El nombre que configuras es el nombre que obtienes.
Un crash raro desapareció. Pasar una fecha inválida a un mensaje podía lanzar un error en vez de degradar con gracia. Lo encontró una suite nueva de tests aleatorios que castiga al parser con basura en cada corrida, así que esta clase de bug se queda arreglada.
El runtime ya no puede crecer en silencio. Cada push mide el bundle del core y falla si supera su presupuesto de tamaño. La promesa de ~3 KB la hace cumplir una máquina, no una costumbre.
Añadido · 4
verbaly export --format po and .po import: gettext PO with the catalog key as context, source locations as comments and fuzzy entries respected
XLIFF exports protect placeholders and tags as inline codes with semantic names; imports reconstruct them exactly
A size gate in CI: the core bundle is measured (min+gzip) on every push and the build fails over budget
Property-based tests: parser and formatter are exercised with random input on every run and must never crash
Corregido · 2
A custom sitemap filename in render was ignored and always wrote sitemap-i18n.xml
An invalid date passed to a message threw an error instead of degrading with a warning
Traducciones a máquina que revisas antes de confiar
Las traducciones a máquina ahora llegan como borradores que revisas, no como trabajo terminado en el que confías a ciegas. Un comando nuevo las aprueba, el gate de CI puede esperar a un humano, y el modelo traduce con contexto de tu código.
Destacados
La salida de la máquina es un borrador hasta que digas lo contrario. verbaly translate sigue llenando los huecos, pero todo lo que escribe queda marcado como borrador sin revisar. Tus catálogos quedan tan limpios como antes: el estado de revisión vive en un archivo .verbaly-drafts.json aparte, junto a ellos.
Aprueba las traducciones cuando las hayas leído. verbaly review lista todo lo que espera a un humano; verbaly review --approve lo acepta, todo o un idioma a la vez. Importar el archivo de un traductor también cuenta como revisión, así que la marca de borrador se limpia sola.
Bloquea merges con texto de máquina sin revisar. verbaly check --drafts hace fallar el CI mientras quede alguna traducción a máquina sin revisar. El verbaly check normal no cambia, así que nada se rompe hasta que decidas activarlo.
Mejores traducciones, porque el modelo recibe contexto. verbaly translate ahora le dice al provider en qué archivos fuente aparece cada texto, así el tono y el largo encajan donde el texto se usa.
Mira qué falta revisar de un vistazo. verbaly status (y --json) ahora reportan cuántos borradores sin revisar hay por idioma.
Añadido · 5
A .verbaly-drafts.json sidecar next to the catalogs records which keys are unreviewed machine translations; catalogs stay plain flat JSON
verbaly review: list drafts awaiting review; --approve accepts them, optionally per --locale
verbaly check --drafts: opt-in CI gate that also fails while unreviewed drafts remain
Translation requests carry the source files of each text, so providers translate with context
verbaly status (and --json) report the unreviewed draft count per locale
Cambiado · 3
verbaly translate marks everything it writes as a draft
verbaly import clears the draft flag for imported keys: a human file is reviewed by definition
Locale discovery ignores dotfile sidecars, so the drafts file never becomes a phantom locale
Soporte para Astro, un codemod wrap y anotaciones en CI
Astro se une a la familia: una línea conecta todo y el rendering por idioma corre solo. Un nuevo comando wrap envuelve tu texto JSX hardcodeado por ti, y los fallos de CI se vuelven anotaciones directamente en tus pull requests.
Destacados
Soporte para Astro, una línea. Agrega verbaly() a las integrations de astro.config y todo queda conectado: extracción en vivo mientras programas, claves type-safe y el gate del build. Las claves tipadas viven dentro de la carpeta .astro propia de Astro, así que ningún archivo extra aterriza en tu proyecto. En el flujo de espejo, verbaly render corre solo después de cada build.
Escribe tu texto directamente en archivos .astro. El mismo t`…` que ya usas en .js, .tsx, .svelte y .vue ahora funciona en el frontmatter y el markup de los componentes Astro.
¿Migrando una app existente? Deja que verbaly wrap haga la parte aburrida. Escanea tu JSX, encuentra el texto visible hardcodeado y lo envuelve en t`…` por ti. Primero reporta; nada se toca hasta que pases --write.
Los fallos de CI ahora apuntan a tu código. verbaly check --reporter github convierte cada traducción faltante en una anotación sobre el archivo y la línea exactos, directamente en el pull request.
verbaly status --json te da los números de cobertura en formato machine-readable, listos para badges y dashboards.
Añadido · 7
@verbaly/astro: a thin Astro integration wiring the Vite plugin and running verbaly render after static builds (opt-in, never on path-based i18n routing)
.astro extraction: frontmatter and markup, same strict scanner as .svelte/.vue; the default include covers the extension
verbaly wrap: JSX/TSX onboarding codemod with report-first flow, --write to apply, and a "needs a human" list for ambiguous cases
verbaly check --reporter github: failures as workflow annotations with source file and line; githubCheckAnnotations exported for tooling
verbaly status --json: machine-readable coverage
verbaly init detects Astro projects and points at the integration
A dts config option: move the generated types file anywhere, or turn it off with dts: false
Cambiado · 1
The Vite and unplugin transforms now respect the config include/exclude: only files the CLI would scan get rewritten, and include: [] turns source scanning off entirely
Corregido · 1
A raw NUL byte in a compiler source file made git treat it as binary (no reviewable diffs); replaced with the escape
RTL automático, nombres de idioma reales y contexto para traductores
Agregar árabe o hebreo ahora simplemente funciona: la dirección de la página sigue al idioma en todas partes. Los selectores de idioma reciben nombres reales de la plataforma, y los archivos de traducción dicen dónde vive cada texto.
Destacados
Los idiomas de derecha a izquierda simplemente funcionan. Agrega árabe, hebreo o persa y la dirección de la página sigue al idioma por su cuenta: cuando el usuario cambia, cuando el servidor renderiza y en las páginas estáticas pre-renderizadas. Sin atributo dir que administrar, nunca.
Selectores de idioma sin nombres hardcodeados. localeName('es') devuelve 'español' y localeName('de', 'en') devuelve 'German'. Los nombres vienen de la plataforma, así que cualquier locale funciona sin mantener una tabla.
¿Necesitas la dirección tú mismo? localeDirection('ar') devuelve rtl. Es el mismo helper que usa el resto del toolkit, exportado para tus propios layouts.
Los traductores ahora ven dónde vive cada texto. verbaly export marca cada mensaje con los archivos fuente de donde viene: XLIFF recibe notas de ubicación estándar y CSV una columna de ubicación, así que el contexto deja de ser adivinanza.
Añadido · 4
localeDirection(locale): ltr or rtl for any locale, script-aware, never throws
localeName(locale, displayIn?): localized language names via Intl.DisplayNames, endonym by default
Source-file locations in translator exports: XLIFF location notes and a CSV location column
%verbaly.dir% placeholder for SvelteKit's app.html, next to %verbaly.lang%
Cambiado · 3
switchLocale and persistLocale now set <html dir> alongside <html lang>
verbaly render writes dir on each mirrored page; the Nuxt module keeps it in sync live
CSV export header is now key,source,target,location; files in the old three-column shape still import fine
Llaves literales y páginas pre-renderizadas más seguras
Los mensajes rich por fin pueden mostrar una llave literal, y las páginas pre-renderizadas con verbaly render aplican las mismas reglas de seguridad que el navegador.
Destacados
Los mensajes ya pueden mostrar llaves literales. Escribe { y } en un mensaje rich y se renderizan como { y }. Las formas hex como { también funcionan, en el navegador, en cada <Trans> y en páginas pre-renderizadas.
Las páginas pre-renderizadas son ahora tan seguras como el navegador. Los atributos traducidos pasan por los mismos guards, así que los links inseguros y los atributos peligrosos de un catálogo nunca llegan al HTML estático.
Añadido · 2
Numeric character references ({, {) decode in rich text: bindDom, every <Trans> and verbaly render
safeAttribute(name, value): the attribute guard as a public core helper, shared by bindDom and verbaly render
Cambiado · 1
Internal: render link handling reuses normalizeLink, the same helper every adapter uses
Corregido · 1
verbaly render now applies the runtime attribute guards: unsafe URLs are blocked and style/srcdoc are never written from translations
Un <Trans> para todo, extracción en vivo y una vista de cobertura
Los componentes <Trans> de React, Vue y Svelte ahora comparten las mismas props y las mismas reglas de render, el CLI aprende dos comandos de uso diario y los últimos breaking changes planeados llegan antes de 1.0.
Destacados
<Trans> ya es el mismo componente en React, Vue y Svelte: mismas props, mismas reglas de render. Tu componente propio gana, luego los links nombrados, luego la whitelist segura de tags; lo desconocido degrada a texto plano.
Breaking: los <Trans> de React y Vue ahora renderizan tags whitelisteados como em y code como elementos reales, igual que Svelte y el intérprete de DOM. Pasa una lista richTags vacía para mantener el comportamiento anterior de aplanar a texto.
Breaking: @verbaly/svelte ahora requiere Svelte 5. A cambio <Trans> gana la prop components: mapea un tag del mensaje a tu propio componente. En Svelte 4, quédate en 0.22.0.
verbaly status muestra cuánto falta por traducir, una línea por idioma, sin hacer fallar nada. verbaly check sigue siendo el gate del build.
verbaly extract --watch mantiene catálogos y tipos al día mientras escribes código, pensado para setups de webpack, Rspack y Rollup donde el plugin de Vite no está disponible.
Dar feedback es más fácil: el README lleva los reportes de bugs y de fricción al lugar correcto, y los formularios de issues cubren los diez paquetes.
Añadido · 3
components prop on the Svelte <Trans>; instance and richTags props on React and Vue
verbaly status: per-language translation coverage, informational only
verbaly extract --watch: re-extracts as source files change, with the building block exported as watchProject
Cambiado · 3
React and Vue <Trans> render whitelisted tags as real elements (breaking; an empty richTags list restores the old behavior)
@verbaly/svelte requires Svelte 5 (breaking); the shipped components moved to runes
An extract that changes nothing no longer rewrites catalog files, so file watchers stay quiet
Tus catálogos se vuelven recursos nativos de mobile
El comando export aprende dos formatos mobile: carpetas de recursos de Android y archivos de strings de iOS. Una app web y su companion mobile ya pueden compartir un solo set de traducciones.
Destacados
Exporta tus traducciones para apps mobile: verbaly export --format android-xml escribe carpetas que puedes soltar en res/, y --format ios-strings escribe carpetas .lproj para Xcode.
Defaults sensatos por plataforma: tu idioma fuente se vuelve el default de la plataforma, y las keys sin traducir se omiten para que la app caiga en él en vez de mostrar texto vacío.
Catálogos más seguros: un catálogo con JSON roto ahora detiene el comando y nombra el archivo, en vez de leerse en silencio como vacío. Un BOM de Windows al inicio del archivo simplemente se tolera.
Añadido · 1
Two export formats in @verbaly/compiler: android-xml (Android resource layout, keys adapted to valid resource names) and ios-strings (one .lproj folder per locale)
Cambiado · 2
A corrupt catalog file fails with a clear error naming the path; before, it was read as empty and a later extract could have lost translations
--missing is rejected for the mobile formats with a message explaining why: mobile output already skips untranslated keys
Una limpieza a fondo de los diez paquetes, más opciones totalmente tipadas en tu config de Nuxt. Los internos quedaron más ordenados y un poco más pequeños, sin cambiar cómo escribes Verbaly.
Destacados
La key verbaly de nuxt.config.ts ahora está totalmente tipada: tienes autocompletado y detección de typos, y el módulo sigue sin sumar dependencias.
Un editor más tranquilo en dev. verbaly.d.ts solo se reescribe cuando su contenido realmente cambia, así tu servidor de TypeScript deja de recargar tipos en cada guardado.
Un runtime un poco más pequeño: el core tree-shaken ahora pesa 3.28 KB gzip, antes 3.30.
Tipos generados más limpios: un valor usado como fecha y como texto plano ya no aparece dos veces en verbaly.d.ts.
Mensajes del CLI más claros. Cada error y sugerencia se lee en palabras llanas, y check ahora imprime el texto fuente justo al lado de la key faltante.
Cambiado · 3
A quality pass across the whole codebase: shared plugin code lives in one place, dead code is gone, and hot paths are simpler, with the full test suite green
Breaking, advanced only: the compiler helpers generateDts and writeDts take the catalog object directly instead of a Map. The command line and every bundler plugin are unaffected
Dev tooling updated to the latest stable (tsdown, typescript-eslint)
Escribe tu texto también en archivos .svelte y .vue
El texto natural ya funciona dentro de componentes Svelte y Vue: escríbelo en el archivo, script o markup, y el compilador lo extrae con key estable y params tipados.
Destacados
Escribe t`Hello ${name}` directamente en un template .vue o {$t`Hello ${name}`} en markup .svelte. El compilador lo detecta como siempre lo hizo en archivos .ts: key estable, params tipados, catálogos por idioma.
La forma store de Svelte se entiende: $t`…`, $t('key') y $t.id('key')`…` funcionan en cualquier parte de un archivo .svelte.
En vivo en dev, con gate en build: guarda un componente con texto nuevo y los catálogos más verbaly.d.ts se actualizan al instante. Las traducciones faltantes siguen deteniendo el build.
Las keys legibles y las keys de runtime también funcionan en markup: t.id('home.title')`…` extrae bajo tu key, y las llamadas t('key') cuentan como keys usadas para check y prune.
verbaly extract escanea más por defecto: archivos .svelte y .vue, más la carpeta app/ junto a src/ (donde viven Nuxt 4 y el App Router de Next.js). Tu propio include sigue ganando.
Añadido · 2
SFC extraction in @verbaly/compiler: .svelte and .vue files are analyzed in full, script blocks and markup
@verbaly/vite and @verbaly/unplugin now transform .svelte/.vue sources
Cambiado · 2
Rewrites inside component markup use single quotes, so bindings like :title="t`…`" stay valid
Default include is now {src,app}/** and covers .svelte/.vue; explicit configs are untouched
Next.js se une a la familia: el ciclo está completo
La última gran integración de meta-framework. Las páginas del App Router de Next.js se renderizan ya traducidas, hidratan sin flash y funcionan en Turbopack y webpack.
Destacados
Nuevo paquete @verbaly/next. Dos pasos: envuelve tu config con withVerbaly() en next.config.ts y suelta <VerbalyProvider> en el layout raíz. Los Server Components traducen con await getT(); los Client Components siguen usando useT y <Trans> como en cualquier app React.
Funciona en Turbopack (el default de Next 16) y en webpack. next dev extrae tus mensajes mientras escribes, y next build se detiene si falta una traducción.
Cada visitante recibe su propio idioma, resuelto una vez por request desde su cookie o las preferencias del navegador. Las páginas llegan ya traducidas, nada parpadea, y los visitantes concurrentes nunca ven el idioma de otro.
useSwitchLocale() cambia el idioma al instante, recuerda la elección para la próxima visita y actualiza también los Server Components.
Los mensajes rich ya pueden mostrar markup literal, así un mensaje puede enseñar "<html lang>" como texto visible en vez de un escape roto.
Añadido · 2
@verbaly/next, the 10th package: the withVerbaly config wrapper, a server API (getT, getVerbaly, getRequestLocale, getVerbalyProps), a client provider with useSwitchLocale, and the compiler as a loader for both bundlers
loadMessages(locale) in virtual:verbaly: the raw catalog that SSR integrations send to the client
Corregido · 1
HTML entities decode inside rich text (core), so a message can display <html lang> literally and pre-rendered HTML round-trips cleanly
La segunda integración de meta-framework: un módulo de Nuxt sin config renderiza cada página en el idioma del visitante e hidrata sin flash.
Destacados
Nuevo paquete @verbaly/nuxt. Una línea en tu config (modules: ['@verbaly/nuxt']) conecta todo: extracción en vivo, idioma por visitante e hidratación con el mismo locale y catálogo que usó el servidor. Sin flash, sin mismatch.
Sin lock del framework: el módulo no depende ni de nuxt ni de @nuxt/kit, y está verificado contra una app Nuxt 4 real.
switchLocale() ahora es un export del core compartido por SvelteKit, Nuxt y setups a mano. Carga el catálogo primero y cambia después, así nada parpadea. Los imports existentes siguen funcionando.
Las apps client-only también están cubiertas: con ssr: false el módulo igual elige el idioma correcto en el navegador.
Apoya a Verbaly: GitHub Sponsors ya está activo en github.com/sponsors/AronSoto.
Añadido · 2
@verbaly/nuxt, the 9th package: zero-config module plus a runtime plugin (per-request language, payload hydration, reactive <html lang>)
switchLocale / SwitchLocaleOptions (core)
Cambiado · 1
@verbaly/sveltekit re-exports switchLocale from core, with identical API and behavior
La release de hardening: una auditoría completa de los ocho paquetes corrigió cada borde afilado que encontró, y SSR gana su pieza faltante.
Destacados
La traducción de atributos ahora es XSS-safe de punta a punta: data-verbaly-attr sanitiza atributos de URL y bloquea style y srcdoc por completo, así un catálogo comprometido no puede inyectar scripts.
t() nunca crashea por argumentos de formato malos: un código de moneda, fecha o estilo de hora inválido avisa una vez y renderiza el valor plano.
createRequestInstance(locale) en virtual:verbaly: el patrón SSR per-request en una llamada, con el catálogo ya cargado y sin flash.
El CLI ahora rechaza flags mal puestas. verbaly translate --locale es antes traducía todos los idiomas en silencio; ahora da error y sugiere --locales. Además --dry-run para extract --prune.
normalizeLink() y resolveRequestLocale() ahora viven una sola vez en el core, compartidos por cada adapter e integración SSR.
Breaking:instance.inspect(key) ahora devuelve { from, source } en lugar de { locale, source }: un solo nombre para un solo concepto en toda la API de observabilidad.
Añadido · 2
normalizeLink, resolveRequestLocale, LOCALE_STORAGE_KEY (core) and createRequestInstance (virtual:verbaly)
failOnMissing opt-out for the Vite build gate; --dry-run on verbaly extract
La primera integración de meta-framework: las páginas de SvelteKit se renderizan en el idioma del visitante en el servidor e hidratan sin flash.
Destacados
Nuevo paquete @verbaly/sveltekit. SSR en tres cables: verbalyHandle() en tus hooks de servidor, un load de +layout y %verbaly.lang% en app.html. Las páginas llegan traducidas.
El idioma se decide por request: cookie primero, luego el header Accept-Language (es-PE matchea es), luego tu fallback. Los requests concurrentes nunca filtran el idioma de otro.
switchLocale(): un switch client-side que carga el catálogo primero y luego guarda la elección en la cookie que el servidor lee.
negotiateLocale() en el core: el matcher de Accept-Language es un export público que cualquier servidor Node puede usar.
createInstance() en virtual:verbaly: instancias frescas por request sobre tus catálogos compilados, más los exports locales y sourceLocale.
Añadido · 2
@verbaly/sveltekit, the 8th package: verbalyHandle + switchLocale
negotiateLocale (core) and SSR exports in virtual:verbaly
Cambiado · 1
The Release workflow now resumes per package after a partial run
El ciclo write→ship se abre a humanos: exporta archivos listos para traductores, impórtalos de vuelta validados.
Destacados
verbaly export escribe un archivo listo para traductores por idioma: XLIFF 2.0 para plataformas de traducción, CSV para hojas de cálculo. --missing exporta solo lo que falta por traducir.
verbaly import valida cada entrada: una traducción que pierde un placeholder, una variante o un tag se rechaza y se reporta.
Las traducciones existentes se conservan salvo que pases --overwrite, y --dry-run previsualiza el import completo.
No necesitas plataforma de traducción: los catálogos son JSON plano, y Crowdin, Lokalise y Phrase lo leen nativo.
Añadido · 2
verbaly export: translator-ready XLIFF 2.0 / CSV files per locale
verbaly import: a validated round-trip back into your catalogs
Cambiado · 2
GitHub Release notes are now short: theme, highlights and a link to the full changelog
Community docs pass: SECURITY and CONTRIBUTING read friendlier
Links nombrados con hrefs que nunca salen de los catálogos, y los últimos formatters de Intl que faltaban.
Destacados
Los links nombrados en mensajes rich renderizan elementos <a> reales. El href sale de tu código, nunca del catálogo, y los schemes inseguros se bloquean.
Tiempo relativo ({when:relative}), listas ({xs:list}) y unidades CLDR ({n:unit/kilometer}): todo Intl nativo, cero dependencias.
Licencia: Apache-2.0 → MIT en los siete paquetes.
Añadido · 3
richLinks + data-verbaly-links + links prop on every <Trans>