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:

i18nextVerbaly
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-languagedetectorresolveLocale() + persistLocale()
i18next-http-backend (lazy load)per-locale code-splitting, built into the plugin
useTranslation() (react-i18next)useT() (@verbaly/react)
Trans componentTrans component: values / components map over
i18next.changeLanguage('es')await setLocale('es')
saveMissing, runtime fallbacksnpx 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:

locales/en.json (i18next)
{
  "inbox": { "title": "Inbox" },
  "messages_one": "one message",
  "messages_other": "{{count}} messages"
}
locales/en.json (Verbaly)
{
  "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:

before (react-i18next)
import { useTranslation } from 'react-i18next';

const { t } = useTranslation();
t('inbox.title');
t('messages', { count });
after (@verbaly/react)
import { useT } from '@verbaly/react';

const t = useT();
t('inbox.title');
t('messages', { count });  // params now typed

El 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.

src/app.tsx
t`Hola ${name}`;                    // key + types generated
t.id('inbox.title')`Inbox`;      // readable id, i18next-style

Detecció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:

src/main.ts
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:

  1. npx verbaly init + el plugin de build, así el toolchain corre junto a i18next desde el día uno.
  2. Porta los catálogos: aplana keys, fusiona sufijos de plural y baja las llaves de interpolación de cuatro a dos.
  3. Cambia los hooks página por página (useTranslationuseT) y borra cada namespace de i18next cuando quede vacío.
  4. Enciende el gate: npx verbaly check en CI, npx verbaly pseudo para cazar strings hardcodeados que la migración pasó por alto.
Copiado en el portapapeles