Guías
Migrar desde i18next
Las dos herramientas hablan JSON, así que es más fácil de lo que parece: tus keys y traducciones portan casi directo, los sufijos de plural se fusionan en un solo mensaje y el cambio de runtime es casi buscar-y-reemplazar. De paso sueltas ~20 KB de runtime.
Qué mapea con qué
El modelo mental se traslada uno a uno, y lo único que cambia es la sintaxis:
| 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 |
Conserva tus keys: los catálogos portan casi directo
Los catálogos de Verbaly son JSON plano sin formato propietario. Aplana los grupos anidados a keys con puntos y fusiona los sufijos _one/_other en un solo mensaje con variantes, y cada traducción existente sobrevive:
{
"inbox": { "title": "Inbox" },
"messages_one": "one message",
"messages_other": "{{count}} messages"
}{
"inbox.title": "Inbox",
"messages": "{count | one: one message | other: # messages}"
}Los namespaces (ns:key) pasan a formar parte de la key con puntos (ns.key). El # dentro de una variante renderiza el count ya localizado, sin interpolación formattedCount aparte.
Cambia el runtime
Las keys existentes siguen funcionando vía t('key', params), así que los componentes migrados apenas cambian. En 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 typedEl copy nuevo se salta la ceremonia de keys: escribe el texto como tagged template y deja que el compilador lo extraiga, o conserva ids legibles con t.id. Los adapters de Vue y Svelte siguen el mismo patrón, y las páginas de HTML plano pueden bindear con data-verbaly.
t`Hola ${name}`; // key + types generated
t.id('inbox.title')`Inbox`; // readable id, i18next-styleDetección de idioma y carga lazy
El plugin detector y el backend HTTP desaparecen. resolveLocale lee storage → navigator.languages → fallback (SSR-safe, con narrowing BCP-47), persistLocale guarda la elección y sincroniza <html lang>, y setLocale carga el chunk de ese idioma bajo demanda:
import { resolveLocale, persistLocale } from 'verbaly';
import { setLocale, getLocale } from 'virtual:verbaly';
await setLocale(resolveLocale({ supported: ['en', 'es', 'pt'] }));
persistLocale(getLocale());Migra incremental
No hace falta un big-bang: el runtime de Verbaly pesa ~3 KB, así que ambas librerías pueden convivir mientras migras una página o namespace a la vez. Un orden pragmático:
npx verbaly init+ el plugin de build, así el toolchain corre junto a i18next desde el día uno.- Porta los catálogos: aplana keys, fusiona sufijos de plural y baja las llaves de interpolación de cuatro a dos.
- Cambia los hooks página por página (
useTranslation→useT) y borra cada namespace de i18next cuando quede vacío. - Enciende el gate:
npx verbaly checken CI,npx verbaly pseudopara cazar strings hardcodeados que la migración pasó por alto.