Referência
CLI
O comando verbaly vive em @verbaly/compiler. Adicione-o às suas dependências de desenvolvimento para que npx verbaly funcione: os gestores de pacotes só ligam um comando de um pacote que você mesmo instalou, e os plugins dependem do compilador sem o instalar por você.
Comandos
13 comandos, na ordem em que o ciclo os usa. Cada um leva à sua seção.
Scaffolding
verbaly init deixa o projeto pronto em um comando: uma config tipada (.ts se você tem tsconfig, .mjs se não), o seu catálogo fonte mais um por --locales, e os próximos passos conforme o que ele encontrar: Astro, Nuxt, Next.js, SvelteKit e Vite têm cada um a sua integração, e qualquer outro bundler usa @verbaly/unplugin.
npx verbaly init --locales es,pt- created: verbaly.config.ts, locales/en.json, locales/es.json, locales/pt.json
- detected: vite
- next steps:
pnpm add -D @verbaly/vite
Reexecutar é seguro: uma config ou catálogo existente é reportado como “kept” e fica intacto.
Checagem de saúde
verbaly doctor é o irmão do init para projetos já rodando: inspeciona todo o setup e reporta cada achado com o comando exato que o corrige. Verde significa saudável; problemas saem com 1.
npx verbaly doctor- Correto config: verbaly.config.ts found
- Correto catalogs: 3 locales (en, es, pt) in locales/
- Correto plugin: @verbaly/vite installed for vite
- Aviso types: verbaly.d.ts is stale fix: run
npx verbaly extract - Aviso orphans: 2 catalog keys are no longer referenced (old.title, hero.cta) fix: run
npx verbaly extract --pruneto drop them - Erro imports: 1 file imports t from a verbaly package, which never exports it (src/app.ts) fix: t comes from your instance (React:
const t = useT()) or fromvirtual:verbaly - Erro translations: 1 broken translation (es): present but not rendering what the source renders fix: run
npx verbaly checkto read what each one lost
Um comando para responder “porque é que isto não traduz?” Verifica a tua config e os teus catálogos, se está instalada a integração que o teu framework precisa, se o verbaly.d.ts está atualizado, as chaves órfãs e a saúde das tuas traduções: as que faltam, as quebradas e os avisos. Também apanha duas coisas que o gate da build não consegue ver: t importado de um pacote do verbaly, que nenhum bundler resolve, e uma mensagem que ficou com um bloco plural ou de formato como texto literal. Também nomeia qualquer ficheiro que não conseguiu analisar, como aviso: os seus textos não são extraídos, mas o ficheiro pode compilar na mesma no teu projeto. Um relatório saudável significa uma build que passa: o doctor falha em tudo o que o check falha, mais falhas de setup como essas duas, e nunca em algo que compila e renderiza.
Envolver texto fixo
verbaly wrap encontra texto fixo no seu JSX e o envolve em t`…` por você. Primeiro informa, e --write aplica. O que for ambíguo é listado para um humano em vez de adivinhado, e um ficheiro onde t não está disponível fica intacto e é informado, então o projeto continua sempre a compilar.
Passar de outra biblioteca
verbaly migrate passa os catálogos que já tens. O teu ficheiro mantém a forma, aninhada ou plana, e a única coisa que tem de reescrever é a interpolação: {{name}} passa a {name}, porque uma chaveta duplicada é como o Verbaly escreve uma chaveta literal. Primeiro reporta, e --write aplica.
--plurals é a metade opcional: junta _one e _other numa só mensagem com variantes, que é o que dá a seleção de plural automática, e lá dentro o número escreve-se já localizado. Também percebe a forma antiga key + key_plural. Sem a flag, essas keys continuam a funcionar tal como estão.
O que não vai adivinhar fica listado: um formato do i18next dentro das chavetas, um valor {{- sem escape}}, um aninhamento com $t(), e uma junção que apagaria uma key que já usas. Essas mensagens ficam exatamente como estavam.
Extração
verbaly extract analisa o seu código (.js/.ts/.jsx/.tsx, mais .svelte, .vue e .astro), escreve as mensagens novas no catálogo de origem, acrescenta marcadores "" ao resto e regenera o verbaly.d.ts. Avisa quando uma mensagem ficou com um bloco plural ou de formato como texto literal, e nomeia qualquer ficheiro que não conseguiu analisar em vez de parar nele.
Cobertura
verbaly status mostra num relance quanto está traduzido por idioma (es: 45/48 translated), quantas traduções automáticas ainda esperam revisão e quantas estão quebradas. É apenas informativo e nunca falha; --json entrega os mesmos números a badges e ferramentas.
Pseudo-localização
verbaly pseudo preenche um catálogo de QA (en-XA por padrão) a partir do locale fonte: letras acentuadas, marcadores ⟦…⟧ e ~33% de padding. Texto que sai limpo no build pseudo está hardcoded; layouts que cortam se revelam antes de uma tradução real.
Params, blocos de variantes e tags sobrevivem verbatim, garantido pela mesma validação estrutural do translate. Re-executar regenera o catálogo completo.
Tradução de máquina
verbaly translate fecha o ciclo: escrever → extract → translate → check verde. O provider padrão usa Claude (@anthropic-ai/sdk como peer opcional + ANTHROPIC_API_KEY); batches de 20 por request, cada um dizendo ao modelo em quais arquivos fonte o texto vive, então ele traduz com contexto.
Cada tradução é verificada: os placeholders e as tags precisam sobreviver intactos. Se um não sobrevive, a tradução é rejeitada e a entrada fica em "", então o check continua sinalizando até você corrigir.
Sem lock-in: conecte seu próprio provider no verbaly.config.ts. Em TypeScript, TranslateProvider tipa ele para você, e origins diz em quais arquivos do código cada texto aparece, então você traduz pensando no lugar onde ele é usado:
import type { TranslateProvider } from '@verbaly/compiler';
const provider: TranslateProvider = async ({ sourceLocale, targetLocale, messages, origins }) => ({
/* key → translation */
});
export default { translate: { provider } };Revise os rascunhos
A saída da máquina é um rascunho, não trabalho revisado. O translate registra tudo o que escreve em locales/.verbaly-drafts.json (commitado com seus catálogos, nunca editado à mão), e o verbaly review fecha o ciclo: leia os rascunhos e aprove.
Quer bloquear o merge até um humano assinar? Adicione --drafts ao check do seu CI: ele continua falhando enquanto restarem traduções por máquina sem revisar. Um arquivo de tradutor trazido com import conta como revisão por si só.
Tradutores e TMS
Humanos no ciclo? verbaly export escreve arquivos XLIFF 2.0, CSV ou gettext PO por idioma prontos para o tradutor e verbaly import os traz de volta, validados estruturalmente com o mesmo gate do translate. O fluxo completo, opções de TMS incluídas, vive em Trabalhar com tradutores.
verbaly import valida cada entrada, e rejeita e informa as quebradas. Entradas PO marcadas como fuzzy contam como não traduzidas, e as chaves importadas deixam de contar como rascunhos sem revisar.
O export também escreve recursos nativos de mobile: --format android-xml para a estrutura res/ do Android e --format ios-strings para o Xcode. As keys sem tradução ficam de fora para o app cair no seu idioma fonte (--missing vale só para os formatos de tradutor). Todos os formatos escrevem em verbaly-export/ a menos que você aponte --out para outro lugar.
O que o gate verifica
check faz duas perguntas, não uma. Cada mensagem está traduzida? E cada tradução consegue mostrar o que o original mostra? Uma tradução preenchida não é automaticamente uma que funciona, então isto também para o build, venha de onde vier: de uma pessoa, de uma máquina ou de uma edição à mão.
| A tradução | Por que falha |
|---|---|
perdeu um {param}, ou mudou o nome dele | o valor nunca chega ao texto |
perdeu um <em>, ou ganhou um | a ênfase ou a marca do link desaparece |
| transformou um bloco de plural em texto simples | uma única redação para qualquer quantidade |
tem um bloco de plural sem o caso other | cada quantidade que não lista aparece vazia |
Dois achados são avisos: eles aparecem, o código de saída continua em 0, porque o texto ainda é mostrado. Um é um plural sem as formas que o idioma precisa, já que polonês e árabe pedem mais que o inglês. O outro é um caso específico como =0 que o original tinha e a tradução deixou de fora, então essa quantidade agora cai em other.
Seja qual for a falha, o relatório fecha com o passo que a repara: gerar e preencher quando uma mensagem ainda não tem tradução, corrigir a key quando ela não vive em nenhum catálogo, e consertar a própria mensagem quando ela está quebrada.
Exemplo de CI
- run: pnpm install
- run: npx verbaly check --reporter github # each finding becomes a PR annotation
- run: pnpm buildCom --reporter github cada achado aparece como anotação no pull request, apontando para o arquivo e a linha onde o texto vive: os erros como erros, os avisos como avisos, então um aviso nunca deixa o job vermelho. Sem isso você recebe a lista em texto simples, com o mesmo código de saída.
Renderização estática (SSG)
verbaly render mata o piscar de texto por traduzir em sites estáticos: percorre o teu HTML construído e preenche cada elemento data-verbaly por idioma usando o runtime a sério: plurais, formatação com Intl, data-verbaly-args, tradução de atributos e texto rico da whitelist. Dentro de um idioma, os links para páginas que esse idioma também tem mantêm o seu prefixo, por isso quem lá chega fica lá. Levá-lo até lá é o --redirect, mais abaixo.
- Cada idioma recebe o seu
<html lang>e<html dir>(locales da direita para a esquerda espelham correto); o locale fonte é preenchido in-place e o resto é espelhado paradist/<locale>/…. - Os atributos
data-verbalyficam no output, então a troca de idioma client-side continua funcionando sobre a página pré-renderizada. - Mensagens são escapadas como HTML (nunca injetadas), keys faltantes são reportadas e ficam intactas, re-execuções são idempotentes e atributos traduzidos passam pelos mesmos guards do runtime: URLs inseguras bloqueadas,
style/srcdocnunca são escritos. - Links nomeados: defina
render.linksemverbaly.config.*(oudata-verbaly-linkspor elemento) e as mensagens rich pré-renderizam elementos<a href>reais, atributos escapados, comjavascript:bloqueado. - SEO multi-idioma: defina
render.baseUrl(ou passe--base-url) e cada página recebe alternateshreflangrecíprocos;--sitemapescreve um sitemap i18n e--cleanremove páginas de idiomas que já não existem. - O sitemap lista apenas páginas que um buscador deveria indexar: uma que redireciona, ou que leva
noindex, fica de fora. Todas as páginas continuam a ser traduzidas e publicadas, então quem chegar a uma recebe-a no seu idioma. Deixe mais de fora por nome comrender.exclude, uma lista de globs contra o caminho dentro da sua pasta de build. - Usa outro data attribute?
--attributeaponta o renderer para ele, do mesmo jeito que o runtime recebe o seu, então os dois lados andam juntos. - Espelhos self-canonical:
rel="canonical"eog:urlsão reescritos para a URL própria de cada idioma, porque um canonical cruzado faria os buscadores ignorarem o hreflang. - Manda o visitante para o seu idioma:
--redirect(ourender.redirect) põe um script minúsculo no topo da tua página inicial que o move antes de se desenhar seja o que for. Só dispara aí, de propósito: um motor de busca que pede uma página profunda tem de receber essa página e não um desvio. Uma escolha guardada ganha sempre, a query e o hash vão junto, e nunca pode entrar em ciclo porque não faz nada quando já estás nesse idioma. Usa{ on: 'all' }se quiseres que encaminhe todas as páginas do idioma de origem, estorageKeypara o apontar à chave onde guardas a escolha. - Site servido a partir de uma subpasta?
--base /app(ourender.base) diz ao renderer onde o site começa, por isso os links dentro de cada idioma leem/app/es/docsem vez de apontarem para o nada. A mesma opção existe emlocaleFromPathelocalePath, por isso o runtime concorda com o build.
Flags
| Flag | Default | Descrição |
|---|---|---|
| --root | cwd | Raiz do projeto |
| --dir | locales | Diretório de catálogos |
| --source | en | Idioma fonte |
| --locales | dos arquivos | Idiomas extras, separados por vírgula |
| --prune | off | Remove chaves sem referências (só no extract) |
| --watch | off | Re-extrai quando os arquivos fonte mudam: extração viva para setups com webpack, Rspack e Rollup (só extract) |
| --locale | en-XA | Id do pseudo-locale (só pseudo) |
| --site | dist | Diretório do site compilado (só render) |
| --base | raiz do site | Subcaminho onde o site é servido (só render) |
| --redirect | off | Manda para o seu idioma quem chega à página inicial (só render) |
As flags são validadas por comando: uma flag que pertence a outro comando termina com um erro acionável em vez de ser ignorada em silêncio: translate --locale es avisa que você queria --locales.
Arquivo de config
As flags vencem verbaly.config.{js,mjs,ts,mts,json} na raiz do projeto, e o plugin recebe as mesmas opções. Todas elas, com o seu valor padrão, vivem em Configuração.
Agentes de código
Seu agente de código pode rodar esse mesmo ciclo sem tocar no terminal, através do servidor @verbaly/mcp. Esse canal tem página própria. Agentes de código cobre o servidor, suas ferramentas e recursos, a Agent Skill instalável e o índice llms.txt que este site serve.