Skip to content

Guides

When something breaks

Start with npx verbaly doctor. It inspects the whole setup and prints the exact fix next to each finding, so most of this page is what it would have told you.

The verbaly command is not found

The command lives in @verbaly/compiler. The plugins depend on it, but a package manager only links a command for a package you installed yourself, so a plugin bringing it along is not enough. Add it to your dev dependencies:

pnpm add -D @verbaly/compiler

A language appeared that I never asked for

If you do not list locales, every JSON file in your catalogs directory counts as a language, the pseudo-locale one aside. So a file left behind by an earlier run becomes one too, and the build starts asking for its translations. List your languages and the list is complete:

locales: ['es', 'en']  // now a stray pt.json is ignored

A warning says a language is not a locale tag

pt-BR is how a language code is written, with a hyphen between its parts. pt_BR, the way gettext and many backends write it, is not a code a browser can read, so Verbaly reads it as pt-BR to keep numbers, dates and plurals formatting, and warns once that it did. A code with no sure fix formats in the browser's default language, with the same warning. doctor names each one in your config with its rename: change the catalog's file name and your config together.

[verbaly] "pt_BR" is not a locale tag, so Intl reads it as pt-BR

wrap reported files it did not write

A file where t is not available is listed and left untouched, because wrapping text there would stop your project compiling. The report says how many texts wait in each file and whether it is a client component, so you know which line to add. Add it and run it again.

The build says a translation is broken

Not missing, broken: the translation is there and cannot render what the source renders. It lost a {value}, dropped a plural case or changed a tag. Every build lists these first, because they render wrong, and npx verbaly check gives the exact reason per message and fails on them in CI.

The build compiled, then listed translations

That is where the check runs. In Next.js, next build compiles your app first and then lists missing, broken and unknown keys, so commands that only load your config, like next typegen, never run it. Fill what it lists, or run npx verbaly check before you build to see the same list sooner. With failOnMissing: true the build stops there instead.

A key is showing on screen instead of the text

A message that resolves nowhere renders as its own key. Usually the catalog for that language has not arrived yet, or the group is one you excluded from the browser with bundle.exclude and something asks for it at runtime. The build warns about the second case by name. If it only happens in your source language, the key is probably in a translation and not in your source catalog: npx verbaly check lists it as a key only a translation has.

My edit in the source catalog disappeared

That text is written in your code, with t`…` or t.id, so the code owns it: the dev server, extract and the build all put the code's text back. The message says which file holds it, and that is where to change it. If you want to edit a text in the catalog, keep only its key in the code, as t('key'). Run from the terminal, extract prints one line for each text it put back, with the text it had, so you can see exactly what it undid.

[verbaly] es.json: your edit of "footer.copyright" was replaced, its text lives in src/components/Footer.tsx:12: change it there

A translation shows as outdated

Its source text changed after it was written, and nobody has touched it since. It can also show up right after an import: a translator's file carries the source text it was made from, so a file made for an older version is outdated as soon as it lands. Update the translation and the warning goes away on its own. If it still says the right thing, keep it with npx verbaly review --approve. It never fails a build unless your CI runs check --outdated.

The state file is broken after a merge

.verbaly-state.json records which translations are machine drafts and which source text each one was written for, and a merge conflict can leave it unreadable. Commands that only read it warn and go on, and the ones that would write drafts (translate, import, Studio's save and an agent's drafts) stop before writing anything. Restore it from git, or resolve the conflict by hand: deleting it would mark every draft as reviewed.

[verbaly] /app/locales/.verbaly-state.json is not valid JSON: restore it from git or fix it (deleting it approves every draft)

A text stays in my source language

The compiler only reads calls named t, so a translation function under another name, like const tr = useT(), never has its text extracted. extract and doctor name each one with its file and line, in .vue, .svelte and .astro files too: rename it to t. If the file is outside your include, nothing in it is read either, and your build and dev server name it when they start, with the pattern to add:

[verbaly] components/save-button.tsx uses t`…` outside the include of your verbaly config, so its texts are never extracted and show in en in every language: add "components/**/*.{js,jsx,ts,tsx,mjs,mts,svelte,vue,astro}" to include (or the file to exclude, if it is not app code)

The page switched language right after it loaded

The page arrived in one language and the instance in the browser started in another, so bindDom translated the page into its own. It warns once, naming both. When the address carries the language, create the instance with the locale localeFromPath reads from it; when it does not, call persistLocale first, so the page and the browser start from the same choice.

[verbaly] the page is <html lang="es"> and this instance is in "en": read the url with localeFromPath, or call persistLocale first

A message shows its ICU source instead of its text

ICU syntax needs its parser, and the compiler only ships it when one of your catalogs uses that syntax at build time. A message that arrives later, from a CMS or through addMessages, finds no parser, so it shows its own source and warns once. Set icu: true in your verbaly config to always ship the parser, or pass icu to createVerbaly if you create the instance yourself.

[verbaly] a message uses ICU syntax and no ICU parser is loaded: set icu: true in your verbaly config, or pass icu to createVerbaly

My keys are not typed, or the types are stale

The types are written by extract, the dev servers and the build, so a fresh clone or a CI job has none until one of them runs: npx verbaly typegen writes them alone, and it is the fix for Cannot find module 'virtual:verbaly'. They go where your framework already reads them, and a Cannot redeclare means an older verbaly.d.ts left at the root, which the next run removes. A typo in a key fails with the key in the error.

Prune left keys you no longer use

While any source file does not parse, extract --prune removes nothing and names the file. A file it cannot read may be the one that uses those keys, and pruning then would delete their translations. Fix the syntax error and run it again.

t cannot be imported from verbaly

No package exports t. It comes from your project's own module, virtual:verbaly, or from your framework's hook. doctor reports this one as an error because no bundler can resolve it.

↑↓to moveEnterto open
Copied to clipboard