<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Verbaly releases</title>
    <link>https://verbaly-web.vercel.app/changelog/</link>
    <atom:link href="https://verbaly-web.vercel.app/changelog/rss.xml" rel="self" type="application/rss+xml" />
    <description>Write natural text, ship type-safe translations. Compiler-powered i18n: zero config, tree-shakeable locales, and CI that blocks missing strings.</description>
    <language>en</language>
    <copyright>MIT</copyright>
    <lastBuildDate>Fri, 28 Aug 2026 12:00:00 GMT</lastBuildDate>
    <item>
      <title>0.45.0: A catalog only grows, and every page was paying for all of it</title>
      <link>https://verbaly-web.vercel.app/changelog/#v0-45-0</link>
      <guid isPermaLink="false">verbaly-0.45.0</guid>
      <pubDate>Fri, 28 Aug 2026 12:00:00 GMT</pubDate>
      <description><![CDATA[<p>Text that lives on one page, a list of releases, a blog archive, a long list of questions, was downloaded by every visitor on every page. This version lets you name those groups of messages and keep them in the build. The pages that use them still come out translated, because that text is already written into the HTML, and the browser stops downloading it. Looking for the proof also uncovered a real defect underneath. After its first pass, Verbaly could replace correct translated text on the page with the message’s internal name whenever a message was missing. That is fixed here, and it matters to anyone who pre-renders their site.</p><ul><li><strong>Text that only one page needs stops travelling to all of them.</strong> Name that group of messages in your config and the browser no longer downloads it. The pages that use it still come out translated, because that text is already in the HTML.</li><li>Pre-rendered text is never destroyed any more. If a message is missing, Verbaly keeps the text that is already on the page instead of replacing it with the message’s internal name. Before, that could happen a moment after a page loaded.</li><li>Measured on this site: the file every page loads went from 46.3 KB to 33.5 KB compressed, and the Spanish and Portuguese trees dropped about 14 KB each. Nothing was lost on screen.</li><li>Nothing changes if you do not switch it on. It is a new option, and a project that does not write it behaves exactly as before.</li><li>The build tells you when it looks wrong, naming a group that matches nothing in your catalog, or one your code reads directly.</li></ul><p><a href="https://github.com/AronSoto/verbaly/releases/tag/v0.45.0">Full release notes</a></p>]]></description>
    </item>
    <item>
      <title>0.44.0: Our own checks were measuring the machine, not the code</title>
      <link>https://verbaly-web.vercel.app/changelog/#v0-44-0</link>
      <guid isPermaLink="false">verbaly-0.44.0</guid>
      <pubDate>Fri, 28 Aug 2026 12:00:00 GMT</pubDate>
      <description><![CDATA[<p>The job that checks our test coverage was failing at random, and the cause was not a slow test. A test that runs in 22 milliseconds was being failed by a 5 second limit, because that limit counts real time and our test command starts twelve processes on the same processor cores. Twice, that blocked a release. This version fixes it at the root, and spends the rest of its time on the same idea applied to our supply chain: what our build is allowed to do, which tools it runs exactly, and code that could never run at all. Nothing about the package changes.</p><ul><li><strong>Nothing you install changes.</strong> The runtime is identical to 0.43.0 and every published API behaves the same. This release is about the machinery that checks us.</li><li>Our build stopped failing at random. Our test suite could fail a perfectly healthy test just because the machine was busy, and twice that blocked a release.</li><li>Every tool our build runs is pinned to an exact commit. A tag can be moved by whoever owns it, but a commit cannot, so nothing can quietly change what builds your package.</li><li>The job that publishes releases asks for write access only where it needs it, instead of granting it to the whole workflow.</li><li>Static analysis runs on every change, and its configuration now lives in our repository, pinned and readable, instead of being a setting only we could see.</li></ul><p><a href="https://github.com/AronSoto/verbaly/releases/tag/v0.44.0">Full release notes</a></p>]]></description>
    </item>
    <item>
      <title>0.43.0: Everything we say about ourselves is true again</title>
      <link>https://verbaly-web.vercel.app/changelog/#v0-43-0</link>
      <guid isPermaLink="false">verbaly-0.43.0</guid>
      <pubDate>Thu, 27 Aug 2026 12:00:00 GMT</pubDate>
      <description><![CDATA[<p>The badge on our README has said ~3KB since the beginning, while the runtime was actually 3.68 KB. Rather than weaken the claim, this release removes the last piece heavy enough to be worth removing: relative time formatting is 318 bytes that most apps never write, and it now travels only when one of your messages asks for it. The rest of the release is the same idea pointed at everything else the project says about itself.</p><ul><li><strong>The runtime is 3.00 KB.</strong> Relative time formatting joins ICU as something that ships only when one of your messages actually uses it, and there is nothing for you to configure.</li><li>A formatter you write yourself now knows which message it was called for. It receives the parameter name and the message key, so its own errors can say where they happened, which was not possible before.</li><li>The security policy covers all twelve packages, not the seven that existed when it was written, and it now explains how to verify that a release really came from our CI.</li><li>Our security posture is something you can check instead of something we claim: a score from the OpenSSF runs on every change and the badge on the README reads it.</li></ul><p><a href="https://github.com/AronSoto/verbaly/releases/tag/v0.43.0">Full release notes</a></p>]]></description>
    </item>
    <item>
      <title>0.42.0: A smaller runtime for everyone who never needed ICU</title>
      <link>https://verbaly-web.vercel.app/changelog/#v0-42-0</link>
      <guid isPermaLink="false">verbaly-0.42.0</guid>
      <pubDate>Thu, 27 Aug 2026 12:00:00 GMT</pubDate>
      <description><![CDATA[<p>Verbaly’s own syntax covers plurals, selects and formats, and ICU message syntax is the way out for what it does not. That escape hatch was always described as something you opt into, and it never was: the parser went into every app whether or not a single message used it. Our own documentation site has no ICU messages at all and shipped it on every page. Verbaly already reads your catalogs, so now it only sends the parser where a message actually needs one.</p><ul><li><strong>The runtime is 11% smaller for anyone who does not use ICU syntax</strong>, which is most projects. The parser only ships when one of your messages actually needs it, and there is nothing for you to switch on.</li><li>Nothing changes if you do use ICU. Verbaly sees it in your catalogs and brings the parser along, exactly as before.</li><li>A message that reaches your app after the build and uses ICU now shows its own text and says why, instead of half-rendering into something that looks almost right. If your text comes from a CMS, one setting ships the parser anyway.</li></ul><p><a href="https://github.com/AronSoto/verbaly/releases/tag/v0.42.0">Full release notes</a></p>]]></description>
    </item>
    <item>
      <title>0.41.0: One line to switch, and your choice finally sticks</title>
      <link>https://verbaly-web.vercel.app/changelog/#v0-41-0</link>
      <guid isPermaLink="false">verbaly-0.41.0</guid>
      <pubDate>Thu, 27 Aug 2026 12:00:00 GMT</pubDate>
      <description><![CDATA[<p>Verbaly shipped a complete language switcher for one way of doing addresses and a spare part for the other, so every project wrote the second one by hand, ours included. Rewriting our own site on the library’s own helper found a bug that had been there since the switcher moved into the runtime: someone who picked a language was sent back to their browser’s language on the next page load. The other half is that Verbaly had started assuming you wanted the language in your addresses; now it reads your setup instead of guessing.</p><ul><li>Switching language is one line now, and it is the same line whichever way your addresses work. It goes to the other address when the language lives there, and changes the text where it stands when it does not.</li><li><strong>Your choice of language survives the next page load.</strong> Picking English on a Spanish page could send you straight back to Spanish, because the switch remembered your choice in one place and the page that greets you read another.</li><li>Verbaly stops assuming you want the language in your addresses. It reads your setup: build a version of your site per language and you get addresses per language, otherwise one address serves all of them. You only write it down to disagree.</li><li><code>verbaly doctor</code> tells you whether it read your choice or worked it out, so a setting nobody wrote down never looks like a decision somebody made.</li><li>The size check now measures what a real app actually ships, instead of only the smallest possible import and the largest impossible one.</li></ul><p><a href="https://github.com/AronSoto/verbaly/releases/tag/v0.41.0">Full release notes</a></p>]]></description>
    </item>
    <item>
      <title>0.40.0: Say where the language lives, and translate the head too</title>
      <link>https://verbaly-web.vercel.app/changelog/#v0-40-0</link>
      <guid isPermaLink="false">verbaly-0.40.0</guid>
      <pubDate>Thu, 27 Aug 2026 12:00:00 GMT</pubDate>
      <description><![CDATA[<p>Verbaly has had two ways to carry a language for a long time and never named either of them, so which one you got was decided by whether you remembered to pass an optional field. Naming them turned up the thing that made the naming matter: a page could be translated everywhere a visitor looks and still tell a search engine it is in English, because the title and the description live in the head where nobody reads them. Our own documentation shipped 27 of 27 pages that way.</p><ul><li>You can now say where the language lives in your addresses, once, in the config: <code>/docs</code> and <code>/es/docs</code>, a prefix on every language, or one address for all of them. Before this the answer depended on which helper you called and what you passed it.</li><li>The language switcher is one line and cannot be written wrong. Verbaly hands you a helper that already knows your languages, your source and your mode, and the same line keeps working if you change your mind about the mode.</li><li>Your page title and description get translated too. They live in the head, nobody reads them on the page, and they are what a search result shows, so a page could look fully translated and still be listed in English.</li><li><code>verbaly render</code> counts the pages whose title still never changes and tells you once, with the fix. It never fails a build: a product name is allowed not to translate.</li><li><code>verbaly doctor</code> tells you which mode your addresses are in, and says so when your settings ask for two things that cannot both be true.</li><li><strong>Breaking:</strong> asking for a path without naming your source language no longer compiles. It used to put a prefix on every language including your own, quietly, which was almost never what anyone wanted.</li></ul><p><a href="https://github.com/AronSoto/verbaly/releases/tag/v0.40.0">Full release notes</a></p>]]></description>
    </item>
    <item>
      <title>0.39.0: Translating survives a bad connection, and agents get the whole cycle</title>
      <link>https://verbaly-web.vercel.app/changelog/#v0-39-0</link>
      <guid isPermaLink="false">verbaly-0.39.0</guid>
      <pubDate>Thu, 27 Aug 2026 12:00:00 GMT</pubDate>
      <description><![CDATA[<p>Verbaly has let a coding agent run it for a while, but only four of its twelve commands were ever handed over, and the two missing ones are the first two an agent needs: finding out what is wrong with a project, and wrapping the text already written in it. Looking at that turned up something worse next door. <code>verbaly translate</code> is the one command that costs money and minutes, and a single bad answer threw the whole run away: batches that had already been paid for and translated were never written to disk.</p><ul><li><code>verbaly translate</code> no longer loses work it already paid for. A request that does not come back is tried again, and if it still fails the run keeps going and saves everything else. Running it again asks only for what is left, instead of paying for all of it a second time.</li><li>Translating is about four times faster. Requests now go out together instead of one after another, and a long run tells you what has landed while it runs rather than staying silent for minutes.</li><li>You can say what must never be translated. Put your product names and any wording you have settled on in <code>translate.glossary</code>, and how you address the reader in <code>translate.instructions</code>, instead of fixing the same word by hand after every run.</li><li>Your coding agent gets the two tools it was missing: one to work out why a project is not working, and one to find text written straight into your components and wrap it. Bringing an existing app to Verbaly no longer needs a terminal.</li><li>Agents read numbers now, not sentences. Every tool answers with plain data as well as text, so an agent stops guessing at wording that can change between releases.</li><li>Accepting a machine translation is still something only a person can do, and that has not changed.</li></ul><p><a href="https://github.com/AronSoto/verbaly/releases/tag/v0.39.0">Full release notes</a></p>]]></description>
    </item>
    <item>
      <title>0.38.0: Translations written as groups are safe everywhere</title>
      <link>https://verbaly-web.vercel.app/changelog/#v0-38-0</link>
      <guid isPermaLink="false">verbaly-0.38.0</guid>
      <pubDate>Sat, 15 Aug 2026 12:00:00 GMT</pubDate>
      <description><![CDATA[<p>You can keep your translations as nested groups instead of one long flat list, and Verbaly has read that shape for a while. Only half of it did: the commands that write your catalogs read the group names as if they were messages. So <code>extract</code> added a second copy of every message, <code>--prune</code> removed whole groups with every translation inside them, exports reached your translator broken, and <code>translate</code> said there was nothing to do about an app that was half untranslated. Nothing changes for a flat catalog.</p><ul><li>Translations kept in groups are safe from end to end. Every command now reads and writes them the same way, and your file keeps the shape you gave it instead of being flattened behind your back.</li><li><code>verbaly extract --prune</code> no longer deletes translations. On a catalog written as groups it read each group name as one message, did not recognise it, and removed the whole group with every translation inside it.</li><li>Your translator files stop arriving broken. Exporting wrote <code>[object Object]</code> where the text should have been, and <code>verbaly translate</code> reported nothing to do while <code>verbaly status</code> said half the app was untranslated.</li><li>A language you add later matches the rest. A new catalog takes the shape of the ones you already have, so a project never ends up half grouped and half flat.</li><li>The tool counts in real English. It used to say <code>1 messages</code>, <code>1 locales</code> and <code>1 pages</code> in nine places.</li><li>A repeated warning says itself once. A key collision was announced three times in a single <code>verbaly extract</code>, one of them after the summary had already printed.</li></ul><p><a href="https://github.com/AronSoto/verbaly/releases/tag/v0.38.0">Full release notes</a></p>]]></description>
    </item>
    <item>
      <title>0.37.0: Your saved language survives a refresh</title>
      <link>https://verbaly-web.vercel.app/changelog/#v0-37-0</link>
      <guid isPermaLink="false">verbaly-0.37.0</guid>
      <pubDate>Mon, 10 Aug 2026 12:00:00 GMT</pubDate>
      <description><![CDATA[<p>The last version named the two ways a site can carry its language. Writing the page that explains them turned up the reason one of them had never really worked: if your app keeps one address per page and loads languages on demand, Verbaly knew which language the visitor wanted and simply never fetched it. One line, and the example in our own docs had been showing it all along.</p><ul><li>A saved language now survives a refresh on a site that keeps one address. A returning visitor used to get the source language back every time, with nothing in the console to explain it.</li><li>Nothing changes if your language is in the address, which is what the last two versions were about. This was only ever broken where the language comes from storage or the browser.</li><li>A warning that used to give impossible advice now gives the right one. When your page says one language and Verbaly is in another, it told you to read the language from the address. On a site that keeps one address there is nothing to read.</li><li>React, Vue and Svelte were each checked against a language that arrives after the page is already on screen. That used to be a rare path and is now the normal one.</li></ul><p><a href="https://github.com/AronSoto/verbaly/releases/tag/v0.37.0">Full release notes</a></p>]]></description>
    </item>
    <item>
      <title>0.36.0: Your visitors reach their language on their own</title>
      <link>https://verbaly-web.vercel.app/changelog/#v0-36-0</link>
      <guid isPermaLink="false">verbaly-0.36.0</guid>
      <pubDate>Sun, 09 Aug 2026 12:00:00 GMT</pubDate>
      <description><![CDATA[<p>Verbaly publishes your site in every language, each at its own address. The last version made those pages right, and this one is about getting people to them, and about asking from your own code which language a page belongs to. Both were things every project had to write by hand, this site twice in one day. It also fixes a way a page name could be mistaken for a language.</p><ul><li>Asking which language a page is in no longer needs a helper of your own. Verbaly answers with the language in the address, or with nothing when the address does not carry one. That is a different question from what a visitor prefers, and mixing the two is what let a saved preference paint the wrong language over a page.</li><li>Verbaly can send a visitor to their language now. Turn it on and someone landing on your home page goes to your site in their language before anything is drawn. Only the home page, so a search engine still reaches every page of your site directly.</li><li>A site served from a subfolder works. Tell Verbaly the folder and the links inside each language point where they should, instead of quietly pointing nowhere.</li><li>A page called /es-la-guia is a page, not Spanish. Verbaly read the start of an address a little too eagerly, so a page whose name began like a language code could be translated into it. Now only a real language code counts.</li><li>What this version does not change: an app with one address per page works exactly as before. Everything here is for sites where each language has its own address.</li></ul><p><a href="https://github.com/AronSoto/verbaly/releases/tag/v0.36.0">Full release notes</a></p>]]></description>
    </item>
    <item>
      <title>0.35.0: Your pre-translated pages stop flickering</title>
      <link>https://verbaly-web.vercel.app/changelog/#v0-35-0</link>
      <guid isPermaLink="false">verbaly-0.35.0</guid>
      <pubDate>Sat, 08 Aug 2026 12:00:00 GMT</pubDate>
      <description><![CDATA[<p>Verbaly publishes your static site in every language, under /es/ and /pt/. This version is about what happened to someone who reached one of those pages: it arrived in Spanish and then flashed to English before settling back, nothing told the runtime it was already on a Spanish page, and every link pointed back out. The page was right and everything between it and the visitor was not.</p><ul><li>A pre-translated page stops flashing into English. Verbaly served the Spanish page and then painted the English text over it until the Spanish catalog finished loading, on exactly the pages this feature exists to keep still.</li><li>Landing on /es/ is now enough. Verbaly reads the prefix it wrote itself before it looks at storage or at the browser, because the page you are looking at is already in that language.</li><li>Each language stops being an island. Its links pointed at the site root, so arriving in Spanish and clicking once took you back to English. Links to pages that language also has now stay inside it.</li><li>Your language switcher sends someone to your site in that language. That is the address they can share and the one search engines index, and the page arrives already translated so nothing flickers. In an app with one address per page, changing the language still just changes the text, exactly as before.</li><li>What this version does not do: it does not route anyone anywhere. Someone who lands on your root is being served the source language, and sending them to their language is one rule in your host settings.</li></ul><p><a href="https://github.com/AronSoto/verbaly/releases/tag/v0.35.0">Full release notes</a></p>]]></description>
    </item>
    <item>
      <title>0.34.0: What the build gate cannot see</title>
      <link>https://verbaly-web.vercel.app/changelog/#v0-34-0</link>
      <guid isPermaLink="false">verbaly-0.34.0</guid>
      <pubDate>Sat, 08 Aug 2026 12:00:00 GMT</pubDate>
      <description><![CDATA[<p>The build gate refuses to build when a translation cannot render what your source renders, but it only ever sees what is in your project at build time. This version goes after everything that arrives later: a catalog that comes from a content system, a source file Verbaly could not read, and a line break inside a rich message that the pre-rendered page and the browser disagreed about. Four real bugs, each one a way to ship something wrong with every command green.</p><ul><li>A line break inside a rich message no longer doubles in a pre-rendered page. Verbaly wrote it in a way that browsers read as two line breaks, and the page then replaced them with one as it loaded. A visible jump, on exactly the pages that feature exists to keep still.</li><li>A translation that arrives as null no longer takes your app down when it starts. A field nobody filled in a content system used to stop everything before the first render. Now that one key is skipped, the app runs, and the console tells you which one it was.</li><li>The commands survive a file they cannot read. One file with a syntax error used to end the whole run with a position and no file name. Now the file is named, its texts are skipped, and every other file is still read.</li><li>Text inside JSX written in a .js file is found like anywhere else. A React app with components in .js could not run the extract command at all, because the first tag ended it.</li><li>A warning no longer repeats itself because a number changed. Three of them printed the value that caused them, so a counter going up could fill memory with copies of the same message.</li></ul><p><a href="https://github.com/AronSoto/verbaly/releases/tag/v0.34.0">Full release notes</a></p>]]></description>
    </item>
    <item>
      <title>0.33.0: The public API becomes a decision</title>
      <link>https://verbaly-web.vercel.app/changelog/#v0-33-0</link>
      <guid isPermaLink="false">verbaly-0.33.0</guid>
      <pubDate>Wed, 29 Jul 2026 12:00:00 GMT</pubDate>
      <description><![CDATA[<p>The compiler package let you import 132 different things, and not one of them was written down anywhere. They had piled up as whatever the framework packages happened to need, and at version 1.0 every single one would have become a promise nobody had ever reviewed. This version reads the whole list against a written rule, publishes the 60 that survive it, documents them, and adds a test that fails if the list ever grows by accident.</p><ul><li>If you use the command line or any of the framework packages, this release changes nothing for you. No config, no imports, nothing to edit. Everything here happens inside a package that only custom integrations reach into.</li><li>The compiler package now documents what you are allowed to import, in two groups: the types your own config file and your own translation provider need, and the toolkit for building an integration we do not ship. Everything else is internal and now says so.</li><li>The list went from 132 names to 60. Each one stayed because a real integration uses it, because it is a documented way to extend Verbaly, or because another one needs it. The 72 that matched none of those are gone.</li><li>A build plugin no longer receives the internal parse tree Verbaly reads your code with. It used to get the whole thing just to read two fields off it, and now it gets the messages it found. One less thing that can break when the parser changes.</li><li>Both packages fail their own test if the list grows, so adding something public is now a deliberate choice someone reviews, instead of something that happens on the way to fixing something else.</li></ul><p><a href="https://github.com/AronSoto/verbaly/releases/tag/v0.33.0">Full release notes</a></p>]]></description>
    </item>
    <item>
      <title>0.32.0: Nothing breaks in silence</title>
      <link>https://verbaly-web.vercel.app/changelog/#v0-32-0</link>
      <guid isPermaLink="false">verbaly-0.32.0</guid>
      <pubDate>Wed, 29 Jul 2026 12:00:00 GMT</pubDate>
      <description><![CDATA[<p>Verbaly already refused to build when a translation could not render what your source renders. This version closes the same story from the other end: every remaining place where Verbaly quietly did the safe thing now tells you, at the moment you can act on it. A plural that renders an empty line, a price with no currency, a block written where it cannot work, and a name imported from somewhere it does not exist: four ways to ship the wrong text with everything green.</p><ul><li>A plural without a catch-all case now warns in the browser, not only in the build. The build check already refused it, but a catalog that arrives from a lazy load or from a content system never goes through the build, so the empty line could still reach a real page. Now it names the message and tells you what to add.</li><li>A placeholder missing its argument stops rendering as a bare number. A price with no currency code, a measure with no unit, a date with no time unit: each one used to print the plain value and say nothing. Each one now says what it needs and shows the shape of the fix.</li><li>Writing a plural inside a <code>t</code> template no longer ships with the braces visible. In that template the values come from your code, so a plural or a format written there becomes text your users see. <code>verbaly extract</code> and <code>verbaly doctor</code> now point at the file and the exact text.</li><li>Importing <code>t</code> from the package finally gets an answer that mentions Verbaly. <code>t</code> comes from your instance or from the generated module, never from the package, and until now only the bundler complained, in a message that gave no hint where <code>t</code> should come from.</li><li>Every error from the command line prints its name once. Every failure used to read <code>[verbaly] [verbaly]</code>.</li></ul><p><a href="https://github.com/AronSoto/verbaly/releases/tag/v0.32.0">Full release notes</a></p>]]></description>
    </item>
    <item>
      <title>0.31.0: The tooling stops contradicting itself</title>
      <link>https://verbaly-web.vercel.app/changelog/#v0-31-0</link>
      <guid isPermaLink="false">verbaly-0.31.0</guid>
      <pubDate>Tue, 28 Jul 2026 12:00:00 GMT</pubDate>
      <description><![CDATA[<p>One command called a project healthy while another blocked its build over the same files. The doctor rejected catalogs written as nested groups, even though the runtime has always read them, and told an Astro or Nuxt app to install the package the setup command had just told it not to. The build gate read any word in angle brackets as markup and failed correct translations over it. All of that is now one story: what a command tells you is what your project is.</p><ul><li><code>verbaly doctor</code> stops disagreeing with <code>verbaly check</code>. A translation that is there but broken is now an error in the doctor too, so the command you run when something looks wrong can no longer answer "setup looks healthy" about a project whose build fails.</li><li>Catalogs written as nested groups work everywhere. The doctor used to reject them as invalid, and the gate only compared the group names, so a group with one untranslated line inside counted as fully translated. Now every line is checked on its own, the way the runtime already read it.</li><li>The doctor points at the package your project actually needs: Astro, Nuxt, Next.js, SvelteKit, Vite or the bundler plugin. It is the same answer <code>verbaly init</code> gives, because both now read one table.</li><li>The build stops failing over text that only looks like markup. "Press &lt;Enter&gt; to continue" translated as "Pulsa &lt;Intro&gt; para continuar" was reported as two broken tags. A tag counts as structure only when the message closes it, so your prose stays prose and a real emphasis tag or a link is still protected.</li><li>Each failure names the step that repairs it, instead of one "run extract" that only ever fixed missing translations. And turning off the missing check now means what it says: you can build with untranslated text, and Verbaly still refuses to ship a translation that renders wrong.</li><li>A missing value says which message it came from. The warning used to print the placeholder name alone, once per name, so the second message with the same gap stayed silent.</li></ul><p><a href="https://github.com/AronSoto/verbaly/releases/tag/v0.31.0">Full release notes</a></p>]]></description>
    </item>
    <item>
      <title>0.30.0: The build gate stops trusting a filled-in translation</title>
      <link>https://verbaly-web.vercel.app/changelog/#v0-30-0</link>
      <guid isPermaLink="false">verbaly-0.30.0</guid>
      <pubDate>Tue, 28 Jul 2026 12:00:00 GMT</pubDate>
      <description><![CDATA[<p>Until now the check asked one question: is there a value for every key? A translation could drop your name placeholder, lose an emphasis tag or flatten a plural block, and the build stayed green while production quietly showed the wrong text, or nothing at all. Now the gate reads every translation against its source and fails on anything that cannot render it, with the reason in plain words and the source line to click. The runtime stops being silent about the same kind of bug, and the React package is verified against Preact.</p><ul><li>The build now catches translations that are there but broken. A missing placeholder, a lost tag, a plural block turned into plain text, or a plural set with no catch-all case: each one stops the build with a sentence saying what happened and why it matters. Before this, a catalog you edited by hand had no check at all.</li><li>The empty-text bug is gone. A plural block that lost its catch-all case rendered nothing at all for every count it did not list. In Polish, a message with only "one" and "few" showed an empty string for 5 items. That fails the build now instead of shipping.</li><li>Verbaly tells a translator which plural forms their language actually needs. A Polish or Russian catalog carrying only two forms gets a warning naming the missing ones. It is advice, not a failure: the text still renders. Forms no real counter ever reaches are never asked for, so Spanish, French, Portuguese and Italian stay quiet.</li><li><code>verbaly status</code> shows a broken count per language, so you can see the state of a catalog without running the gate.</li><li>A missing value now warns in the console. Rendering a raw placeholder because nobody passed the value was the one bad-data case Verbaly did not report. Every other one already did.</li><li>Preact works, and now it is proven. The React package runs its whole suite a second time with React resolved to <code>preact/compat</code>: hooks, language switching, rich text and server rendering all pass. No separate package, no changes to your code.</li><li>VS Code can show your translations on hover. The repository README carries the i18n-ally setup that turns hashed keys into readable text while you code.</li></ul><p><a href="https://github.com/AronSoto/verbaly/releases/tag/v0.30.0">Full release notes</a></p>]]></description>
    </item>
    <item>
      <title>0.29.0: Your coding agent can now run the whole translation cycle</title>
      <link>https://verbaly-web.vercel.app/changelog/#v0-29-0</link>
      <guid isPermaLink="false">verbaly-0.29.0</guid>
      <pubDate>Mon, 27 Jul 2026 12:00:00 GMT</pubDate>
      <description><![CDATA[<p>A new MCP server gives Claude Code, Cursor and any MCP client four tools: see the coverage, list what is missing, extract new text and machine-translate the gaps. Everything the agent translates still waits for your review. A public Agent Skill and an llms.txt on the docs complete the channel, Nuxt projects keep their root clean, and displayed code snippets can no longer become phantom keys.</p><ul><li>The translation cycle as agent tools. One command (<code>claude mcp add verbaly -- npx -y @verbaly/mcp</code>) and your coding agent can check coverage, list missing translations, extract new text and fill the gaps with machine translation. Anything it translates is saved as a draft, so nothing ships without a human saying yes.</li><li>A public Agent Skill teaches your agent Verbaly. Install <code>skills/verbaly</code> from the repository into your project and the agent knows the write, extract, check, translate cycle and the rules that keep it safe.</li><li>The docs now speak LLM. This site publishes an <code>llms.txt</code> index, so agents that read documentation find their way around Verbaly without scraping HTML.</li><li>Nuxt projects keep their root clean. The generated types now live inside <code>.nuxt/</code> and register themselves, exactly like the Astro integration already did. No file at the project root and no tsconfig edits.</li><li>Displayed code can no longer become a translation key. Pages that show <code>t</code> examples as visible text in Vue, Astro or Svelte files used to risk those snippets being extracted as real keys. Now only code inside real expressions counts.</li></ul><p><a href="https://github.com/AronSoto/verbaly/releases/tag/v0.29.0">Full release notes</a></p>]]></description>
    </item>
    <item>
      <title>0.28.0: Gettext PO, protected placeholders and a sturdier runtime</title>
      <link>https://verbaly-web.vercel.app/changelog/#v0-28-0</link>
      <guid isPermaLink="false">verbaly-0.28.0</guid>
      <pubDate>Mon, 20 Jul 2026 12:00:00 GMT</pubDate>
      <description><![CDATA[<p>The translator toolkit is now complete: export and import gettext PO files, and XLIFF now protects your placeholders and tags so a translator cannot break them by accident. A custom sitemap name in render finally works, a rare crash with invalid dates is gone, and the runtime cannot silently grow past its size budget.</p><ul><li>Export and import gettext PO files. <code>verbaly export --format po</code> writes one <code>.po</code> file per language, ready for Poedit, Weblate or any PO editor. <code>verbaly import</code> reads them back with the same safety net as every other format: a translation that breaks a placeholder is rejected, not shipped. Entries a tool marks as fuzzy count as untranslated.</li><li>Translators can no longer break your placeholders in XLIFF. Placeholders and tags travel as protected codes with meaningful names, so translation tools show them as untouchable chips instead of editable text. Plural and select words stay editable on purpose: those need translating.</li><li>Custom sitemap names work now. Setting a custom filename for the sitemap in <code>render</code> used to be ignored: it always wrote <code>sitemap-i18n.xml</code>. The name you configure is the name you get.</li><li>A rare crash is gone. Passing an invalid date to a message could throw instead of degrading gracefully. It was found by a new randomized test suite that hammers the parser with garbage on every run, so this class of bug stays fixed.</li><li>The runtime cannot silently grow. Every push now measures the core bundle and fails if it exceeds its size budget. The ~3 KB promise is enforced by a machine, not a habit.</li></ul><p><a href="https://github.com/AronSoto/verbaly/releases/tag/v0.28.0">Full release notes</a></p>]]></description>
    </item>
    <item>
      <title>0.27.0: Machine translations you review before trusting</title>
      <link>https://verbaly-web.vercel.app/changelog/#v0-27-0</link>
      <guid isPermaLink="false">verbaly-0.27.0</guid>
      <pubDate>Sun, 19 Jul 2026 12:00:00 GMT</pubDate>
      <description><![CDATA[<p>Machine translations now arrive as drafts you review, not as finished work you trust blindly. A new review command approves them, the CI gate can wait for a human, and the model translates with context from your code.</p><ul><li>Machine output is a draft until you say otherwise. <code>verbaly translate</code> still fills the gaps, but every text it writes is now flagged as an unreviewed draft. Your catalogs stay exactly as clean as before: the review state lives in a separate <code>.verbaly-drafts.json</code> file next to them.</li><li>Approve translations when you have read them. <code>verbaly review</code> lists everything waiting for a human; <code>verbaly review --approve</code> accepts it, all of it or one locale at a time. Importing a translator's file counts as review too, so the draft flag clears on its own.</li><li>Block merges on unreviewed machine text. <code>verbaly check --drafts</code> fails CI while any machine translation is still unreviewed. Plain <code>verbaly check</code> is unchanged, so nothing breaks until you opt in.</li><li>Better translations, because the model gets context. <code>verbaly translate</code> now tells the provider which source files each text appears in, so tone and length fit where the text is used.</li><li>See what needs review at a glance. <code>verbaly status</code> (and <code>--json</code>) now report the count of unreviewed drafts per language.</li></ul><p><a href="https://github.com/AronSoto/verbaly/releases/tag/v0.27.0">Full release notes</a></p>]]></description>
    </item>
    <item>
      <title>0.26.0: Astro support, a wrap codemod and CI annotations</title>
      <link>https://verbaly-web.vercel.app/changelog/#v0-26-0</link>
      <guid isPermaLink="false">verbaly-0.26.0</guid>
      <pubDate>Sat, 18 Jul 2026 12:00:00 GMT</pubDate>
      <description><![CDATA[<p>Astro joins the family: one line wires everything and the per-locale rendering runs by itself. A new wrap command wraps your hardcoded JSX text for you, and CI failures become annotations right on your pull requests.</p><ul><li>Astro support, one line. Add <code>verbaly()</code> to the integrations in <code>astro.config</code> and everything is wired: live extraction while you code, type-safe keys and the build gate. The typed keys live inside Astro's own <code>.astro</code> folder, so no extra file lands in your project. In the mirror flow, <code>verbaly render</code> runs by itself after every build.</li><li>Write your text right in <code>.astro</code> files. The same <code>t`…`</code> you already use in <code>.js</code>, <code>.tsx</code>, <code>.svelte</code> and <code>.vue</code> now works in the frontmatter and the markup of Astro components.</li><li>Migrating an existing app? Let <code>verbaly wrap</code> do the boring part. It scans your JSX, finds the hardcoded user-visible text and wraps it in <code>t`…`</code> for you. It reports first; nothing is touched until you pass <code>--write</code>.</li><li>CI failures now point at your code. <code>verbaly check --reporter github</code> turns every missing translation into an annotation on the exact file and line, right on the pull request.</li><li><code>verbaly status --json</code> gives you the coverage numbers machine-readable, ready for badges and dashboards.</li></ul><p><a href="https://github.com/AronSoto/verbaly/releases/tag/v0.26.0">Full release notes</a></p>]]></description>
    </item>
    <item>
      <title>0.25.0: Automatic RTL, real language names and context for translators</title>
      <link>https://verbaly-web.vercel.app/changelog/#v0-25-0</link>
      <guid isPermaLink="false">verbaly-0.25.0</guid>
      <pubDate>Sat, 18 Jul 2026 12:00:00 GMT</pubDate>
      <description><![CDATA[<p>Adding Arabic or Hebrew now just works: the page direction follows the language everywhere. Language switchers get real names from the platform, and translator files say where each text lives.</p><ul><li>Right-to-left languages just work now. Add Arabic, Hebrew or Persian and the page direction follows the language on its own: when the user switches, when the server renders, and in pre-rendered static pages. No <code>dir</code> attribute to manage, ever.</li><li>Language switchers without hardcoded names. <code>localeName('es')</code> returns 'español' and <code>localeName('de', 'en')</code> returns 'German'. The names come from the platform, so any locale works without maintaining a table.</li><li>Need the direction yourself? <code>localeDirection('ar')</code> returns <code>rtl</code>. It is the same helper the rest of the toolkit uses, exported for your own layouts.</li><li>Translators now see where each text lives. <code>verbaly export</code> marks every message with the source files it came from: XLIFF gets standard location notes and CSV gets a location column, so context stops being guesswork.</li></ul><p><a href="https://github.com/AronSoto/verbaly/releases/tag/v0.25.0">Full release notes</a></p>]]></description>
    </item>
    <item>
      <title>0.24.0: Literal braces and safer pre-rendered pages</title>
      <link>https://verbaly-web.vercel.app/changelog/#v0-24-0</link>
      <guid isPermaLink="false">verbaly-0.24.0</guid>
      <pubDate>Thu, 16 Jul 2026 12:00:00 GMT</pubDate>
      <description><![CDATA[<p>Rich messages can finally show a literal brace, and pages pre-rendered with <code>verbaly render</code> now apply the same security rules as the browser.</p><ul><li>Messages can now show literal curly braces. Write <code>&amp;#123;</code> and <code>&amp;#125;</code> in a rich message and they render as <code>{</code> and <code>}</code>. Hex forms like <code>&amp;#x7B;</code> work too, in the browser, in every <code>&lt;Trans&gt;</code> and in pre-rendered pages.</li><li>Pre-rendered pages are now as safe as the browser. Translated attributes go through the same guards, so unsafe links and dangerous attributes from a catalog never reach the static HTML.</li></ul><p><a href="https://github.com/AronSoto/verbaly/releases/tag/v0.24.0">Full release notes</a></p>]]></description>
    </item>
    <item>
      <title>0.23.0: One &lt;Trans&gt; everywhere, live extraction and a coverage view</title>
      <link>https://verbaly-web.vercel.app/changelog/#v0-23-0</link>
      <guid isPermaLink="false">verbaly-0.23.0</guid>
      <pubDate>Tue, 14 Jul 2026 12:00:00 GMT</pubDate>
      <description><![CDATA[<p>The React, Vue and Svelte &lt;Trans&gt; components now share the same props and the same rendering rules, the CLI learns two everyday commands, and the last planned breaking changes land before 1.0.</p><ul><li><code>&lt;Trans&gt;</code> is now the same component in React, Vue and Svelte: same props, same rendering rules. Your custom component wins, then named links, then the safe tag whitelist; anything unknown degrades to plain text.</li><li>Breaking: React and Vue <code>&lt;Trans&gt;</code> now render whitelisted tags like <code>em</code> and <code>code</code> as real elements, matching Svelte and the DOM interpreter. Pass an empty <code>richTags</code> list to keep the old flatten-to-text behavior.</li><li>Breaking: <code>@verbaly/svelte</code> now requires Svelte 5. In exchange <code>&lt;Trans&gt;</code> gains the <code>components</code> prop: map a tag in a message to your own component. On Svelte 4, stay on <code>0.22.0</code>.</li><li><code>verbaly status</code> shows how much is left to translate, one line per language, without failing anything. <code>verbaly check</code> stays the build gate.</li><li><code>verbaly extract --watch</code> keeps catalogs and types in sync as you code, made for webpack, Rspack and Rollup setups where the Vite plugin is not available.</li><li>Easier to give feedback: the README points bug reports and friction reports to the right place, and the issue forms cover all ten packages.</li></ul><p><a href="https://github.com/AronSoto/verbaly/releases/tag/v0.23.0">Full release notes</a></p>]]></description>
    </item>
    <item>
      <title>0.22.0: Your catalogs become native mobile resources</title>
      <link>https://verbaly-web.vercel.app/changelog/#v0-22-0</link>
      <guid isPermaLink="false">verbaly-0.22.0</guid>
      <pubDate>Tue, 14 Jul 2026 12:00:00 GMT</pubDate>
      <description><![CDATA[<p>The export command learns two mobile formats: Android resource folders and iOS string files. A web app and its mobile companion can now share one set of translations.</p><ul><li>Export your translations for mobile apps: <code>verbaly export --format android-xml</code> writes folders you can drop into <code>res/</code>, and <code>--format ios-strings</code> writes <code>.lproj</code> folders for Xcode.</li><li>Sensible defaults for each platform: your source language becomes the platform default, and untranslated keys are left out so the app falls back to it instead of showing empty text.</li><li>Safer catalog files: a catalog with broken JSON now stops the command and names the file, instead of being silently read as empty. A Windows BOM at the start of the file is simply tolerated.</li></ul><p><a href="https://github.com/AronSoto/verbaly/releases/tag/v0.22.0">Full release notes</a></p>]]></description>
    </item>
    <item>
      <title>0.21.0: A quality pass, and typed Nuxt config</title>
      <link>https://verbaly-web.vercel.app/changelog/#v0-21-0</link>
      <guid isPermaLink="false">verbaly-0.21.0</guid>
      <pubDate>Tue, 14 Jul 2026 12:00:00 GMT</pubDate>
      <description><![CDATA[<p>A cleanup sweep across all ten packages, plus fully typed options in your Nuxt config. The internals got tidier and a little smaller, with no change to how you write Verbaly.</p><ul><li>The <code>verbaly</code> key in <code>nuxt.config.ts</code> is now fully typed: you get autocomplete and typo checking, and the module still adds no extra dependency.</li><li>A calmer editor in dev. <code>verbaly.d.ts</code> is only rewritten when its contents actually change, so your TypeScript server stops reloading types on every save.</li><li>A slightly smaller runtime: the tree-shaken core is now 3.28&#160;KB gzipped, down from 3.30.</li><li>Cleaner generated types: a value used as both a date and plain text no longer shows up twice in <code>verbaly.d.ts</code>.</li><li>Clearer command-line messages. Every error and hint reads in plain words, and <code>check</code> now prints the source text right next to the missing key.</li></ul><p><a href="https://github.com/AronSoto/verbaly/releases/tag/v0.21.0">Full release notes</a></p>]]></description>
    </item>
    <item>
      <title>0.20.0: Write your text in .svelte and .vue files too</title>
      <link>https://verbaly-web.vercel.app/changelog/#v0-20-0</link>
      <guid isPermaLink="false">verbaly-0.20.0</guid>
      <pubDate>Mon, 13 Jul 2026 12:00:00 GMT</pubDate>
      <description><![CDATA[<p>Natural text now works inside Svelte and Vue components: write it in the file, script or markup, and the compiler extracts it with a stable key and typed params.</p><ul><li>Write <code>t`Hello ${name}`</code> directly in a <code>.vue</code> template or <code>{$t`Hello ${name}`}</code> in <code>.svelte</code> markup. The compiler picks it up like it always did in <code>.ts</code> files: stable key, typed params, per-locale catalogs.</li><li>Svelte's store form is understood: <code>$t`…`</code>, <code>$t('key')</code> and <code>$t.id('key')`…`</code> all work anywhere in a <code>.svelte</code> file.</li><li>Live in dev, gated in build: save a component with new text and the catalogs plus <code>verbaly.d.ts</code> update on the spot. Missing translations still stop the build.</li><li>Readable keys and runtime keys work in markup too: <code>t.id('home.title')`…`</code> extracts under your key, and <code>t('key')</code> calls count as used keys for <code>check</code> and prune.</li><li><code>verbaly extract</code> scans more by default: <code>.svelte</code> and <code>.vue</code> files, plus the <code>app/</code> folder next to <code>src/</code> (where Nuxt 4 and the Next.js App Router live). Your own include config still wins.</li></ul><p><a href="https://github.com/AronSoto/verbaly/releases/tag/v0.20.0">Full release notes</a></p>]]></description>
    </item>
    <item>
      <title>0.19.0: Next.js joins the family: the cycle is complete</title>
      <link>https://verbaly-web.vercel.app/changelog/#v0-19-0</link>
      <guid isPermaLink="false">verbaly-0.19.0</guid>
      <pubDate>Mon, 13 Jul 2026 12:00:00 GMT</pubDate>
      <description><![CDATA[<p>The last big meta-framework integration. Next.js App Router pages render already translated, hydrate without a flash, and work on both Turbopack and webpack.</p><ul><li>New package <code>@verbaly/next</code>. Two steps: wrap your config with <code>withVerbaly()</code> in <code>next.config.ts</code> and drop <code>&lt;VerbalyProvider&gt;</code> into the root layout. Server Components translate with <code>await getT()</code>; Client Components keep using <code>useT</code> and <code>&lt;Trans&gt;</code> as in any React app.</li><li>Works on Turbopack (the Next 16 default) and on webpack. <code>next dev</code> extracts your messages as you type, and <code>next build</code> stops if a translation is missing.</li><li>Every visitor gets their own language, resolved once per request from their cookie or browser preferences. Pages arrive already translated, nothing flashes, and concurrent visitors never see each other&rsquo;s language.</li><li><code>useSwitchLocale()</code> changes the language instantly, remembers the choice for the next visit, and updates Server Components too.</li><li>Rich messages can now display literal markup, so a message can show "<code>&lt;html lang&gt;</code>" as visible text instead of a broken escape.</li></ul><p><a href="https://github.com/AronSoto/verbaly/releases/tag/v0.19.0">Full release notes</a></p>]]></description>
    </item>
    <item>
      <title>0.18.0: Nuxt joins the family</title>
      <link>https://verbaly-web.vercel.app/changelog/#v0-18-0</link>
      <guid isPermaLink="false">verbaly-0.18.0</guid>
      <pubDate>Mon, 13 Jul 2026 12:00:00 GMT</pubDate>
      <description><![CDATA[<p>The second meta-framework integration: a zero-config Nuxt module renders every page in the visitor&rsquo;s language and hydrates without a flash.</p><ul><li>New package <code>@verbaly/nuxt</code>. One line in your config (<code>modules: ['@verbaly/nuxt']</code>) wires everything: live extraction, per-visitor language, and hydration with the same locale and catalog the server used. No flash, no mismatch.</li><li>No framework lock: the module depends on neither <code>nuxt</code> nor <code>@nuxt/kit</code>, and it is verified against a real Nuxt 4 app.</li><li><code>switchLocale()</code> is now a core export shared by SvelteKit, Nuxt and hand-rolled setups. It loads the catalog first and switches after, so nothing flashes. Existing imports keep working.</li><li>Client-only apps are covered too: with <code>ssr: false</code> the module still picks the right language in the browser.</li><li>Support Verbaly: GitHub Sponsors is live at github.com/sponsors/AronSoto.</li></ul><p><a href="https://github.com/AronSoto/verbaly/releases/tag/v0.18.0">Full release notes</a></p>]]></description>
    </item>
    <item>
      <title>0.17.0: Robust inside, for real</title>
      <link>https://verbaly-web.vercel.app/changelog/#v0-17-0</link>
      <guid isPermaLink="false">verbaly-0.17.0</guid>
      <pubDate>Sat, 11 Jul 2026 12:00:00 GMT</pubDate>
      <description><![CDATA[<p>The hardening release: a full audit of all eight packages fixed every sharp edge it found, and SSR gets its missing piece.</p><ul><li>Attribute translation is now XSS-safe end to end: <code>data-verbaly-attr</code> sanitizes URL attributes and blocks <code>style</code> and <code>srcdoc</code> entirely, so a compromised catalog can&rsquo;t inject scripts.</li><li><code>t()</code> never crashes on bad format arguments: an invalid currency code, date or time style warns once and renders the plain value.</li><li><code>createRequestInstance(locale)</code> in <code>virtual:verbaly</code>: the SSR per-request pattern in one call, with the catalog already loaded and no flash.</li><li>The CLI now rejects misplaced flags. <code>verbaly translate --locale es</code> used to silently translate <em>all</em> locales; now it errors and suggests <code>--locales</code>. Plus <code>--dry-run</code> for <code>extract --prune</code>.</li><li><code>normalizeLink()</code> and <code>resolveRequestLocale()</code> now live once in core, shared by every adapter and SSR integration.</li><li><strong>Breaking:</strong> <code>instance.inspect(key)</code> now returns <code>{ from, source }</code> instead of <code>{ locale, source }</code>: one name for one concept across the observability API.</li></ul><p><a href="https://github.com/AronSoto/verbaly/releases/tag/v0.17.0">Full release notes</a></p>]]></description>
    </item>
    <item>
      <title>0.16.0: Server-side rendering, for real</title>
      <link>https://verbaly-web.vercel.app/changelog/#v0-16-0</link>
      <guid isPermaLink="false">verbaly-0.16.0</guid>
      <pubDate>Sat, 11 Jul 2026 12:00:00 GMT</pubDate>
      <description><![CDATA[<p>The first meta-framework integration: SvelteKit pages render in the visitor&rsquo;s language on the server and hydrate without a flash.</p><ul><li>New package <code>@verbaly/sveltekit</code>. SSR in three wires: <code>verbalyHandle()</code> in your server hooks, one <code>+layout</code> load, and <code>%verbaly.lang%</code> in app.html. Pages arrive translated.</li><li>The language is decided per request: cookie first, then the <code>Accept-Language</code> header (<code>es-PE</code> matches <code>es</code>), then your fallback. Concurrent requests never leak each other&rsquo;s language.</li><li><code>switchLocale()</code>: a client-side switch that loads the catalog first, then saves the choice in the cookie the server reads.</li><li><code>negotiateLocale()</code> in core: the <code>Accept-Language</code> matcher is a public export any Node server can use.</li><li><code>createInstance()</code> in <code>virtual:verbaly</code>: fresh request-scoped instances over your compiled catalogs, plus <code>locales</code> and <code>sourceLocale</code> exports.</li></ul><p><a href="https://github.com/AronSoto/verbaly/releases/tag/v0.16.0">Full release notes</a></p>]]></description>
    </item>
    <item>
      <title>0.15.0: Your translators can work now</title>
      <link>https://verbaly-web.vercel.app/changelog/#v0-15-0</link>
      <guid isPermaLink="false">verbaly-0.15.0</guid>
      <pubDate>Fri, 10 Jul 2026 12:00:00 GMT</pubDate>
      <description><![CDATA[<p>The write→ship cycle opens up to humans: export translator-ready files, import them back validated.</p><ul><li><code>verbaly export</code> writes one translator-ready file per locale: XLIFF 2.0 for translation platforms, CSV for spreadsheets. <code>--missing</code> exports only what&rsquo;s left to translate.</li><li><code>verbaly import</code> validates every entry: a translation that drops a placeholder, a variant or a tag is rejected and reported.</li><li>Existing translations are kept unless you pass <code>--overwrite</code>, and <code>--dry-run</code> previews the whole import.</li><li>No translation platform needed: catalogs are plain JSON, and Crowdin, Lokalise and Phrase read it natively.</li></ul><p><a href="https://github.com/AronSoto/verbaly/releases/tag/v0.15.0">Full release notes</a></p>]]></description>
    </item>
    <item>
      <title>0.14.5: Revision of 0.14.0: the review release</title>
      <link>https://verbaly-web.vercel.app/changelog/#v0-14-5</link>
      <guid isPermaLink="false">verbaly-0.14.5</guid>
      <pubDate>Fri, 10 Jul 2026 12:00:00 GMT</pubDate>
      <description><![CDATA[<p>A hard audit of what 0.14.0 shipped found three real defects. All fixed here.</p><ul><li>The <code>verbaly/devtools</code> overlay no longer freezes the page: its observer now ignores its own writes.</li><li><code>&quot;&quot;</code> now means untranslated <em>everywhere</em>: the runtime falls back instead of rendering a blank hole.</li><li>Locale mirrors get self-canonical <code>rel=&quot;canonical&quot;</code> and <code>og:url</code>, so the hreflang set finally counts.</li></ul><p><a href="https://github.com/AronSoto/verbaly/releases/tag/v0.14.5">Full release notes</a></p>]]></description>
    </item>
    <item>
      <title>0.14.0: Ships translated, and you can see why</title>
      <link>https://verbaly-web.vercel.app/changelog/#v0-14-0</link>
      <guid isPermaLink="false">verbaly-0.14.0</guid>
      <pubDate>Fri, 10 Jul 2026 12:00:00 GMT</pubDate>
      <description><![CDATA[<p>Multi-locale SEO for the static renderer, runtime observability, hardened server-side use.</p><ul><li><code>verbaly render</code> emits reciprocal <code>hreflang</code> alternates and an i18n sitemap: set <code>render.baseUrl</code> and go.</li><li>Opt-in <code>verbaly/devtools</code>: hover any element to see its key, status and source locale.</li><li>New <code>onResolve</code> hook + <code>inspect(key)</code>, with zero cost when unused.</li><li>Server-side Node use hardened and locked with tests.</li></ul><p><a href="https://github.com/AronSoto/verbaly/releases/tag/v0.14.0">Full release notes</a></p>]]></description>
    </item>
    <item>
      <title>0.13.1: README refresh: readable on npm dark mode</title>
      <link>https://verbaly-web.vercel.app/changelog/#v0-13-1</link>
      <guid isPermaLink="false">verbaly-0.13.1</guid>
      <pubDate>Fri, 10 Jul 2026 12:00:00 GMT</pubDate>
      <description><![CDATA[<p>Docs-only patch; first release published from CI with npm provenance.</p><ul><li>The seven package READMEs are readable on npm&rsquo;s dark theme and friendlier to skim.</li><li>MIT re-affirmed; releases now ship with <strong>npm provenance</strong> from the automatic workflow.</li></ul><p><a href="https://github.com/AronSoto/verbaly/releases/tag/v0.13.1">Full release notes</a></p>]]></description>
    </item>
    <item>
      <title>0.13.0: verbaly doctor + adoption &amp; trust</title>
      <link>https://verbaly-web.vercel.app/changelog/#v0-13-0</link>
      <guid isPermaLink="false">verbaly-0.13.0</guid>
      <pubDate>Thu, 09 Jul 2026 12:00:00 GMT</pubDate>
      <description><![CDATA[<p>One command answers &ldquo;why isn&rsquo;t this translating?&rdquo;, and a real path off i18next.</p><ul><li><code>verbaly doctor</code> inspects config, catalogs, plugin wiring, stale types and orphan keys, and gives each finding its exact fix.</li><li>New <a href="/docs/guide/migrate">Migrate from i18next</a> guide: keep your keys and JSON, swap the runtime page by page.</li><li>Publishing moves to GitHub Actions with <strong>npm provenance</strong>: every package is publicly linked to its commit.</li></ul><p><a href="https://github.com/AronSoto/verbaly/releases/tag/v0.13.0">Full release notes</a></p>]]></description>
    </item>
    <item>
      <title>0.12.0: verbaly init + the TypeScript 7 toolchain</title>
      <link>https://verbaly-web.vercel.app/changelog/#v0-12-0</link>
      <guid isPermaLink="false">verbaly-0.12.0</guid>
      <pubDate>Thu, 09 Jul 2026 12:00:00 GMT</pubDate>
      <description><![CDATA[<p>From zero to translating in one command; the build moves to tsdown and native TS 7.</p><ul><li><code>verbaly init</code> scaffolds config + catalogs and detects your bundler. It never overwrites.</li><li>Toolchain: tsup → <strong>tsdown</strong>; typecheck on native TypeScript 7 (~10× faster). No consumer-visible change.</li></ul><p><a href="https://github.com/AronSoto/verbaly/releases/tag/v0.12.0">Full release notes</a></p>]]></description>
    </item>
    <item>
      <title>0.11.0: Rich links + the full modern Intl surface</title>
      <link>https://verbaly-web.vercel.app/changelog/#v0-11-0</link>
      <guid isPermaLink="false">verbaly-0.11.0</guid>
      <pubDate>Wed, 08 Jul 2026 12:00:00 GMT</pubDate>
      <description><![CDATA[<p>Named links with hrefs that never come from catalogs, and the last missing Intl formatters.</p><ul><li>Named links in rich messages render real <code>&lt;a&gt;</code> elements. The href comes from <strong>your code, never the catalog</strong>, and unsafe schemes are blocked.</li><li>Relative time (<code>{when:relative}</code>), lists (<code>{xs:list}</code>) and CLDR units (<code>{n:unit/kilometer}</code>): all native <code>Intl</code>, zero dependencies.</li><li>License: Apache-2.0 → <strong>MIT</strong> across all seven packages.</li></ul><p><a href="https://github.com/AronSoto/verbaly/releases/tag/v0.11.0">Full release notes</a></p>]]></description>
    </item>
    <item>
      <title>0.10.0: Static sites ship translated + Verbaly beyond Vite</title>
      <link>https://verbaly-web.vercel.app/changelog/#v0-10-0</link>
      <guid isPermaLink="false">verbaly-0.10.0</guid>
      <pubDate>Tue, 07 Jul 2026 12:00:00 GMT</pubDate>
      <description><![CDATA[<p>The FOUC dies on SSG, and webpack/Rollup/esbuild/Rspack join via unplugin.</p><ul><li><code>verbaly render</code> pre-fills your built HTML per locale with the real runtime, so static pages ship already translated.</li><li>New <code>@verbaly/unplugin</code>: the same virtual modules + build gate on webpack 5, Rollup, esbuild and Rspack.</li><li><code>verbaly pseudo</code>: a QA locale (<code>⟦Ĥéĺĺó ~⟧</code>) that exposes hardcoded strings and clipped layouts.</li><li><code>&lt;Trans&gt;</code> lands in Svelte.</li></ul><p><a href="https://github.com/AronSoto/verbaly/releases/tag/v0.10.0">Full release notes</a></p>]]></description>
    </item>
    <item>
      <title>0.9.0: Machine translation closes the loop</title>
      <link>https://verbaly-web.vercel.app/changelog/#v0-9-0</link>
      <guid isPermaLink="false">verbaly-0.9.0</guid>
      <pubDate>Mon, 06 Jul 2026 12:00:00 GMT</pubDate>
      <description><![CDATA[<p>write → extract → translate → check green: the whole cycle in one tool.</p><ul><li><code>verbaly translate</code> fills the <code>&quot;&quot;</code> holes <code>check</code> reports: batched, with <code>--dry-run</code> and per-locale filters.</li><li>Structural validation: placeholders, variants and tags must survive verbatim or the translation is rejected.</li><li>Pluggable providers: Claude as the reference, and any <code>(messages) =&gt; translations</code> function works. Zero runtime impact.</li></ul><p><a href="https://github.com/AronSoto/verbaly/releases/tag/v0.9.0">Full release notes</a></p>]]></description>
    </item>
    <item>
      <title>0.8.0: Readable keys + lazy catalogs</title>
      <link>https://verbaly-web.vercel.app/changelog/#v0-8-0</link>
      <guid isPermaLink="false">verbaly-0.8.0</guid>
      <pubDate>Mon, 06 Jul 2026 12:00:00 GMT</pubDate>
      <description><![CDATA[<p>Human-readable ids when you want them, per-locale lazy loading when you need it.</p><ul><li><code>t.id('inbox.title')`Hello ${name}`</code>: readable, namespaced keys; hashed stays the zero-config default.</li><li><code>loaders</code> in <code>createVerbaly</code>: bundle only the source locale and load the rest on demand, flash-free with <code>await loadLocale</code>.</li></ul><p><a href="https://github.com/AronSoto/verbaly/releases/tag/v0.8.0">Full release notes</a></p>]]></description>
    </item>
    <item>
      <title>0.7.0: Svelte joins the ecosystem</title>
      <link>https://verbaly-web.vercel.app/changelog/#v0-7-0</link>
      <guid isPermaLink="false">verbaly-0.7.0</guid>
      <pubDate>Sat, 04 Jul 2026 12:00:00 GMT</pubDate>
      <description><![CDATA[<p>Idiomatic stores over the same reactive core, for Svelte 4 & 5.</p><ul><li><code>@verbaly/svelte</code>: <code>$t('key')</code> re-renders on locale switch, and <code>bind:value={$locale}</code> just works.</li><li>Context via <code>provideVerbaly</code>/<code>useVerbaly</code> or store factories without a component tree.</li></ul><p><a href="https://github.com/AronSoto/verbaly/releases/tag/v0.7.0">Full release notes</a></p>]]></description>
    </item>
    <item>
      <title>0.6.0: The compiler understands your whole codebase</title>
      <link>https://verbaly-web.vercel.app/changelog/#v0-6-0</link>
      <guid isPermaLink="false">verbaly-0.6.0</guid>
      <pubDate>Sat, 04 Jul 2026 12:00:00 GMT</pubDate>
      <description><![CDATA[<p>Write JSX in place and the compiler extracts it.</p><ul><li><code>&lt;Trans&gt;</code> write-in-place extraction: source text with real elements becomes key + <code>components</code> automatically.</li><li><code>verbaly.config.ts</code> / <code>.mts</code> support (esbuild as optional peer).</li></ul><p><a href="https://github.com/AronSoto/verbaly/releases/tag/v0.6.0">Full release notes</a></p>]]></description>
    </item>
    <item>
      <title>0.5.0: Hardening + performance, with receipts</title>
      <link>https://verbaly-web.vercel.app/changelog/#v0-5-0</link>
      <guid isPermaLink="false">verbaly-0.5.0</guid>
      <pubDate>Sat, 04 Jul 2026 12:00:00 GMT</pubDate>
      <description><![CDATA[<p>Benchmarks vs i18next run on every release from here on.</p><ul><li>Hot path memoized end to end: lookup <strong>31×</strong>, interpolation <strong>10×</strong>, plurals <strong>5×</strong> vs i18next.</li><li>Defensibility audit: cached fallback chains, per-element DOM args, capped AST cache.</li></ul><p><a href="https://github.com/AronSoto/verbaly/releases/tag/v0.5.0">Full release notes</a></p>]]></description>
    </item>
    <item>
      <title>0.4.0: The DOM interpreter catches up</title>
      <link>https://verbaly-web.vercel.app/changelog/#v0-4-0</link>
      <guid isPermaLink="false">verbaly-0.4.0</guid>
      <pubDate>Sat, 04 Jul 2026 12:00:00 GMT</pubDate>
      <description><![CDATA[<p>Rich text in plain HTML, safely, plus locale bootstrap helpers.</p><ul><li><code>data-verbaly-rich</code>: tagged messages render real elements from a phrasing whitelist, never <code>innerHTML</code>.</li><li><code>resolveLocale</code> / <code>persistLocale</code>: storage → navigator → fallback, SSR-safe.</li></ul><p><a href="https://github.com/AronSoto/verbaly/releases/tag/v0.4.0">Full release notes</a></p>]]></description>
    </item>
    <item>
      <title>0.3.0: ICU escape-hatch + robustness</title>
      <link>https://verbaly-web.vercel.app/changelog/#v0-3-0</link>
      <guid isPermaLink="false">verbaly-0.3.0</guid>
      <pubDate>Fri, 03 Jul 2026 12:00:00 GMT</pubDate>
      <description><![CDATA[<p>Bring your ICU strings: auto-detected, zero dependencies.</p><ul><li>ICU MessageFormat (<code>plural</code>, <code>select</code>, <code>selectordinal</code>) parses into the same engine, opt-in per message.</li><li>Hardened <code>&lt;Trans&gt;</code> tokenizer + build-time key-collision warnings.</li></ul><p><a href="https://github.com/AronSoto/verbaly/releases/tag/v0.3.0">Full release notes</a></p>]]></description>
    </item>
    <item>
      <title>0.2.0: Ecosystem public + rich text</title>
      <link>https://verbaly-web.vercel.app/changelog/#v0-2-0</link>
      <guid isPermaLink="false">verbaly-0.2.0</guid>
      <pubDate>Fri, 03 Jul 2026 12:00:00 GMT</pubDate>
      <description><![CDATA[<p>First npm publish of the ecosystem packages.</p><ul><li><code>@verbaly/compiler</code>, <code>@verbaly/vite</code>, <code>@verbaly/react</code> and <code>@verbaly/vue</code> land on npm, version-aligned.</li><li><code>&lt;Trans&gt;</code> in React and Vue: interpolate elements inside translated sentences with named tags.</li></ul><p><a href="https://github.com/AronSoto/verbaly/releases/tag/v0.2.0">Full release notes</a></p>]]></description>
    </item>
    <item>
      <title>0.1.0: The first release</title>
      <link>https://verbaly-web.vercel.app/changelog/#v0-1-0</link>
      <guid isPermaLink="false">verbaly-0.1.0</guid>
      <pubDate>Thu, 02 Jul 2026 12:00:00 GMT</pubDate>
      <description><![CDATA[<p>The core idea, working: write text, ship translations.</p><ul><li>Runtime: message format over native <code>Intl</code>, reactive locale store, DOM interpreter, type-level keys and params.</li><li>Compiler: AST extraction of <code>t`…`</code> into stable hashed keys, <code>extract</code>/<code>check</code>, typed <code>verbaly.d.ts</code>.</li><li>Vite plugin: live extraction, HMR, per-locale code-splitting, builds fail on missing translations.</li></ul><p><a href="https://github.com/AronSoto/verbaly/releases/tag/v0.1.0">Full release notes</a></p>]]></description>
    </item>
  </channel>
</rss>
