Haz tu pregunta y obtén un resumen del documento referenciando esta página y el proveedor AI de tu elección
El contenido de esta página ha sido traducido con una IA.
Ver la última versión del contenido original en inglésSi tienes una idea para mejorar esta documentación, no dudes en contribuir enviando una pull request en GitHub.
Enlace de GitHub a la documentaciónCopiar el Markdown del documento a la portapapeles
i18next VS Intlayer | Benchmark de internacionalización (i18n) para React y Next.js
i18next es el framework de i18n más utilizado en el ecosistema JavaScript. A través de react-i18next y next-i18next, impulsa una gran parte de las aplicaciones React y Next.js. Intlayer es una alternativa basada en compilador con alcance por componente.
Este artículo los compara mediante mediciones prácticas en lugar de listas de características. Los números provienen de Benchmark Bloom, una suite de código abierto que construye la misma aplicación con cada biblioteca y registra lo que el navegador realmente descarga.
tl;dr:i18nextes el runtime más pesado del benchmark: +77 KB gzip por página en Next.js en la configuración ingenua (naive), +22 KB tras la optimización completa de namespaces + lazy loading. Intlayer añade +0.3 KB. Todas las configuraciones dei18nextexcepto la totalmente aislada (scoped) envían ~90% de cadenas de páginas ajenas; Intlayer envía el 0% de forma predeterminada. Cambiar de idioma con un backend cargado perezosamente costó 123-185 ms conreact-i18nextfrente a 3-4 ms con Intlayer. El adaptador@intlayer/next-i18nextmantiene la API dei18nexty se situó en 150.7 KB por página frente a 218.5 KB del original.
En resumen
- i18next / react-i18next / next-i18next - Maduro, repleto de plugins e independiente del framework. Namespaces, detectores de idioma, backends, ICU mediante plugin,
<Trans>para contenido enriquecido. El contenido está centralizado enlocales/{lng}/{ns}.json. Potente, pero cada optimización (división de namespaces, carga por página, seguridad de tipos) requiere configuración manual que tú debes mantener. - Intlayer - Modelo de contenido centrado en componentes. Los diccionarios
.content.tsse ubican junto al componente al que dan servicio, un compilador en tiempo de compilación aplica tree-shaking y lazy loading por componente y por locale, se generan tipos estrictos de TypeScript a partir de tu contenido y las traducciones faltantes fallan en la compilación. Incluye middleware, helpers de SEO, Editor Visual / CMS y traducción asistida por IA.
Abrir la tabla en una ventana flotante para ver todo el contenido claramente
Los badges se actualizan automáticamente. Las capturas pueden variar con el tiempo.
Comparación directa de características
Abrir la tabla en una ventana flotante para ver todo el contenido claramente
| Característica | Intlayer (react-intlayer / next-intlayer) | i18next (react-i18next / next-i18next) |
|---|---|---|
| Traducciones junto a los componentes | ✅ Sí, .content.ts colocalizado con cada componente | ❌ No, locales/{lng}/{ns}.json centralizado |
| Integración con TypeScript | ✅ Tipos estrictos generados automáticamente a partir del contenido | ⚠️ Básico; claves estrictas requieren extensión CustomTypeOptions y tipado |
| Detección de traducciones faltantes | ✅ Error de TypeScript + error/advertencia en compilación | ⚠️ Fallback en runtime (saveMissing, eco de clave) |
| Contenido enriquecido (JSX / Markdown) | ✅ Soporte directo | ⚠️ <Trans> con marcadores numéricos |
| Soporte de ICU | ⚠️ En desarrollo | ⚠️ Mediante plugin (i18next-icu) |
| Pluralización | ✅ Patrones basados en enumeraciones | ✅ Sufijos _one / _other (Intl.PluralRules) |
| Formateo (fechas, números, monedas) | ✅ useNumber, useDate, ... (Intl integrado) | ⚠️ Formateadores de interpolación o llamadas directas a Intl.* |
| Enrutamiento localizado y middleware | ✅ Proxy/middleware integrado, getMultilingualUrls | ⚠️ No integrado; requiere middleware manual o paquetes de terceros |
| Helpers de SEO (hreflang, sitemap, robots) | ✅ Helpers integrados | ❌ Manual |
| Componentes de servidor síncronos | ✅ useIntlayer de next-intlayer/server funciona en cualquier server component | ⚠️ getFixedT en la página y pasar t como props |
| Tree-shaking (incluir solo contenido usado) | ✅ Por componente, por locale, automatizado por el compilador | ⚠️ Manual: namespaces + lista ns por página + backend |
| Lazy loading | ✅ importMode: 'dynamic' (una línea de configuración) | ✅ Vía plugins de backend (i18next-resources-to-backend, i18next-http-backend) |
| Purgar contenido en desuso | ✅ Diccionarios huérfanos eliminados en el build | ❌ No integrado |
| Pruebas de traducciones faltantes (CLI/CI) | ✅ npx intlayer content test | ⚠️ i18next-parser / paquetes de terceros |
| Traducción asistida por IA | ✅ Integrada, utiliza tus propias claves de API | ❌ No (Locize es un servicio de pago separado) |
| Editor Visual / CMS | ✅ Editor Visual gratuito + CMS opcional | ❌ No (Locize / plataformas externas) |
| Servidor MCP y Agent Skills | ✅ Sí | ❌ No |
| Ecosistema / comunidad | ⚠️ Más reciente pero en rápido crecimiento | ✅ El más amplio y maduro |
El benchmark
Qué se midió
La suite Benchmark Bloom construye la misma aplicación con cada biblioteca: 10 páginas (home, about, blog, careers, contact, FAQ, pricing, products, settings, team), 10 idiomas (en, fr, es, de, it, pt, zh, ja, ko, ru), componentes idénticos y contenido idéntico. Las páginas se miden en en y fr. Cada biblioteca se implementa en hasta cuatro estrategias de carga, desde la configuración ingenua hasta la óptima:
Abrir la tabla en una ventana flotante para ver todo el contenido claramente
| Estrategia | Descripción | Quién suele usarla |
|---|---|---|
| static | Todos los idiomas y páginas incluidos en el bundle (resources incrustados en init()) | Prototipos rápidos, código generado por IA |
| dynamic | Solo el idioma activo se carga vía backend, pero todos los namespaces a la vez | La mayoría de los proyectos |
| scoped-static | Un namespace por ruta, todos empaquetados por adelantado | Poco habitual |
| scoped-dynamic | Un namespace por ruta + carga dinámica por backend. Solo la página actual y el idioma actual | Apps con presupuestos de rendimiento estrictos |
Intlayer no tiene variante "scoped": el compilador aísla el contenido por componente automáticamente, por lo que sus filas static y dynamic ya están optimizadas.
Para cada build, la suite registra:
- Lib size: tamaño gzip de un componente vacío que solo importa la biblioteca i18n. El coste fijo del runtime.
- Page JS: JavaScript gzip descargado por página, promediado entre todas las páginas e idiomas.
- Locale leak %: porcentaje de cadenas traducidas en el JS descargado pertenecientes a un idioma que el usuario no está viendo (evaluado con
enyfr, por lo que 50% significa que el otro idioma medido está totalmente presente; con 10 idiomas empaquetados, el desperdicio real es mayor). - Page leak %: porcentaje de cadenas traducidas en el JS descargado que pertenecen a páginas en las que el usuario no está.
- Component avg: tamaño promedio gzip de cada componente compilado de forma aislada.
- E2E reactivity: tiempo real entre la selección de un nuevo idioma y la actualización de
html[lang]en el DOM (Playwright, 5 iteraciones). - Hydration: duración de la fase de hidratación de React.
Los números a continuación provienen de la ejecución del 2026-09-12 connext-i18next16.3.0,react-i18next17.0.13 eintlayer9.5.1. La aplicación de prueba es deliberadamente pequeña (unas pocas decenas de cadenas por idioma), por lo que los porcentajes de fuga representan un patrón: crecen a medida que aumenta el contenido mientras el coste del runtime permanece fijo.
Resultados en Next.js (next-i18next)
Abrir la tabla en una ventana flotante para ver todo el contenido claramente
| Biblioteca | Estrategia | Lib size (gz) | Page JS avg (gz) | Locale leak | Page leak | Component avg (gz) | Reactividad E2E | Hidratación |
|---|---|---|---|---|---|---|---|---|
| base (sin i18n) | - | 0.0 KB | 141.0 KB | 0.0% | 0.0% | 0.9 KB | 13.4 ms | 11.8 ms |
next-i18next | static | 19.7 KB | 218.5 KB | 0.0% | 89.8% | 78.5 KB | 16.4 ms | 15.6 ms |
next-i18next | dynamic | 19.7 KB | 169.5 KB | 50.0% | 89.8% | 26.1 KB | 15.4 ms | 27.7 ms |
next-i18next | scoped-static | 19.7 KB | 220.1 KB | 0.0% | 89.8% | 78.9 KB | 16.4 ms | 14.7 ms |
next-i18next | scoped-dynamic | 19.7 KB | 163.4 KB | 0.0% | 0.0% | 27.1 KB | 15.9 ms | 15.1 ms |
next-intlayer | static | 5.5 KB | 141.3 KB | 0.0% | 0.0% | 8.5 KB | 15.5 ms | 16.9 ms |
next-intlayer | dynamic | 5.5 KB | 141.3 KB | 0.0% | 0.0% | 6.9 KB | 15.3 ms | 15.9 ms |
@intlayer/next-i18next (compat) | static | 9.4 KB | 150.7 KB | 0.0% | 0.0% | 9.7 KB | 10.7 ms | 11.3 ms |
@intlayer/next-i18next (compat) | dynamic | 9.4 KB | 150.7 KB | 0.0% | 0.0% | 9.7 KB | 11.9 ms | 10.6 ms |
Cómo interpretar los resultados
- Coste del runtime. El núcleo de
i18nextjunto conreact-i18nextrepresenta el runtime más grande evaluado: 19.7 KB gzip para un componente vacío, frente a 5.5 KB denext-intlayer. - La configuración ingenua es costosa. Incrustar
resourceseninit()genera 218.5 KB por página, +77.5 KB respecto a la aplicación base. Cada página transporta todos los namespaces. - Optimizar requiere un largo camino. Migrar a un backend (
dynamic) ahorra 49 KB pero mantiene un 90% de fuga en páginas ajenas y, en esta configuración, la mitad de las cadenas pertenecen al idioma incorrecto. Dividir en namespaces por ruta (scoped-dynamic) finalmente alcanza 0% de fuga con 163.4 KB, manteniéndose +22.4 KB por página por encima de Intlayer (141.3 KB), que no requirió configuración manual. - Tamaño de componentes. Un componente que invoca
useTranslation()compila entre 26 y 79 KB según la configuración; el mismo componente conuseIntlayer()compila en solo 6.9 KB. - La hidratación sube a 27.7 ms en la configuración
dynamic: la instancia de i18next se inicializa y resuelve su backend en el cliente antes de que React pueda hidratar el DOM.
Resultados en TanStack Start (react-i18next)
La misma aplicación de prueba en TanStack Start con react-i18next puro, eliminando las particularidades de Next.js.
Abrir la tabla en una ventana flotante para ver todo el contenido claramente
| Biblioteca | Estrategia | Lib size (gz) | Page JS avg (gz) | Locale leak | Page leak | Component avg (gz) | Reactividad E2E | Hidratación |
|---|---|---|---|---|---|---|---|---|
| base (sin i18n) | - | 0.0 KB | 111.0 KB | 0.0% | 0.0% | 0.7 KB | 8.1 ms | 21.6 ms |
react-i18next | static | 18.4 KB | 180.3 KB | 50.0% | 89.8% | 24.3 KB | 12.9 ms | 85.1 ms |
react-i18next | dynamic | 18.4 KB | 136.4 KB | 23.1% | 89.8% | 24.8 KB | 123.1 ms | 32.9 ms |
react-i18next | scoped-static | 18.4 KB | 184.2 KB | 50.7% | 89.8% | 25.3 KB | 185.1 ms | 25.2 ms |
react-i18next | scoped-dynamic | 18.4 KB | 127.2 KB | 0.0% | 0.0% | 26.7 KB | 17.6 ms | 11.3 ms |
intlayer | static | 5.0 KB | 125.8 KB | 50.0% | 0.0% | 8.1 KB | 3.2 ms | 11.5 ms |
intlayer | dynamic | 5.0 KB | 118.6 KB | 0.0% | 0.0% | 6.3 KB | 3.6 ms | 14.1 ms |
Cómo interpretar los resultados
- La app básica con
react-i18nextenvía +69 KB por página sobre la aplicación base, y la hidratación tarda 85 ms (4 veces la base) porque todo el árbol de recursos se analiza y registra en el cliente antes del primer renderizado. - El cambio de idioma expone el retraso del lazy loading. Cuando los recursos se cargan a petición mediante un backend, cambiar el idioma requiere un ciclo de red antes de actualizar
html[lang]: 123 ms endynamic, 185 ms enscoped-static. Intlayer actualiza el DOM en 3-4 ms en ambos casos: el cambio se aplica de inmediato sin bloquearse por peticiones de red. - La configuración totalmente optimizada
scoped-dynamicalcanza el 0% de fuga con 127.2 KB, quedando todavía +8.6 KB por encima de Intlayer endynamic, tras requerir un mapa rutas-namespaces, un backend de recursos y límites Suspense en cada ruta. - La fila
staticde Intlayer ya cuenta con 0% de fuga de página porque solo se empaquetan los diccionarios importados por los componentes de esa página. ActivarimportMode: 'dynamic'elimina además la fuga de idiomas. - Tamaño por componente: 24-27 KB con
react-i18nextfrente a 6-8 KB con Intlayer.useTranslation()vincula cada componente a la instancia global de i18next.
¿Por qué esta diferencia? Instancia global vs. diccionarios compilados
i18next fue diseñado en 2012 como un runtime: una instancia global almacena un almacén de recursos, los plugins la amplían y t() busca claves en tiempo de render. Esto le otorga una gran versatilidad (cualquier framework, backend o formato), pero también la hace pesada:
Copiar el código al portapapeles
La instancia no puede saber qué claves necesitará un componente, por lo que retiene todos los namespaces cargados. Optimizar exige que tú dividas los catálogos en namespaces, que tú listes los namespaces que requiere cada página y que tú mantengas esa lista actualizada cuando los componentes cambian de ubicación. Como señalan las notas del benchmark: "mantener la seguridad de tipos y saber con exactitud qué namespace incluir en cada página es una pesadilla".
Intlayer elimina la instancia global. El contenido se define junto al componente y el compilador resuelve el árbol de dependencias en tiempo de compilación:
Copiar el código al portapapeles
@intlayer/swc / @intlayer/babel detecta qué componente importa cada diccionario, empaqueta únicamente esos, solo para el idioma activo, y descarta lo que no se usa. El patrón "scoped-dynamic" se convierte en el resultado natural del build en vez de un protocolo manual que el equipo deba mantener.
Para conseguir los datos de la filadynamic, definedictionary.importMode: 'dynamic'enintlayer.config.ts. Consulta la documentación de optimización de bundle.
Experiencia de desarrollo
Configuración
next-i18next (App Router)
Copiar el código al portapapeles
Más un I18nProvider del lado del cliente que replica la instancia con idénticas opciones, generateStaticParams y una lista de namespaces por página.
Intlayer
Copiar el código al portapapeles
Copiar el código al portapapeles
Componente de cliente
react-i18next
Copiar el código al portapapeles
Copiar el código al portapapeles
La página que renderiza este componente debe cargar el namespaceabout, yt("counter.label")es un string genérico a menos que extiendasCustomTypeOptions.
Intlayer
Copiar el código al portapapeles
Copiar el código al portapapeles
label e increment están tipados de forma estricta; cualquier errata genera un error en TypeScript y un texto faltante en francés produce un fallo en el build.
Componente de servidor síncrono
next-i18next
Copiar el código al portapapeles
La página invoca i18n.getFixedT(locale, "about") y pasa t y locale hacia abajo vía props.
Intlayer
Copiar el código al portapapeles
Conserva la API de i18next, obtén las ventajas de Intlayer
No es necesario reescribir tus componentes para beneficiarte de las cifras del benchmark. @intlayer/i18next, @intlayer/react-i18next y @intlayer/next-i18next son adaptadores compatibles: useTranslation, t(), <Trans>, {{interpolation}}, plurales _one / _other, sufijos de contexto y returnObjects continúan funcionando, servidos desde los diccionarios generados por el compilador de Intlayer.
Copiar el código al portapapeles
Copiar el código al portapapeles
En el benchmark, la versión adaptada de la misma app de Next.js pasó de 218.5 KB a 150.7 KB por página, de 78.5 KB a 9.7 KB por componente, de ~90% de fuga de página a 0%, y la hidratación de 15.6 ms a 11.3 ms, con el código de la aplicación intacto. Tus archivos actuales locales/{lng}/{ns}.json pueden seguir siendo la fuente de la verdad con el plugin de sincronización JSON.
Consulta las guías de migración: i18next, react-i18next, next-i18next.
¿Cuándo elegir cada una?
- Elige i18next si necesitas su ecosistema de plugins (detectores, backends, ICU, Locize), si traduces fuera de React (servicios Node, JavaScript vanilla u otros frameworks), si tu equipo ya lo domina o si tu plataforma de traducción requiere
locales/{lng}/{ns}.json. Reserva tiempo para separar catálogos en namespaces, configurar un backend y mantener el mapa rutas-namespaces si el rendimiento es prioritario. - Elige Intlayer si buscas contenido con alcance por componente, TypeScript estricto, detección de claves faltantes en tiempo de compilación, tree-shaking y lazy loading automáticos, cambio instantáneo de idioma, componentes de servidor síncronos y herramientas editoriales nativas (Editor Visual, CMS, traducción con IA, servidor MCP). Ideal para aplicaciones modulares y sistemas de diseño.
- Elige los adaptadores
@intlayer/*-i18nextsi ya utilizas i18next y deseas obtener las mejoras de bundle y reactividad sin reescribir tus componentes.
Comparativas relacionadas
- next-intl vs Intlayer (mismo benchmark)
- Lingui vs Intlayer (mismo benchmark)
- vue-i18n vs Intlayer benchmark (mismo benchmark)
- next-i18next vs next-intl vs Intlayer
- react-i18next vs react-intl vs Intlayer
- ¿Está desactualizado i18next?
Estrellas en GitHub
Las estrellas en GitHub son un reflejo de la popularidad, la confianza de la comunidad y la relevancia a largo plazo de un proyecto. Aunque no miden directamente la calidad técnica, ilustran cuántos desarrolladores encuentran útil el proyecto, siguen sus avances y planean adoptarlo.
Conclusión
i18next se ganó su lugar: funciona en todas partes, dispone de plugins para todo y acumula más de una década de soporte. El benchmark evidencia el coste de un enfoque centrado en el runtime. La configuración habitual en la mayoría de equipos añade +70-77 KB gzip por página, fuga ~90% del contenido de páginas ajenas y requiere más de 100 ms para un cambio de idioma diferido. Lograr 0% de fuga es factible, pero demanda un backend, un namespace por ruta y un mapa manual, quedando todavía +9-22 KB por encima de Intlayer.
Intlayer traslada ese esfuerzo al compilador. Los diccionarios por componente, la carga perezosa por idioma y la eliminación de contenido huérfano son resultados automáticos de la compilación. En la misma aplicación: +0.3 KB por página, 0% de fuga, componentes de 3 a 10 veces más pequeños y cambio de idioma en 3-4 ms.
Todos los datos brutos, las aplicaciones de prueba y los scripts se encuentran en el repositorio de Benchmark Bloom. Puedes ejecutarlos tú mismo.
Consulta la sección '¿Por qué Intlayer?' para más detalles.
Comentarios
Aún no hay comentarios. Sé el primero en compartir tus pensamientos.
