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/i18next: Misma API, Distinto Bundle

@intlayer/i18next, @intlayer/react-i18next y @intlayer/next-i18next son adaptadores de compatibilidad. Exponen la API de i18next que tu código ya utiliza (useTranslation, t(), <Trans>, i18n.changeLanguage(), getFixedT, serverSideTranslations...) y la sirven a partir de diccionarios compilados por Intlayer. Los componentes no cambian. El runtime debajo de ellos sí.
Este artículo mide ese reemplazo en la misma aplicación Next.js, construida una vez con next-i18next y otra con @intlayer/next-i18next. Las cifras proceden de Benchmark Bloom. Para comparar i18next e Intlayer como librerías independientes, consulta i18next vs Intlayer. Este análisis se enfoca en lo que transforma el adaptador cuando mantienes tu código tal cual.
tl;dr: En la misma aplicación Next.js, sustituirnext-i18nextpor@intlayer/next-i18nextredujo el JavaScript por página de 218.5 KB a 150.7 KB gzip (configuración inicial) y superó a la configuración denext-i18nexttotalmente optimizada (163.4 KB) por 12.7 KB. El componente promedio pasó de 78.5 KB a 9.7 KB, la fuga de cadenas hacia otras páginas bajó de ~90% a 0%, la hidratación se redujo de 15.6 ms a 11.3 ms, y el runtime de 19.7 KB a 9.4 KB. No se editó ningún componente; solo se modificó un archivo de provider. Los plugins dei18next(backends, detectores de idioma) se aceptan pero no hacen nada: no queda nada que cargar o detectar en tiempo de ejecución.
Qué es @intlayer/i18next
i18next es un runtime. i18n.init({ resources }) o un plugin backend carga locales/{lng}/{ns}.json en una instancia global; useTranslation("about") suscribe el componente a ella; t("title") busca la clave en el momento del renderizado. Los namespaces, la carga diferida (lazy loading), las listas de namespaces por página y la seguridad de tipos son responsabilidad tuya a la hora de configurar y mantener.
Los adaptadores conservan la API y reemplazan la instancia:
- Alias de importación.
createNextI18nPlugin()de@intlayer/next-i18next/plugin(owithI18next) envuelvewithIntlayery agrega alias de Webpack / Turbopack para quenext-i18next,react-i18nextei18nextresuelvan hacia sus equivalentes en@intlayer/*. En Vite,reactI18nextVitePlugin()de@intlayer/react-i18next/pluginhace lo mismo. No es necesario renombrar ninguna importación. - JSON como fuente de verdad. El plugin
syncJSONlee tus archivos existenteslocales/{lng}/{ns}.jsonconformat: "i18next"(de modo que{{name}}, anidamiento$t(),_one/_othery sufijos de contexto se procesen adecuadamente) y reescribe las traducciones cuando el CLI o el CMS las actualizan. - Vinculación en el punto de llamada. El paso de optimización de Intlayer reescribe
useTranslation("about")en una llamada que recibe directamente el diccionarioabout, en el idioma activo. El componente deja de consultar el store global.
Copiar el código al portapapeles
Copiar el código al portapapeles
Esa reescritura es la responsable de la drástica reducción en el tamaño de los componentes y en la fuga de contenido por página que se detalla a continuación.
Qué conservan, ignoran y no reemplazan los adaptadores
Abrir la tabla en una ventana flotante para ver todo el contenido claramente
API de i18next | Con @intlayer/* |
|---|---|
useTranslation("ns"), useTranslation("ns", { keyPrefix }) | ✅ Se conserva. Vinculado al diccionario ns en tiempo de compilación; claves tipadas contra tu contenido |
t("key", { name }), {{interpolation}}, anidamiento $t(key) | ✅ Se conserva |
Plurales key_one / key_other, contexto key_male, returnObjects | ✅ Se conserva. Plurales evaluados con Intl.PluralRules |
<Trans> con components, etiquetas numeradas <1>...</1>, values | ✅ Se conserva |
withTranslation, Translation, I18nContext | ✅ Se conserva |
i18n.changeLanguage(), i18n.language, i18n.dir(), on("languageChanged") | ✅ Se conserva. changeLanguage controla el idioma de Intlayer |
getFixedT(lng, ns, keyPrefix), i18n.exists(), hasLoadedNamespace() | ✅ Se conserva |
i18n.use(Backend).use(LanguageDetector).init({...}) | ⚠️ use() llama al init del plugin y finaliza; backends y detectores no tienen nada que cargar o detectar |
init({ resources }), addResourceBundle() | ⚠️ resources se ignora con una advertencia en desarrollo; elimina los imports JSON para obtener los ahorros de bundle |
I18nextProvider i18n={i18n} | ⚠️ Renderiza un IntlayerProvider; la prop i18n se ignora. En App Router, pasa el idioma (ver abajo) |
serverSideTranslations(locale, ["common"]) (next-i18next) | ⚠️ Devuelve la estructura esperada y no carga nada. Seguro de mantener, seguro de eliminar |
appWithTranslation(App) (next-i18next) | ✅ Se conserva |
next-i18next.config.js | ⚠️ No se lee. Los idiomas provienen de intlayer.config.ts |
useTranslation() sin namespace | ✅ Funciona contra el diccionario global translation del archivo completo (splitKeys: false) |
El benchmark
Qué se midió
La suite Benchmark Bloom construye la misma aplicación con cada configuración: 10 páginas (inicio, nosotros, blog, empleo, contacto, preguntas frecuentes, precios, productos, ajustes, equipo), 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.
next-i18next se evaluó bajo cuatro estrategias de carga, desde el JSON de cada idioma importado en resources (static) hasta un namespace por ruta, cargado bajo demanda mediante un backend (scoped-dynamic). El adaptador se probó sobre los mismos componentes que la configuración básica, modificando únicamente next.config.ts, intlayer.config.ts y el archivo de provider. No cuenta con variante manual "scoped": el compilador asigna el alcance del contenido por componente.
Para cada build, la suite registra:
- Tamaño de la lib: tamaño gzip de un componente vacío que solo importa la librería de i18n.
- JS por página: promedio de JavaScript gzip descargado por página en todas las rutas e idiomas.
- % de fuga de idioma: porcentaje de cadenas traducidas en el JS descargado que pertenecen a un idioma que el usuario no está viendo.
- % de fuga de página: porcentaje de cadenas traducidas en el JS descargado que pertenecen a una página en la que el usuario no está.
- Promedio de componente: tamaño gzip promedio de cada componente compilado en aislamiento.
- Reactividad E2E: tiempo real medido entre la selección de un nuevo idioma y la actualización de
html[lang]en el DOM (Playwright, 5 iteraciones). - Hidratación: duración de la fase de hidratación de React.
Los valores siguientes proceden de la ejecución del 12-09-2026 connext-i18next16.3.0 (react-i18next17.0.13,i18next26.4.2) y@intlayer/next-i18next9.5.1. La aplicación de prueba es intencionadamente compacta (unas decenas de cadenas por idioma), por lo que los porcentajes de fuga describen un patrón: aumentan conforme crece tu contenido mientras el costo del runtime permanece fijo.
Resultados en Next.js
Selecciona las métricas y las bibliotecas que te interesen:
Métrica
Carga JSON dinámica
Carga traducciones en tiempo de ejecución
JSON con alcance (namespacing)
Espacios de nombres de traduction por página
¿Qué es esta métrica?
El tamaño total comprimido en gzip del paquete de la biblioteca de internacionalización. Solo incluye el proveedor y la lógica de recuperación de contenido después del tree-shaking y la minificación.
¿Por qué es importante?
Un tamaño de biblioteca más pequeño reduce la carga útil inicial de JavaScript, lo que acelera el tiempo de descarga y ejecución en el cliente.
Ver como
Abrir la tabla en una ventana flotante para ver todo el contenido claramente
| Configuración | Estrategia | Tamaño lib (gz) | JS pág prom (gz) | Fuga idioma | Fuga pág | Comp prom (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 |
@intlayer/next-i18next | static | 9.4 KB | 150.7 KB | 0.0% | 0.0% | 9.7 KB | 10.7 ms | 11.3 ms |
@intlayer/next-i18next | dynamic | 9.4 KB | 150.7 KB | 0.0% | 0.0% | 9.7 KB | 11.9 ms | 10.6 ms |
next-intlayer (nativo) | static | 5.5 KB | 141.3 KB | 0.0% | 0.0% | 8.5 KB | 15.5 ms | 16.9 ms |
next-intlayer (nativo) | dynamic | 5.5 KB | 141.3 KB | 0.0% | 0.0% | 6.9 KB | 15.3 ms | 15.9 ms |
Cómo interpretarlo
- 68 KB menos por página frente a la configuración inicial.
resources: { en, fr, ... }envía cada idioma y cada namespace en cada página: 218.5 KB. La compilación con adaptador para los mismos componentes queda en 150.7 KB. Además supera a la mejor configuración denext-i18next(163.4 KB, un namespace por ruta, cargado bajo demanda) por 12.7 KB, porque el runtime dei18nextpor sí solo pesa 19.7 KB contra 9.4 KB. - La fuga cae al 0% sin modificar ningún componente. Cada configuración de
next-i18next, excepto la totalmente aislada, envía ~90% de cadenas de otras páginas. La filadynamicresulta más perjudicial de lo que parece: no elimina la fuga de página e introduce un 50% de fuga de idioma, dado que el backend por idioma continúa trayendo todo el namespacetranslation. El adaptador alcanza 0% / 0% directamente desde el código original. - Componentes: 8 veces más pequeños. Un componente con
useTranslation()compilado en aislamiento promedia 78.5 KB conresourcesincrustado y 26-27 KB con backend, debido a quetqueda atado al store global. Con el adaptador promedia 9.7 KB. - Hidratación y cambio de idioma más rápidos. La hidratación pasa de 15.6 ms a 11.3 ms (y de 27.7 ms en la configuración
dynamic, donde la petición del backend bloquea la ruta crítica). El cambio de idioma pasa de 15-16 ms a 11-12 ms. - El adaptador no es el runtime nativo.
next-intlayerregistra 141.3 KB, apenas +0.3 KB sobre la app base. El adaptador carga con la superficie de la API dei18next(sintaxis de interpolación, sufijos de plural y contexto, análisis de etiquetas<Trans>) sobre el núcleo de Intlayer: 9.4 KB y +9.4 KB por página respecto al nativo. Es un puente de transición, no el destino final.
Tabla completa, cada biblioteca y cada estrategia, en el informe de benchmark de Next.js.
Resultados en TanStack Start (react-i18next)
Para Vite y TanStack Start, el benchmark compara react-i18next estándar con intlayer:
Abrir la tabla en una ventana flotante para ver todo el contenido claramente
| Biblioteca | Estrategia | Tamaño lib (gz) | JS pág prom (gz) | Fuga idioma | Fuga pág | Comp prom (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 | dynamic | 18.4 KB | 136.4 KB | 23.1% | 89.8% | 24.8 KB | 123.1 ms | 32.9 ms |
intlayer | dynamic | 5.0 KB | 118.6 KB | 0.0% | 0.0% | 6.3 KB | 3.6 ms | 14.1 ms |
Tabla completa en el informe de benchmark de TanStack Start.
El adaptadorreact-i18nexten Vite / TanStack Start no formó parte de esta prueba. La referencia base dereact-i18nexten TanStack Start se encuentra en i18next vs Intlayer: 127-184 KB por página y 123-185 ms en el cambio de idioma cuando el backend se carga de forma diferida.
Por qué varían las cifras

Nada en components/ ha cambiado, por lo que las ganancias se deben al destino al que se vincula useTranslation.
Con i18next, la vinculación se realiza con la instancia global. Todo lo cargado en ella (todos los idiomas en static, el namespace entero del idioma activo en dynamic) resulta accesible desde cualquier componente que invoque useTranslation(). El empaquetador no puede dividir por debajo de lo que la instancia retiene, y el runtime no puede prever qué claves solicitará cada componente.
Copiar el código al portapapeles
Todo lo que contiene la instancia se envía a cada página, y el desperdicio crece en dos ejes, páginas e idiomas:

Con @intlayer/next-i18next, la vinculación se establece directamente con el diccionario. syncJSON transforma cada archivo de namespace en un diccionario; el paso de optimización proporciona al componente el diccionario requerido como una importación que el empaquetador puede rastrear y dividir por página y por idioma.
Copiar el código al portapapeles
i18n/i18n.ts y su importación de resources se convierten en código muerto. De ahí provienen los 68 KB de ahorro.
Migración en tres pasos
Instalación
bashCopiar códigoCopiar el código al portapapeles
El comando detecta
i18next/react-i18next/next-i18next, instalaintlayer, el paquete correspondiente al framework (next-intlayeroreact-intlayer), el adaptador@intlayer/*adecuado y@intlayer/sync-json-plugin, además de preconfigurarintlayer.config.ts. Mantén instalados los paquetes originales: actúan como dependencias par y suministran los tipos.Apunta Intlayer a tus archivos de idioma
intlayer.config.tsCopiar códigoCopiar el código al portapapeles
Si dispones de un único archivo
translation.jsonpor idioma (el namespace predeterminado de i18next), definesplitKeys: falsepara que el archivo completo permanezca como un solo diccionario y las llamadas simples auseTranslation()sigan resolviéndose.Agrega el plugin
next.config.tsCopiar códigoCopiar el código al portapapeles
En App Router, los componentes cliente obtienen su idioma mediante el segmento
[locale]. Dado que elI18nextProviderdel adaptador no recibe idioma, reemplázalo una única vez en tu archivo de provider:components/AppProviders.tsxCopiar códigoCopiar el código al portapapeles
Todos los componentes inferiores seguirán invocando
useTranslation().vite.config.tsCopiar códigoCopiar el código al portapapeles
reactI18nextVitePlugin()envuelvevite-intlayery crea los alias dereact-i18nextei18next. Para un proyecto sin React,i18nextVitePlugin()de@intlayer/i18next/plugincrea el alias dei18nexten solitario.
Qué puedes eliminar a continuación
Abrir la tabla en una ventana flotante para ver todo el contenido claramente
| Archivo / patrón | Motivo |
|---|---|
resources: { en, fr, ... } y las importaciones JSON | Ignorados por el adaptador. Aquí residían los 68 KB |
i18next-http-backend, i18next-resources-to-backend | Nada que consultar en tiempo de ejecución |
i18next-browser-languagedetector | La detección de idioma la gestiona el enrutamiento de Intlayer (prefijo URL, cookie, cabecera) |
serverSideTranslations() en getStaticProps | Devuelve una estructura vacía; inocuo, pero redundante |
next-i18next.config.js | No se lee. Los idiomas residen en intlayer.config.ts |
Listas ns: [...] por página | El compilador determina los namespaces por componente |
Qué ganas más allá de los bytes
- Claves tipadas.
useTranslation("about")se tipa contra el diccionario compiladoabout;t("does.not.exist")genera un error de TypeScript en lugar de devolver la clave como texto. npx intlayer testbloquea la CI ante cualquier clave ausente en cualquier idioma.npx intlayer filltraduce las claves faltantes con tu propia clave de proveedor (OpenAI, Anthropic, Mistral, Gemini...) y las escribe de nuevo enlocales/{lng}/{ns}.json.- Editor Visual y CMS operan sobre el mismo JSON, permitiendo a los traductores editar mediante interfaz gráfica mientras los archivos se actualizan.
- Transición progresiva a
.content.ts. Cualquier componente puede migrar deuseTranslation("about")auseIntlayer("about")con un archivo de contenido dedicado. Los archivos JSON y.content.tsconviven sin conflicto.
Límites que debes conocer antes de empezar
i18n.use(HttpBackend) llama al init del plugin y nada más. Si tu aplicación dependía de obtener traducciones de un CMS en tiempo de ejecución, ese flujo desaparece; usa el CMS de Intlayer o los comandos intlayer pull / push en su lugar. La detección de idioma pasa a ser la configuración de enrutamiento de Intlayer (prefijo de URL, cookie, cabecera).
A diferencia de otros adaptadores, @intlayer/i18next no utiliza resources en línea como fallback. Cada clave debe existir en los diccionarios sincronizados, lo que verifica intlayer test.
Un solo archivo, mostrado arriba. Pages Router con appWithTranslation no requiere nada.
localePath, fallbackLng, reloadOnPrerender y similares no tienen equivalente; los idiomas y el fallback provienen de intlayer.config.ts.
9.4 KB de runtime y +9.4 KB por página respecto a next-intlayer. Una vez que todos los componentes hayan pasado a useIntlayer, elimínalo.
Comparación de funcionalidades
Más allá de los bytes, lo que ofrece cada opción:
Abrir la tabla en una ventana flotante para ver todo el contenido claramente
| Funcionalidad | i18next / react-i18next / next-i18next | Adaptadores @intlayer/* | Intlayer nativo |
|---|---|---|---|
Tus llamadas a t(), useTranslation, <Trans> | ✅ | ✅ Sin cambios | ❌ Migradas a useIntlayer |
| Tamaño del runtime (gzip, Next.js) | 19.7 KB | 9.4 KB | 5.5 KB |
| Fuga de otras páginas sin namespaces manuales | ~90% | 0% | 0% |
| Claves tipadas | ⚠️ Declaración manual | ✅ Desde los diccionarios compilados | ✅ Autogeneradas |
| Backends y plugins en runtime | ✅ Ecosistema completo de plugins | ❌ Inertes | ❌ No aplica, usa el CMS |
| Contenido junto a los componentes | ❌ JSON centralizado | ⚠️ JSON, .content.ts puede coexistir | ✅ .content.ts junto a cada componente |
| Traducciones faltantes en CI | ⚠️ No integrado | ✅ npx intlayer test | ✅ npx intlayer test |
| Traducción con IA | ❌ No | ✅ npx intlayer fill | ✅ npx intlayer fill |
| Editor visual / CMS | ❌ Mediante plataformas externas | ✅ Sobre el mismo JSON | ✅ Sí |
| Ecosistema / comunidad | ✅ Muy grande | ⚠️ Más pequeño, en rápido crecimiento | ⚠️ Más pequeño, en rápido crecimiento |
Los tamaños de runtime provienen de la ejecución en Next.js descrita arriba.
¿Cuándo elegir cada opción?
Tu aplicación depende de backends en tiempo de ejecución (traducciones servidas por un CMS al momento de la petición), del ecosistema de plugins o de un entorno no-React que los adaptadores no cubren.
Estás en react-i18next / next-i18next y quieres los 68 KB, componentes 8 veces más pequeños, 0% de fuga, claves tipadas y comprobaciones de CI sin reescribir código. Este es el punto de entrada para una base de código i18next existente.
Para nuevos proyectos, o una vez que el adaptador haya cumplido su función. Tiene el runtime más ligero (5.5 KB, +0.3 KB por página) y permite Server Components síncronos y archivos .content.ts por componente. Comienza con Intlayer con Next.js o con Vite y React.
Preguntas frecuentes
De resources: { en, fr, ... }. La configuración básica de next-i18next importa el JSON de cada idioma en init(), por lo que cada página carga cada namespace en cada idioma: 218.5 KB por página. El adaptador nunca empaqueta ese bloque; entrega a cada componente solo el diccionario que nombra, en el idioma activo.
Sí, con components, etiquetas numeradas <1>...</1> y values. También funcionan {{interpolation}}, anidación $t(key), plurales key_one / key_other (evaluados con Intl.PluralRules), sufijos de contexto y returnObjects.
Configura splitKeys: false en el plugin syncJSON. Todo el archivo se mantiene como un único diccionario y un useTranslation() básico continuará resolviendo sobre él.
No, es el puente. El adaptador conserva la API de i18next y cuesta 9.4 KB de runtime; next-intlayer nativo cuesta 5.5 KB y añade Server Components síncronos y archivos .content.ts colocados junto al código. Puedes migrar componente por componente, ya que los diccionarios JSON y .content.ts coexisten.
Sí. locales/{lng}/{ns}.json sigue siendo la fuente de verdad: syncJSON lo lee con el dialecto de i18next y escribe las traducciones de vuelta cuando la CLI o el CMS los actualiza.
Comparativas relacionadas
Misma serie de adaptadores:
Las bibliotecas comparadas directamente:
Documentación de referencia:
Compat adapters:
Migration guides:
Para entender de dónde vienen estas bibliotecas, lee la historia del i18n en JavaScript.
Conclusión
i18next es el runtime más pesado de este benchmark, y los adaptadores eliminan la mayor parte de su carga sin pedirte que abandones su API. En la misma aplicación Next.js, esto se traduce en 68 KB menos por página que la configuración inicial, 12.7 KB menos que la alternativa más optimizada a mano, componentes 8 veces más pequeños, 0% de fuga y 4 ms de hidratación, a cambio de un archivo de configuración, una línea de plugin y un cambio menor en el provider. Los backends y detectores pasan a ser inocuos, resources se descarta en lugar de combinarse, y el runtime nativo next-intlayer se mantiene aún 9 KB más ligero.
Todos los datos brutos, las aplicaciones de prueba y los scripts están disponibles en el repositorio de Benchmark Bloom. Puedes comprobarlo tú mismo.
Consulta el documento ¿Por qué Intlayer? para más información.
Comentarios
Aún no hay comentarios. Sé el primero en compartir tus pensamientos.
