Guides
CLI
The verbaly binary ships with @verbaly/compiler (a dependency of the Vite plugin), so npx verbaly works out of the box.
Commands
| Command | What it does |
|---|---|
| verbaly init | Scaffolds config + locale catalogs and suggests the right plugin for your bundler |
| verbaly doctor | Diagnoses the setup (config, catalogs, plugin wiring, stale types, orphan keys) and tells you the exact fix for each finding |
| verbaly extract | Scans sources (.js/.ts/.jsx/.tsx, plus .svelte/.vue since 0.20.0), writes new messages to the source catalog, adds "" placeholders to the rest and regenerates verbaly.d.ts |
| verbaly status | Shows translation coverage per locale at a glance (es: 45/48 translated), informational only, never fails |
| verbaly check | Verifies every key has a translation in every locale. Exits 1 on missing or unknown keys, built for CI |
| verbaly translate | Fills the "" holes check reports via a translation provider, Claude by default |
| verbaly export | Writes translator-ready XLIFF 2.0 or CSV files per locale, or native Android and iOS resources for a companion mobile app |
| verbaly import | Fills catalogs back from translated XLIFF/CSV files, with every entry structure-validated and broken ones rejected and reported |
| verbaly pseudo | Generates a pseudo-locale QA catalog (en-XA) from the source, exposing hardcoded strings and clipped layouts |
| verbaly render | Pre-fills data-verbaly HTML per locale in your built site, so static pages ship already translated |
Scaffolding
verbaly init sets up a project in one command: a typed config (.ts if you have a tsconfig, .mjs otherwise), your source catalog plus one per --locales, and the next steps for your bundler (Vite → @verbaly/vite, everything else → @verbaly/unplugin).
npx verbaly init --locales es,pt
# created: verbaly.config.ts, locales/en.json, locales/es.json, locales/pt.json
# detected bundler: vite → pnpm add -D @verbaly/viteRe-running is safe: an existing config or catalog is reported as “kept” and left byte-identical.
Health check
verbaly doctor (0.13.0) is init's sibling for projects already running: it inspects the whole setup and reports every finding with the exact command that fixes it. Green means healthy; problems exit 1.
npx verbaly doctor
# ✓ config: verbaly.config.ts found
# ✓ catalogs: 3 locales (en, es, pt) in locales/
# ⚠ types: verbaly.d.ts is stale
# fix: run `npx verbaly extract`
# ⚠ orphans: 2 catalog keys are no longer referenced (old.title, hero.cta)
# fix: run `npx verbaly extract --prune` to drop them
# ✗ keys: 1 unknown key used in code (ghost.key)One command to answer “why isn't this translating?” It checks your config, catalogs and plugin wiring, whether verbaly.d.ts is fresh, and any orphan or missing keys.
Readable keys
Hashed keys are the zero-config default. When you want human-readable ids, grouped with dotted namespaces, opt in per message with t.id:
t.id('inbox.title')`Hello ${name}`;
// extracted under "inbox.title" and rewritten to:
t('inbox.title', { name });Catalogs stay flat JSON. The dots are only a naming convention. Dynamic ids (t.id(someVar)) are left untouched, and duplicate ids with different texts trigger the key-collision warning.
Flags
| Flag | Default | Description |
|---|---|---|
| --root | cwd | Project root |
| --dir | locales | Catalogs directory |
| --source | en | Source locale |
| --locales | from files | Extra locales, comma-separated |
| --prune | off | Drop keys no longer referenced (extract only) |
| --watch | off | Re-extracts as source files change: live extraction for webpack, Rspack and Rollup setups (extract only, 0.23.0) |
| --locale | en-XA | Pseudo-locale id (pseudo only) |
| --site | dist | Built site directory (render only) |
Since 0.17.0 flags are validated per command: a flag that belongs to another command exits with an actionable error instead of being silently ignored, so translate --locale es tells you it means --locales.
Machine translation
verbaly translate (0.9.0) closes the loop: write → extract → translate → check green. The default provider uses Claude (@anthropic-ai/sdk as an optional peer + ANTHROPIC_API_KEY); batches of 20 per request.
npx verbaly translate --dry-run # list what's missing, write nothing
npx verbaly translate --locales es,pt # fill only these locales
npx verbaly translate --model claude-opus-4-8 # override the default (claude-sonnet-5)Every translation is checked: placeholders and tags must survive verbatim. If one doesn't, the translation is rejected and the entry stays "", so check keeps flagging it until it's fixed.
No lock-in: plug your own provider in verbaly.config.ts:
export default {
translate: {
provider: async ({ sourceLocale, targetLocale, messages }) => ({ /* key → translation */ }),
},
};Translators & TMS
Humans in the loop? verbaly export (0.15.0) writes translator-ready XLIFF 2.0 or CSV files per locale and verbaly import brings them back, structure-validated with the same gate as translate. The full flow, TMS options included, lives in Work with translators.
Since 0.22.0, export also writes native mobile resources: --format android-xml for Android's res/ layout and --format ios-strings for Xcode. Untranslated keys are left out so the app falls back to your source language (--missing applies only to the translator formats).
Static rendering (SSG)
verbaly render (0.10.0) kills the flash of untranslated content on static sites: it walks your built HTML and pre-fills every data-verbaly element per locale using the real runtime: plurals, Intl formatting, data-verbaly-args, attribute translation and whitelisted rich text.
npx vite build # or astro build, eleventy, …
npx verbaly render # dist/index.html filled in the source locale
# dist/es/index.html, dist/pt/… for the rest
npx verbaly render --site out --locales es # custom dir / locale filter- Each copy gets
<html lang>set; the source locale is filled in place, the rest mirror todist/<locale>/…. - The
data-verbalyattributes stay in the output, so client-side locale switching keeps working on top of the pre-rendered page. - Messages are HTML-escaped (never injected), missing keys are reported and left untouched, and re-runs are idempotent.
- Named links (
0.11.0): setrender.linksinverbaly.config.*(ordata-verbaly-linksper element) and rich messages pre-render real<a href>elements, attribute-escaped, withjavascript:blocked. - Multi-locale SEO (
0.14.0): setrender.baseUrland every page gets reciprocalhreflangalternates;--sitemapwrites an i18n sitemap,--cleandrops stale locale pages. - Self-canonical mirrors (
0.14.5):rel="canonical"andog:urlare rewritten to each locale's own URL, because a cross-locale canonical would make search engines ignore the hreflang set.
Pseudo-localization
verbaly pseudo (0.10.0) fills a QA catalog (en-XA by default) from the source locale: accented letters, ⟦…⟧ markers and ~33% padding. Text that stays clean in the pseudo build is hardcoded; layouts that clip reveal themselves before a real translation does.
npx verbaly pseudo # locales/en-XA.json
# "Hello {name}" → "⟦Ĥéĺĺó {name} ~⟧"Params, variant blocks and named tags survive verbatim, guaranteed by the same structural validation as translate. Re-running regenerates the whole catalog.
Config file
Flags win over verbaly.config.{js,mjs,ts,mts,json} at the project root (TS configs need esbuild installed). Same options as the Vite plugin, one source of truth:
export default {
sourceLocale: 'es',
locales: ['es', 'en', 'pt'],
dir: 'locales',
};CI example
- run: pnpm install
- run: npx verbaly check # fails the job on missing translations
- run: pnpm build