v0.23.0latest

Writenaturaltext.Shiptype-safetranslations.

The compiler extracts your messages, generates the types and splits bundles per locale. A missing translation fails the build, so it never reaches your users.

Integrations
~3 KBruntime
10packages
MITlicense
i18n that feels like zero config

Features

Why Verbaly

Zero config, actually

One Vite plugin. No giant init(), no Babel macros, no proprietary formats. Catalogs are plain JSON you can take anywhere.

vite.config.ts
import verbaly from '@verbaly/vite';

export default { plugins: [verbaly()] };

Types from your messages

The text itself declares its params, so if you forget one TypeScript stops you.

editor
t('inbox', { count: 3 });  
t('inbox');  ✗ { count: number }

The build gate

Missing translations abort vite build, so raw keys never reach your users.

vite build
✗ [es] x7Ka9q2f: "Welcome back"
✓ all locales complete

Plain HTML, first-class

No framework? The DOM interpreter hydrates data-verbaly nodes, re-renders on locale switch and watches new elements.

index.html
<h1 data-verbaly="home.title"></h1>
<p  data-verbaly="inbox" data-verbaly-args='{"count":3}'></p>

Compare

Same job, less machinery

An honest look against the tools you'd otherwise reach for. It shows where Verbaly wins, and where the others are fine too.

FeatureVerbalyVerbalyi18nextLinguiParaglide
Type-safe keys & paramsInferred from your textPlugin / manualPartialYes
Runtime size (gzip)~3 KB, zero deps~14 KB (+9 KB react)~5 KB~0 (compiled)
Key maintenanceNone, auto-extractedBy handExtract → compile stepBy hand
Dynamic / CMS contentReal runtime pathYesYesWeak
Plain HTML (no framework)DOM interpreter
Missing translationsBuild failsRuntime warningCI stepBuild fails
Catalog formatPlain JSONJSONPO / JSONinlang format
VerbalyVerbaly
Type-safe keys & params
Inferred from your text
Runtime size (gzip)
~3 KB, zero deps
Key maintenance
None, auto-extracted
Dynamic / CMS content
Real runtime path
Plain HTML (no framework)
DOM interpreter
Missing translations
Build fails
Catalog format
Plain JSON
i18next
Type-safe keys & params
Plugin / manual
Runtime size (gzip)
~14 KB (+9 KB react)
Key maintenance
By hand
Dynamic / CMS content
Yes
Plain HTML (no framework)
Missing translations
Runtime warning
Catalog format
JSON
Lingui
Type-safe keys & params
Partial
Runtime size (gzip)
~5 KB
Key maintenance
Extract → compile step
Dynamic / CMS content
Yes
Plain HTML (no framework)
Missing translations
CI step
Catalog format
PO / JSON
Paraglide
Type-safe keys & params
Yes
Runtime size (gzip)
~0 (compiled)
Key maintenance
By hand
Dynamic / CMS content
Weak
Plain HTML (no framework)
Missing translations
Build fails
Catalog format
inlang format

Sizes are approximate gzip runtime footprints; every project weighs trade-offs differently.

Start here

From words to shipped locales.

Three steps and a guardrail, or let verbaly init scaffold it in one command. No key files written by hand: the compiler does the boring parts while you keep writing product copy.

  1. Install

    One command, two packages.

     pnpm add verbaly @verbaly/vite
  2. Add the plugin

    Point it at the locale you write in.

    vite.config.ts
    import verbaly from '@verbaly/vite';
    
    export default { plugins: [verbaly({ sourceLocale: 'es' })] };
  3. Write text, not keys

    Keys, types and catalogs: generated.

    src/app.ts
    import { t, setLocale } from 'virtual:verbaly';
    
    t`Hola ${name}`;      // keys, types & catalogs: generated
    await setLocale('pt'); // lazy-loads its chunk
  4. Let CI guard it

    Exits 1 while any locale is incomplete, so untranslated text never ships.

    .github/workflows/ci.yml
    - run: npx verbaly check  # the build gate, in your pipeline

Rather break it yourself?

Edit the catalogs, switch locales and empty a translation, all on the real runtime, live.

Open the playground
Copied to clipboard