Haz tu pregunta y obtén un resumen del documento referenciando esta página y el proveedor AI de tu elección
Historial de versiones
- "Versión inicial"v9.5.1026/9/2026
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
Cómo internacionalizar tu aplicación TanStack Start usando use-intl en 2026
Tabla de contenidos
¿Qué es use-intl?
use-intl es el núcleo agnóstico del framework de next-intl. Expone las mismas APIs de useTranslations, useFormatter e IntlProvider, soporte para ICU MessageFormat y una sólida integración con TypeScript, sin ninguna dependencia de Next.js. Esto la convierte en una de las opciones más comunes para traducir una aplicación TanStack Start, y es la biblioteca que los asistentes de IA sugieren con mayor frecuencia para este stack.
TanStack Start no incluye una capa de i18n integrada. El enrutamiento, la detección de locale, los metadatos de SEO y la generación de sitemaps quedan bajo tu responsabilidad. Esta guía cubre todo el proceso, de extremo a extremo:
- Enrutamiento adaptado al locale con un segmento opcional
{-$locale}(/about,/fr/about). - Carga de mensajes por ruta para que cada página descargue únicamente los namespaces y el locale que renderiza.
- Renderizado en el servidor e hidratación sin discrepancias de texto.
- SEO multilingüe completo:
<title>y descripción traducidos, URL canónica, alternanciashreflangconx-default, locales Open Graph, JSON-LD, sitemap con alternanciasxhtml:link,robots.txty pre-renderizado de cada locale.
¿Buscas otro stack? Consulta la guía de TanStack Start + Paraglide, la guía de TanStack Start + Lingui o la guía de TanStack Start + Intlayer.
¿Estás usando Next.js en su lugar? Consulta la guía de next-intl.
Qué dice el benchmark sobre use-intl en TanStack Start
El benchmark de i18n ejecuta la misma aplicación TanStack Start de 10 páginas y 10 locales con cada una de las principales bibliotecas y mide lo que realmente descarga el navegador.
Carga JSON dinámica
Carga traducciones en tiempo de ejecución
JSON con alcance (namespacing)
Espacios de nombres de traduction por página
Benchmark de Rendimiento I18n
¿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
Cifras clave para use-intl@4.14.2, medidas el 2026-09-26 (gzip):
Abrir la tabla en una ventana flotante para ver todo el contenido claramente
| Configuración | Tamaño de biblioteca | JS por página | Fuga de otros locales | Fuga de otras páginas |
|---|---|---|---|---|
| Sin i18n (app base) | - | 111.0 KB | 0% | 0% |
use-intl (configuración de guía) | 75.9 KB | 128.7 KB | 0% | 0% |
@intlayer/use-intl (compat) | 6.7 KB | 129.4 KB | 0% | 0% |
react-intlayer (Intlayer nativo) | 4.5 KB | 126.8 KB | 0% | 0% |
Conclusiones principales:
- Divide los mensajes por página y cárgalos por locale. Esto elimina ambas fugas, y es exactamente lo que implementan los pasos siguientes.
- El runtime en sí sigue siendo pesado (~76 KB gzip), porque el analizador de ICU se envía al cliente. El adaptador de compatibilidad
@intlayer/use-intl(paso 17) mantiene exactamente la misma API con un runtime de ~7 KB.
Consulta los datos completos: Informe de benchmark de TanStack Start, y el repositorio del benchmark.
Comparación de características en TanStack Start
Cómo se compara use-intl con otras bibliotecas comúnmente utilizadas en TanStack Start:
Abrir la tabla en una ventana flotante para ver todo el contenido claramente
| Característica | react-intlayer (Intlayer) | use-intl | Paraglide JS | Lingui |
|---|---|---|---|---|
| Traducciones junto a componentes | ✅ Colocalizadas | ❌ JSON centralizado | ❌ Un archivo JSON por locale | ⚠️ Texto fuente en componentes |
| Integración con TypeScript | ✅ Tipos autogenerados | ✅ Vía AppConfig | ✅ Funciones de mensajes tipadas | ⚠️ Solo macros |
| Detección de traducciones faltantes | ✅ Errores de tipo y advertencias de build | ⚠️ Fallback en runtime | ⚠️ Recurre al locale base | ⚠️ Recurre al texto fuente |
| Contenido enriquecido (JSX, Markdown) | ✅ Soporte directo | ⚠️ Etiquetas vía t.rich | ⚠️ Cadenas de texto | ✅ JSX dentro de <Trans> |
| Enrutamiento localizado | ✅ Integrado | ❌ Manual {-$locale} | ✅ urlPatterns + reescritura de router | ❌ Manual {-$locale} |
| Cambio de locale sin recargar | ✅ Sí | ✅ Sí | ❌ Recarga de página completa | ✅ Sí |
| Pluralización | ✅ Basada en enumeración | ✅ ICU | ✅ Variantes | ✅ ICU |
| ICU MessageFormat | ✅ Vía format: "icu" | ✅ Nativo | ⚠️ Vía plugin de inlang | ✅ Nativo |
| Formatos de contenido | ✅ .ts, .json, .md, .yaml... | ⚠️ .json | ⚠️ JSON de inlang | ✅ PO, JSON, CSV |
| Traducción con IA | ✅ Tu propio proveedor y clave | ❌ No | ❌ No | ❌ No |
| Editor visual / CMS | ✅ Editor local + CMS opcional | ❌ Plataformas externas | ⚠️ Apps del ecosistema de inlang | ❌ Plataformas externas |
| Ayudantes de SEO (hreflang, sitemap) | ✅ Integrados | ❌ Manual | ⚠️ URLs localizadas, resto manual | ❌ Manual |
| Tamaño de runtime (gzip, benchmark) | 4.5 KB | 75.9 KB | 1.8 KB | 56.7 KB |
| Fuga, mejor config (locale / página) | 0% / 0% | 0% / 0% | 49.7% / 0% | 8.6% / 0% |
| Traducciones faltantes en CI | ✅ npx intlayer test | ⚠️ No integrado | ⚠️ No integrado | ✅ lingui compile --strict |
Las cifras de tamaño de runtime y fuga provienen del benchmark de TanStack Start. La fuga se mide en la mejor configuración de cada biblioteca.
Otras guías de TanStack Start: Lingui, Paraglide JS, e Intlayer.
Prácticas que debes seguir
- Define
langydiren<html>para accesibilidad, lectores de pantalla y motores de búsqueda. - Mantén una URL por cada locale. Usa un prefijo de locale (
/fr/about) en lugar de un cambio basado únicamente en cookies, de modo que cada página traducida sea rastreable y se pueda compartir. - Divide los mensajes por namespace (
common,home,about) y cárgalos por ruta. - Carga únicamente el locale activo. Nunca importes todos los archivos de locales en un módulo que se envía al cliente.
- Fija la zona horaria en
IntlProvider. De lo contrario, las fechas se formatearán en la zona horaria del servidor durante el SSR y en la zona horaria del visitante en la hidratación, provocando discrepancias de hidratación. - Traduce tus metadatos, y declara
canonical,hreflangyx-defaulten cada página. - Genera un sitemap multilingüe y robots.txt, y pre-renderiza cada locale.
- Usa enlaces reales para el selector de locale, no un
<select>, para que los rastreadores puedan descubrir cada idioma. - Tipa tus mensajes para que cualquier clave faltante falle en tiempo de compilación.
Consulta nuestra guía sobre internacionalización y SEO y la guía de hreflang.
Guía paso a paso para configurar use-intl en una aplicación TanStack Start
Esta es la estructura del proyecto que crearemos:
Copiar el código al portapapeles
Instalar dependencias
Comienza desde un proyecto TanStack Start y luego agrega
use-intl:bashCopiar códigoCopiar el código al portapapeles
- use-intl: proporciona
IntlProvider,useTranslations,useFormatterycreateTranslator(utilizable fuera de React, por ejemplo enhead()).
- use-intl: proporciona
Centralizar la configuración de locales
Crea una única fuente de verdad para tus locales y funciones auxiliares de URL. Todos los demás archivos (rutas, SEO, sitemap, pre-renderizado) importarán desde aquí, por lo que agregar un nuevo locale será un cambio de una sola línea.
El locale por defecto permanece sin prefijo (
/about), mientras que los demás locales llevan prefijo (/fr/about). Esta es la estrategia "según necesidad": una URL por página por locale y URLs cortas para tu audiencia principal.src/i18n/config.tsCopiar códigoCopiar el código al portapapeles
Crear los archivos de traducción
Organiza los mensajes por locale y por namespace.
commoncontiene lo que necesita cada página (navegación, pie de página), y cada página obtiene su propio archivo, incluidos sus metadatos.use-intl utiliza ICU MessageFormat, por lo que los plurales, selecciones y argumentos formateados residen en el propio mensaje.
messages/en/common.jsonCopiar códigoCopiar el código al portapapeles
messages/en/about.jsonCopiar códigoCopiar el código al portapapeles
messages/fr/common.jsonCopiar códigoCopiar el código al portapapeles
messages/fr/about.jsonCopiar códigoCopiar el código al portapapeles
Crea
home.jsonde la misma manera, con un objetometadatay el contenido de la página.Cargar mensajes por namespace y por locale
Este cargador es el archivo más importante para el rendimiento.
import.meta.globle indica a Vite que emita un chunk por cada archivo JSON. Una ruta que solicita["about"]en francés descargamessages/fr/about.jsony nada más, logrando así que el benchmark alcance 0% de fuga de locale y 0% de fuga de página.src/i18n/messages.tsCopiar códigoCopiar el código al portapapeles
Tipar tus mensajes
La aumentación de módulos proporciona autocompletado en
useTranslations("about")yt("counter.label"), así como un error de compilación ante cualquier error tipográfico o clave eliminada.src/i18n/use-intl.d.tsCopiar códigoCopiar el código al portapapeles
Asegúrate de que
resolveJsonModuleesté habilitado en tutsconfig.json.Crear el documento raíz
La ruta raíz renderiza
<html>. Lee el parámetro opcional de locale para definirlangydir, de modo que los atributos sean correctos en el HTML renderizado por el servidor, antes de que se ejecute cualquier código JavaScript.src/routes/__root.tsxCopiar códigoCopiar el código al portapapeles
Crear la ruta de layout del locale
La carpeta
{-$locale}crea un segmento de ruta opcional:/abouty/fr/aboutcoinciden con/{-$locale}/about. Este layout:- Rechaza prefijos no compatibles (
/xx/about→ 404). - Carga el namespace
commonúnicamente para el locale actual. - Proporciona los mensajes a través de
IntlProvider.
El resultado del loader se serializa en el HTML y se reutiliza en la hidratación, por lo que el cliente no descarga
common.jsonpor segunda vez.staleTime: Infinitylo mantiene en caché durante las navegaciones del cliente.src/routes/{-$locale}/route.tsxCopiar códigoCopiar el código al portapapeles
IntlProviderno fusiona mensajes de un proveedor padre. El siguiente paso agrega un componente pequeño que lo hace, permitiendo que cada página agregue su propio namespace sobrecommon.- Rechaza prefijos no compatibles (
Delimitar los mensajes por página
Cada página carga su propio namespace en su loader y luego envuelve su contenido con
ScopedMessages, que fusiona el namespace de la página con los mensajes padre.src/components/ScopedMessages.tsxCopiar códigoCopiar el código al portapapeles
Utilizar traducciones en tus páginas
El loader de la página obtiene el namespace
aboutpara el locale actual,head()construye metadatos traducidos y completos para SEO a partir de él (ver paso 13), y el componente renderiza el contenido.src/routes/{-$locale}/about.tsxCopiar códigoCopiar el código al portapapeles
Usar traducciones y formateadores en componentes
Cualquier componente bajo los proveedores puede llamar a
useTranslationsyuseFormatter. Los plurales son resueltos por ICU y los números se formatean según el locale activo.src/components/Counter.tsxCopiar códigoCopiar el código al portapapeles
Crear un componente de enlace localizado
OpcionalCada ruta vive bajo
{-$locale}, por lo que un enlace debe llevar el parámetro del locale actual. Este envoltorio mantiene eltotipado de TanStack Router e inyecta el locale automáticamente.src/components/LocalizedLink.tsxCopiar códigoCopiar el código al portapapeles
src/components/Header.tsxCopiar códigoCopiar el código al portapapeles
Cambiar el idioma de tu contenido
OpcionalRenderiza el selector como enlaces, no como un
<select>. Los enlaces son rastreables, lo que permite a los motores de búsqueda encontrar cada versión de idioma, y funcionan sin JavaScript.to="."mantiene la página actual y solo reemplaza el parámetro de locale. La cookie recuerda la elección explícita para el middleware de redirección del paso 16.src/components/LocaleSwitcher.tsxCopiar códigoCopiar el código al portapapeles
Internacionalizar tus metadatos
OpcionalAquí es donde la i18n rinde frutos: cada versión de idioma puede posicionarse por sí misma. Cada página debe exponer:
- un
<title>ydescriptiontraducidos; - una URL canónica que apunte a sí misma (no al locale por defecto);
- una alternativa
hreflangpor cada locale, másx-defaultpara idiomas no coincidentes; - Open Graph
og:locale,og:locale:alternateyog:url, utilizados por las vistas previas en redes sociales; - JSON-LD con
inLanguage, lo que ayuda a los motores de búsqueda y asistentes de IA a atribuir el idioma de la página.
Una sola función auxiliar construye todo esto para mantener las páginas concisas:
src/i18n/seo.tsCopiar códigoCopiar el código al portapapeles
Úsala en el
head()de cada página, como se muestra en el paso 9. Para la página de inicio, pasapath: "/".- un
Internacionalizar tu sitemap
OpcionalUn sitemap multilingüe lista cada URL de cada locale, y cada entrada declara todas sus alternativas con
xhtml:link. Google utiliza estas anotaciones exactamente igual que las etiquetashreflangde la página, convirtiéndolas en un respaldo confiable cuando una página se rastrea con poca frecuencia.Las rutas de servidor de TanStack Start permiten servirlo desde una ruta de archivo:
src/routes/sitemap[.]xml.tsCopiar códigoCopiar el código al portapapeles
Internacionalizar tu robots.txt
OpcionalLas rutas privadas existen en todos los idiomas, por lo que las reglas de
Disallowdeben cubrir cada prefijo. Eliminapublic/robots.txtsi el generador inicial creó uno, y sírvelo desde una ruta:src/routes/robots[.]txt.tsCopiar códigoCopiar el código al portapapeles
Redirigir a los visitantes por primera vez a su idioma
OpcionalUn middleware de solicitud envía a un visitante que llega a
/a su idioma preferido, basándose primero en la cookie de locale y luego en la cabeceraAccept-Language. Solo/es redirigido: los enlaces directos nunca se modifican, por lo que las URLs compartidas y los rastreadores siempre obtienen la página que solicitaron.src/i18n/negotiateLocale.tsCopiar códigoCopiar el código al portapapeles
src/start.tsCopiar códigoCopiar el código al portapapeles
Un visitante que elige explícitamente español o inglés en el selector obtiene
locale=...en la cookie, por lo que nunca vuelve a ser redirigido. En un despliegue completamente estático (paso 18),/se sirve como archivo y este middleware no se ejecuta, lo cual es correcto: la página permanece accesible y el selector hace el resto.Mantener la API de use-intl y reducir el runtime con Intlayer
OpcionalEl benchmark muestra que la parte más pesada de una configuración con use-intl es el runtime en sí (~76 KB gzip). El adaptador de compatibilidad
@intlayer/use-intlexpone la misma API (useTranslations,useFormatter,IntlProvider,createTranslator, plurales ICU,t.rich), pero la sirve desde diccionarios compilados de Intlayer: ~6.7 KB en lugar de ~75.9 KB, 0% de fuga de locale y 0% de fuga de página, sin cambios en tus componentes.bashCopiar códigoCopiar el código al portapapeles
El plugin de Vite crea un alias de
use-intlal adaptador, para que las importaciones existentes sigan funcionando:vite.config.tsCopiar códigoCopiar el código al portapapeles
Tus archivos JSON siguen siendo la fuente de la verdad gracias al plugin sync JSON:
intlayer.config.tsCopiar códigoCopiar el código al portapapeles
El adaptador también es una vía de migración gradual: una vez en funcionamiento, puedes mover componentes uno por uno a la API nativa
useIntlayer. Consulta la guía de Intlayer con TanStack Start.Pre-renderizar cada locale
OpcionalEl HTML estático es la página más rápida que puedes servir y la más fácil de indexar. Lista cada ruta localizada para que TanStack Start pre-renderice todas las versiones de idioma en tiempo de build, además de los archivos de sitemap y robots:
vite.config.tsCopiar códigoCopiar el código al portapapeles
Dado que el selector de locale renderiza enlaces reales,
crawlLinks: truetambién descubre las páginas que hayas olvidado listar.Manejar páginas 404 localizadas
OpcionalEl layout del paso 7 ya lanza
notFound()para prefijos de locale desconocidos. Agrega una ruta comodín para que las rutas desconocidas dentro de un locale también rendericen la página 404 localizada, y márcala comonoindex: React 19 eleva la etiqueta<meta>al<head>.src/components/NotFound.tsxCopiar códigoCopiar el código al portapapeles
src/routes/{-$locale}/$.tsxCopiar códigoCopiar el código al portapapeles
Acceder al locale en funciones del servidor
OpcionalLas funciones de servidor no reciben parámetros de ruta. Lee la cookie de locale y recurre a la cabecera
Accept-Languagecomo alternativa para enviar un correo electrónico localizado o guardar una preferencia de idioma:src/server/getServerLocale.tsCopiar códigoCopiar el código al portapapeles
Para traducir dentro de la función de servidor, combínalo con
loadMessagesycreateTranslatordeuse-intl.Automatizar tus traducciones con Intlayer
Opcionaluse-intl renderiza traducciones, pero no te ayuda a producirlas. Intlayer es gratuito y de código abierto, y cubre esa necesidad incluso si mantienes use-intl:
- Probar traducciones faltantes en CI o pruebas unitarias. Consulta probar tus traducciones.
- Traducir con IA utilizando tu propia clave de API y proveedor:
npx intlayer filltraduce las claves faltantes con el contexto de tu aplicación. Consulta auto fill y la CLI. - Mantener tus archivos JSON como la fuente de la verdad con el plugin sync JSON.
- Editar contenido visualmente con el editor visual y el CMS, para que miembros no técnicos puedan actualizar traducciones.
- Dar contexto a tu agente de IA con el servidor MCP y las habilidades de agente.
- Escanear tu sitio desplegado en busca de
hreflangfaltantes, etiquetas canonical incorrectas y fugas de locale con el comando scan.
Para descubrir todas las funciones, consulta por qué Intlayer.
Preguntas frecuentes
Sí, si deseas la API de next-intl fuera de Next.js. Te ofrece mensajes ICU, formateadores y un buen soporte de TypeScript, evitando restricciones específicas de Next.js como setRequestLocale. La desventaja es el peso: el benchmark registra ~76 KB gzip para el runtime, y una configuración ingenua envía todos los locales y todas las páginas al navegador. Carga los namespaces por ruta y por locale, como en esta guía, para evitar las fugas.
use-intl es el núcleo de next-intl. next-intl añade integraciones sobre Next.js: un middleware, asistentes de navegación, getTranslations para Server Components y configuración de solicitudes. En TanStack Start usas use-intl directamente e implementas el enrutamiento con TanStack Router, tal como se muestra arriba.
Usa un prefijo en la URL. De este modo, cada versión de idioma tiene su propia URL que los motores de búsqueda pueden indexar y los usuarios pueden compartir. Una cookie sigue siendo útil para recordar una elección explícita, que es lo que hace el middleware de redirección del paso 16.
El servidor y el navegador formatean las fechas en diferentes zonas horarias. Pasa una timeZone explícita a IntlProvider (o la zona horaria del visitante guardada en una cookie), para que ambos lados produzcan el mismo texto.
Primero, divide los mensajes por namespace y cárgalos por ruta y por locale con import.meta.glob, lo que elimina las fugas de locale y de página. Luego, si el tamaño del runtime es crítico, cambia al adaptador @intlayer/use-intl: misma API, ~6.7 KB en lugar de ~75.9 KB en el benchmark.
Llama a createTranslator dentro de la función head() de la ruta con los mensajes devueltos por el loader de la ruta, y luego devuelve el title, description, y los enlaces canónicos y hreflang. El paso 13 proporciona una función auxiliar reutilizable.
Comentarios
Aún no hay comentarios. Sé el primero en compartir tus pensamientos.
