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
Lingui VS Intlayer: Benchmark de Internacionalización (i18n) en React & Next.js
Lingui e Intlayer son las dos librerías en este benchmark que se basan en un compilador en lugar de un runtime puro. Lingui extrae mensajes de macros en tiempo de compilación y compila catálogos por locale. Intlayer compila diccionarios por componente y los tree-shake por locale. En teoría deberían ser similares. Los números muestran dónde divergen.
Los datos provienen de Benchmark Bloom, un suite de código abierto que construye la misma aplicación con cada librería y registra lo que el navegador realmente descarga y ejecuta.
tl;dr: Lingui es el más cercano a Intlayer en JavaScript puro por página: 115-120 KB vs 118.6 KB en TanStack Start una vez configurada la carga perezosa, 148.6 KB vs 141.3 KB en Next.js. La brecha se abre en otros lugares: un componente Lingui compilado en aislamiento pesa 58-153 KB contra 6-8 KB para Intlayer, la hidratación toma 28-34 ms contra 11-14 ms, el fallback de locale de origen filtra 3-15% de cadenasenen páginasfren cada configuración optimizada, y llegar a esa configuración optimizada significa extraer, compilar y seleccionar manualmente catálogos por ruta. Intlayer lo logra sin configuración.
En resumen
- Lingui - Basado en macros (
t`...`,<Trans>,msg), ICU MessageFormat, catálogos.po/ JSON, flujo de trabajolingui extract+lingui compile. Compila IDs de mensajes en hashes cortos, soporta carga dinámica de catálogos por locale. Bien establecido, agnóstico de framework, fuerte historia de herramientas para traductores alrededor de.po. - Intlayer - Modelo de contenido centrado en componentes. Los diccionarios
.content.tsse ubican junto al componente al que sirven, un compilador en tiempo de construcción tree-shakes y lazy-loads por componente y por locale, tipos TypeScript estrictos se generan a partir de tu contenido, y las traducciones faltantes fallan en tiempo de construcción. Incluye middleware, ayudantes SEO, un Editor Visual / CMS y traducción asistida por IA.
Abrir la tabla en una ventana flotante para ver todo el contenido claramente
Las insignias se actualizan automáticamente. Las instantáneas variarán con el tiempo.
Comparación de características lado a lado
Abrir la tabla en una ventana flotante para ver todo el contenido claramente
| Característica | Intlayer (react-intlayer / next-intlayer) | Lingui (@lingui/core / @lingui/react) |
|---|---|---|
| Traducciones cerca de componentes | ✅ Sí, .content.ts colocado junto a cada componente | ⚠️ Cadenas de origen inline en JSX mediante macros; traducciones en catálogos .po centralizados |
| Integración con TypeScript | ✅ Tipos estrictos autogenerados a partir del contenido | ⚠️ Las macros están tipadas; los IDs de mensajes no, las entradas faltantes del catálogo no se detectan |
| Detección de traducciones faltantes | ✅ Error de TypeScript + error/advertencia en tiempo de compilación | ⚠️ lingui extract reporta estadísticas; el runtime recurre a la cadena de origen |
| Contenido enriquecido (JSX / Markdown / componentes) | ✅ Soporte directo | ✅ <Trans> con componentes anidados |
| Soporte ICU | ⚠️ En progreso | ✅ Sí (macros plural, select, selectOrdinal) |
| Formateo (fechas, números, monedas) | ✅ useNumber, useDate, ... (Intl bajo el capó) | ✅ i18n.date(), i18n.number() |
| Enrutamiento localizado y middleware | ✅ Proxy/middleware integrado, getMultilingualUrls | ❌ No forma parte del núcleo |
| Ayudantes SEO (hreflang, sitemap, robots) | ✅ Ayudantes integrados | ❌ Manual |
| Componentes de servidor sincrónicos | ✅ useIntlayer de next-intlayer/server funciona en cualquier componente de servidor hijo | ⚠️ Requiere una instancia I18n por solicitud, pasada hacia abajo o establecida mediante setI18n |
| Tree-shaking (enviar solo el contenido usado) | ✅ Por componente, por locale, automatizado por el compilador | ⚠️ Por locale mediante lingui compile; por ruta requiere división manual de catálogos |
| Carga perezosa (Lazy loading) | ✅ importMode: 'dynamic' (una línea de configuración) | ⚠️ import() manual de catálogos compilados + i18n.load() / i18n.activate() |
| Purgar contenido no utilizado | ✅ Los diccionarios muertos se descartan en tiempo de compilación | ✅ lingui extract --clean elimina mensajes obsoletos |
| Pruebas de traducciones faltantes (CLI / CI) | ✅ npx intlayer content test | ⚠️ Estadísticas de lingui extract (sin código de salida con error por defecto) |
| Canal de compilación (Pipeline) | ✅ Un plugin (@intlayer/swc / @intlayer/babel / vite-intlayer) | ⚠️ Plugin de macros (Babel o SWC) + pasos extract + compile |
| Traducción impulsada por IA | ✅ Integrada, utiliza tus propias claves de proveedor | ❌ No |
| Editor Visual / CMS | ✅ Editor Visual gratuito + CMS opcional | ❌ No (.po funciona con TMS externos) |
| Servidor MCP y Agent Skills | ✅ Sí | ❌ No |
| Ecosistema / comunidad | ⚠️ Más pequeño pero creciendo rápidamente | ✅ Establecido, agnóstico de framework |
El benchmark
Qué se midió
La suite Benchmark Bloom construye la misma aplicación con cada librería: 10 páginas (home, about, blog, careers, contact, FAQ, pricing, products, settings, team), 10 locales (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 librería 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 hace esto |
|---|---|---|
| static | Catálogo compilado de cada locale importado y cargado por adelantado | Prototipos rápidos, código generado por IA |
| dynamic | Solo se importa mediante import() el catálogo del locale activo, pero contiene todas las páginas | La mayoría de los proyectos |
| scoped-static | Un catálogo por ruta, todos empaquetados por adelantado | Raro |
| scoped-dynamic | Un catálogo por ruta + import() perezoso. Solo la página actual, locale actual | Aplicaciones con un presupuesto de rendimiento estricto |
Intlayer no tiene variante "scoped": el compilador delimita el contenido por componente automáticamente, por lo que sus filas static y dynamic ya están delimitadas.
Para cada build, la suite registra:
- Tamaño de librería (Lib size): tamaño gzip de un componente vacío que solo importa la librería i18n. El costo fijo del runtime.
- JS por página (Page JS): JavaScript gzip descargado por página, promediado sobre todas las páginas y locales.
- % de fuga de locale (Locale leak %): porcentaje de cadenas traducidas encontradas en el JS descargado que pertenecen a un locale que el usuario no está viendo (huella digital en
enyfr, por lo que 50% significa "el otro locale medido está completamente presente"; con 10 locales empaquetados, el desperdicio real es mayor). - % de fuga de página (Page leak %): porcentaje de cadenas traducidas encontradas en el JS descargado que pertenecen a una página en la que el usuario no está.
- Promedio por componente (Component avg): tamaño gzip promedio de cada componente compilado en aislamiento. Muestra cuánto runtime i18n y catálogo arrastra un solo componente.
- Reactividad E2E: tiempo transcurrido entre seleccionar un nuevo locale 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 números a continuación provienen de la ejecución del 2026-09-12 con@lingui/react6.6.0 eintlayer9.5.1. La aplicación de prueba es deliberadamente pequeña (unas pocas docenas de cadenas por locale), por lo que los porcentajes de fuga describen un patrón: crecen con tu contenido mientras que el costo del runtime se mantiene 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
| Librería | Estrategia | Tamaño Lib (gz) | JS por pág avg (gz) | Fuga locale | Fuga pág | Prom component (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 |
| Lingui | static | 11.9 KB | 207.4 KB | 50.0% | 90.0% | 73.3 KB | 15.3 ms | 15.2 ms |
| Lingui | dynamic | 11.9 KB | 145.4 KB | 2.8% | 89.9% | 19.9 KB | 15.7 ms | 12.7 ms |
| Lingui | scoped-static | 11.9 KB | 148.2 KB | 2.7% | 89.1% | 20.4 KB | 15.1 ms | 13.1 ms |
| Lingui | scoped-dynamic | 11.9 KB | 148.6 KB | 14.8% | 0.0% | 152.6 KB | 16.1 ms | 14.8 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 |
Cómo interpretarlo
- Costo de runtime. Un componente vacío cuesta 11.9 KB gzip con Lingui, 5.5 KB con Intlayer. En la página completa, la mejor configuración de Lingui se sitúa en +7.3 KB sobre Intlayer (148.6 vs 141.3 KB); Intlayer se sitúa en +0.3 KB sobre la aplicación base.
- La configuración ingenua es costosa. Cargar cada catálogo compilado por adelantado da 207.4 KB por página, +66 KB sobre la aplicación base. La mitad de las cadenas identificadas pertenecen al locale incorrecto, el 90% a la página incorrecta.
- La carga dinámica corrige el locale, no la página. Con un catálogo por locale, la fuga de página se mantiene en ~90%: todo el catálogo
frse envía en cada página francesa. Alcanzar un 0% de fuga de página requiere la configuraciónscoped-dynamic: un catálogo por ruta, extraído y compilado por separado, seleccionado manualmente en cada página. - El fallback del locale de origen tiene fugas. Incluso en las configuraciones optimizadas, 3-15% de las cadenas
ense envían dentro de las páginasfr. Las macros de Lingui mantienen el mensaje de origen disponible como fallback, por lo que llega al bundle junto a la traducción. Intlayer resuelve los fallbacks en tiempo de compilación y envía solo el locale activo. - El tamaño del componente se dispara en
scoped-dynamic. Cada componente compilado en aislamiento promedia 152.6 KB, porque el catálogo de cada ruta es accesible desde el componente que lo importa. El mismo componente conuseIntlayer()promedia 6.9 KB.
Tabla completa, cada biblioteca y cada estrategia, en el informe de benchmark de Next.js.
Resultados en TanStack Start
Abrir la tabla en una ventana flotante para ver todo el contenido claramente
| Librería | Estrategia | Tamaño Lib (gz) | JS por pág avg (gz) | Fuga locale | Fuga pág | Prom component (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 |
| Lingui | static | 11.2 KB | 152.2 KB | 50.0% | 90.0% | 58.0 KB | 3.9 ms | 19.9 ms |
| Lingui | dynamic | 11.2 KB | 115.2 KB | 9.3% | 0.0% | 85.5 KB | 5.9 ms | 28.0 ms |
| Lingui | scoped-static | 11.2 KB | 120.8 KB | 4.0% | 0.0% | 147.9 KB | 7.1 ms | 33.9 ms |
| Lingui | scoped-dynamic | 11.2 KB | 120.2 KB | 8.6% | 0.0% | 83.7 KB | 42.1 ms | 32.9 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 |
@intlayer/lingui (compat) | dynamic | 10.3 KB | 137.0 KB | 9.9% | 0.0% | 12.8 KB | 2.9 ms | 19.7 ms |
Cómo interpretarlo
- En JavaScript por página, Lingui gana por poco.
dynamicLingui alcanza 115.2 KB, 3.4 KB por debajo de los 118.6 KB de Intlayer. Los catálogos compilados de Lingui con IDs en hash son compactos, y el enrutador de TanStack Start divide las rutas lo suficientemente bien como para que la fuga de página ya sea del 0% en la filadynamic. - Todo lo demás alrededor del tamaño de página va en la otra dirección. La hidratación tarda 28-34 ms con Lingui frente a 11-14 ms con Intlayer:
i18n.load()+i18n.activate()se ejecutan en el cliente antes de que React pueda hidratar. Los componentes compilados en aislamiento pesan 58-148 KB frente a 6-8 KB. La fuga de locale nunca alcanza el 0% (4-9%) debido al fallback del locale de origen. - El cambio de locale en la configuración optimizada es lento.
scoped-dynamicLingui tarda 42 ms en actualizarhtml[lang]: el catálogo de la nueva ruta debe obtenerse, cargarse y activarse antes de que el cambio sea visible. Intlayer cambia en 3-4 ms en ambos modos. - La fila
staticde Intlayer ya tiene 0% de fuga de página porque solo se empaquetan los diccionarios importados por los componentes de la página. Una línea de configuración (importMode: 'dynamic') elimina también la fuga de locale. @intlayer/linguiconserva la sintaxis de macros de Lingui y la sirve desde diccionarios de Intlayer. Intercambia algo de tamaño de página (137 KB, dado que el runtime de macros permanece) por componentes más pequeños (12.8 KB) y una hidratación más rápida que el Lingui nativo. Es un paso de migración, no el destino final.
Tabla completa en el informe de benchmark de TanStack Start.
¿Por qué la diferencia? Dos compiladores, dos unidades de trabajo

Ambas librerías compilan. La diferencia radica en qué compilan.
Lingui compila catálogos. Las macros en tu código fuente se extraen en un archivo .po por locale, y luego se compilan en un módulo JS por locale. La unidad es el locale. Dividir aún más, por ruta o por componente, implica crear múltiples catálogos, configurar lingui.config.ts para extraer cada uno de un conjunto diferente de archivos y cargar el catálogo adecuado en cada ruta. La instancia runtime I18n es global; cada llamada a useLingui() suscribe el componente a ella.
Copiar el código al portapapeles
Intlayer compila diccionarios. Cada archivo .content.ts es un diccionario vinculado a una clave; el compilador resuelve qué componente importa qué clave y emite, por diccionario y por locale, exactamente el JSON que ese componente necesita. La unidad es el componente. El alcance por ruta es una consecuencia: una página solo incluye los diccionarios de los componentes que renderiza.
Copiar el código al portapapeles
Por eso el patrón scoped-dynamic es una salida de compilación automática para Intlayer y un proyecto de configuración manual para Lingui. La brecha se amplía en dos ejes a la vez, páginas e idiomas:

Para obtener los números de la filadynamic, definedictionary.importMode: 'dynamic'enintlayer.config.ts. Consulta la documentación de optimización de bundle.
Experiencia de desarrollador
Configuración
Copiar el código al portapapeles
Copiar el código al portapapeles
Luego agrega @lingui/babel-plugin-lingui-macro (o @lingui/swc-plugin) al empaquetador, ejecuta lingui extract después de editar el código fuente, lingui compile antes de compilar y envuelve el árbol en <I18nProvider i18n={i18n}>.
Copiar el código al portapapeles
Agrega intlayer() a vite.config.ts (o withIntlayer() a next.config.ts) y envuelve el árbol en <IntlayerProvider>. No se requiere ningún paso de extracción o compilación: los diccionarios se construyen cuando se ejecuta el empaquetador.
Componente
Copiar el código al portapapeles
El texto en inglés vive en el componente; el francés vive en src/locales/fr/messages.po bajo un ID en hash, después de ejecutar lingui extract. Olvidar ejecutarlo o compilar recurre silenciosamente al inglés.
Copiar el código al portapapeles
Copiar el código al portapapeles
Ambos locales residen en un archivo junto al componente. Un valor faltante de fr es un error de compilación, una clave incorrecta es un error de TypeScript.
Fuera de los componentes
Metadatos, loaders, funciones de servidor: en cualquier lugar sin un árbol de React.
Copiar el código al portapapeles
Una nueva instancia de I18n por llamada, el catálogo correcto cargado manualmente y msg + i18n._() en lugar de t. Como señalan las notas del benchmark, saber cuándo usar t, t` ` , i18n.t(), msg o <Trans> "no es intuitivo".
Copiar el código al portapapeles
Conserva las macros de Lingui, obtén los diccionarios de Intlayer
@intlayer/lingui es un adaptador directo para @lingui/core y @lingui/react. Las macros continúan compilándose como antes; el runtime i18n._() al que se compilan es servido desde diccionarios de Intlayer, con plugins de sincronización .po manteniendo tus catálogos existentes como fuente de verdad. Los plurales y selecciones ICU se renderizan de forma idéntica.
Copiar el código al portapapeles
Mantén @lingui/babel-plugin-lingui-macro / @lingui/swc-plugin en la compilación, ejecutándose antes del compilador de Intlayer. Consulta la documentación de compatibilidad con Lingui.
¿Cuándo elegir cuál?
Deseas ICU MessageFormat con macros tipadas, tus traductores trabajan en .po con un flujo TMS existente, prefieres cadenas fuente en línea en JSX y tu equipo gestiona cómodamente el flujo de extracción / compilación / división de catálogos. Su JS por página es competitivo una vez configurado el lazy loading.
Deseas contenido con ámbito por componente, TypeScript estricto, errores de claves faltantes en tiempo de compilación, tree-shaking y lazy loading sin esfuerzo, componentes pequeños, hidratación rápida, cambio instantáneo de idioma y herramientas editoriales integradas (Editor Visual, CMS, traducción por IA, servidor MCP). Especialmente relevante para bases de código modulares grandes y sistemas de diseño.
Ya usas Lingui y deseas migrar a los diccionarios de Intlayer gradualmente sin tocar las macros. Tus catálogos .po siguen siendo la fuente de la verdad gracias al plugin de sincronización PO. Medido lado a lado en Lingui vs @intlayer/lingui.
Preguntas frecuentes
Porque la unidad de compilación difiere. Lingui compila un catálogo por idioma: todo lo que está por debajo (catálogos por ruta, lazy loading, excluir el fallback del bundle) requiere configuración. Intlayer compila un diccionario por componente, por lo que la división por rutas surge naturalmente de la compilación. Por eso un componente de Lingui compilado aisladamente pesa 58-153 KB frente a 6-8 KB en Intlayer.
Las macros mantienen el mensaje fuente disponible como fallback en tiempo de ejecución, por lo que la cadena en inglés se envía junto a su traducción. El benchmark mide 3-15% de cadenas en dentro de páginas fr en cada configuración optimizada. Intlayer resuelve los fallbacks en tiempo de compilación y solo envía el idioma activo.
Sí, y en TanStack Start gana por muy poco: 115.2 KB en dynamic frente a 118.6 KB de Intlayer. Los catálogos compilados con identificadores hasheados son compactos. El coste aparece en otros aspectos: hidratación de 28-34 ms frente a 11-14 ms, y un cambio de idioma de 42 ms en la configuración scoped-dynamic.
No. @intlayer/lingui mantiene la compilación de t`...` , <Trans>, msg, plural, select y selectOrdinal exactamente como antes; solo cambia aquello contra lo que i18n._() resuelve. Mantén @lingui/babel-plugin-lingui-macro o @lingui/swc-plugin en la compilación. Consulta la documentación de compatibilidad con Lingui.
Se mantienen para las macros y desaparecen para el contenido propio de Intlayer. Los diccionarios .content.ts se compilan cuando se ejecuta el empaquetador, sin ningún paso CLI independiente, y intlayer test falla en CI si falta una clave en lugar de recurrir silenciosamente a la cadena de origen.
Comparaciones relacionadas

Mismo benchmark, otras bibliotecas:
Para profundizar:
Documentación de referencia:
Para entender de dónde vienen estas bibliotecas, lee la historia del i18n en JavaScript.
GitHub STARS
Las estrellas de GitHub son un indicador sólido de la popularidad de un proyecto, la confianza de la comunidad y su relevancia a largo plazo. Aunque no son una medida directa de la calidad técnica, reflejan cuántos desarrolladores encuentran útil el proyecto, siguen su progreso y es probable que lo adopten.
Actividad de commits
Las estrellas reflejan la popularidad. Los commits reflejan el trabajo invertido en un proyecto. En el momento de escribir esto, Intlayer suma unos 7.500 commits, más que la mayoría de las bibliotecas comparadas aquí, y unas 5 veces más que next-intl o next-i18next.
- lingui/js-lingui
- aymericzip/intlayer
Commits en la rama por defecto, fuente: API de GitHub.
Intlayer es un monorepo, así que el total incluye cada paquete de framework, la CLI y la documentación. Lee los commits como una señal de actividad, no de calidad.
Descargas en npm
- @lingui/core
- intlayer
Fuente: API de descargas del registro npm.
Las descargas premian a las soluciones más antiguas, no a las mejores. Una biblioteca publicada hace años se sigue instalando en cada proyecto que la eligió entonces, en cada ejecución de CI y en cada paquete que depende de ella. La cifra mide la inercia más que una elección actual.
Los asistentes de IA amplifican el efecto. next-intl, i18next y vue-i18n están por todas partes en el código con el que se entrenaron, así que los sugieren por defecto, sin comparar las alternativas. Cada sugerencia suma descargas, que alimentan la siguiente sugerencia. Compara con el benchmark y no con el número de descargas.
Conclusión
Lingui es la librería híbrida de runtime y compilador más sólida de este benchmark. Sus catálogos compilados y organizados mediante hashes le permiten mantener un peso de JavaScript por página a muy pocos kilobytes de distancia de Intlayer, e incluso situarse ligeramente por debajo en TanStack Start. Si el tamaño en bytes por página fuera la única métrica considerada, esto sería un empate técnico.
Pero no lo es. El compilador de Lingui se detiene en el límite del locale; todo lo que queda por debajo (catálogos por ruta, carga diferida, mantener las cadenas de fallback fuera del bundle) depende de configuración manual, y el benchmark evidencia el costo de esa frontera: componentes entre 10 y 20 veces más pesados, hidratación 2 a 3 veces más lenta, entre un 3% y un 15% de fuga de cadenas que nunca desaparece y un cambio de locale que tarda 42 ms en la variante optimizada. El compilador de Intlayer trabaja a nivel de componente individual, por lo que esas mismas métricas resultan en 6-8 KB, 11-14 ms, 0% y 3-4 ms sin requerir configuración manual.
Todos los datos brutos, las aplicaciones de prueba y los scripts correspondientes están disponibles en el repositorio Benchmark Bloom. Ejecútalo tú mismo.
Consulta el documento '¿Por qué Intlayer?' para más detalles.
Comentarios
Aún no hay comentarios. Sé el primero en compartir tus pensamientos.
