Guias
Migrar do i18next
As duas ferramentas falam JSON, então é mais fácil do que parece: suas keys e traduções portam quase direto, os sufixos de plural se fundem em uma única mensagem e a troca de runtime é quase buscar-e-substituir. De quebra você solta ~20 KB de runtime.
O que mapeia com o quê
O modelo mental se transfere um a um, e a única coisa que muda é a sintaxe:
| i18next | Verbaly |
|---|---|
i18next.init({ … }) | npx verbaly init + one build plugin |
t('inbox.title') | t('inbox.title'): same call, keys now typed |
{{name}} | {name} |
messages_one / messages_other | {count | one: … | other: # …} |
format: 'currency' (interpolation) | {price:currency/EUR} (native Intl) |
i18next-browser-languagedetector | resolveLocale() + persistLocale() |
i18next-http-backend (lazy load) | per-locale code-splitting, built into the plugin |
useTranslation() (react-i18next) | useT() (@verbaly/react) |
| Trans component | Trans component: values / components map over |
i18next.changeLanguage('es') | await setLocale('es') |
saveMissing, runtime fallbacks | npx verbaly check: the build fails instead |
Mantenha suas keys: os catálogos portam quase direto
Os catálogos do Verbaly são JSON plano sem formato proprietário. Achate os grupos aninhados em keys com pontos e funda os sufixos _one/_other em uma única mensagem com variantes, e cada tradução existente sobrevive:
{
"inbox": { "title": "Inbox" },
"messages_one": "one message",
"messages_other": "{{count}} messages"
}{
"inbox.title": "Inbox",
"messages": "{count | one: one message | other: # messages}"
}Namespaces (ns:key) viram parte da key com pontos (ns.key). O # dentro de uma variante renderiza o count já localizado, sem interpolação formattedCount à parte.
Troque o runtime
As keys existentes continuam funcionando via t('key', params), então os componentes migrados quase não mudam. Em React:
import { useTranslation } from 'react-i18next';
const { t } = useTranslation();
t('inbox.title');
t('messages', { count });import { useT } from '@verbaly/react';
const t = useT();
t('inbox.title');
t('messages', { count }); // params now typedO texto novo pula a cerimônia de keys: escreva como tagged template e deixe o compilador extrair, ou mantenha ids legíveis com t.id. Os adapters de Vue e Svelte seguem o mesmo padrão, e páginas de HTML puro podem fazer bind com data-verbaly.
t`Hola ${name}`; // key + types generated
t.id('inbox.title')`Inbox`; // readable id, i18next-styleDetecção de idioma e carga lazy
O plugin detector e o backend HTTP desaparecem. resolveLocale lê o storage → navigator.languages → fallback (SSR-safe, com narrowing BCP-47), persistLocale guarda a escolha e sincroniza <html lang>, e setLocale carrega o chunk daquele idioma sob demanda:
import { resolveLocale, persistLocale } from 'verbaly';
import { setLocale, getLocale } from 'virtual:verbaly';
await setLocale(resolveLocale({ supported: ['en', 'es', 'pt'] }));
persistLocale(getLocale());Migre incrementalmente
Não precisa de big-bang: o runtime do Verbaly pesa ~3 KB, então as duas bibliotecas podem conviver enquanto você migra uma página ou namespace por vez. Uma ordem pragmática:
npx verbaly init+ o plugin de build, então o toolchain roda ao lado do i18next desde o dia um.- Porte os catálogos: achate as keys, funda os sufixos de plural e reduza as chaves de interpolação de quatro para duas.
- Troque os hooks página por página (
useTranslation→useT) e apague cada namespace do i18next quando esvaziar. - Ligue o gate:
npx verbaly checkno CI,npx verbaly pseudopara caçar strings hardcoded que a migração deixou passar.