Internacionalizar Apps Next.js con next-intl

Internacionalizar Apps Next.js con next-intl

Enrutamiento por idioma, traducciones renderizadas en el servidor, diseños RTL y SEO hreflang sin parpadeo del lado del cliente.

20 de mayo de 202610 min de lecturaPor Shehzad Asadullah

La internacionalización es un requisito fundamental para aplicaciones que sirven audiencias globales. En la era del App Router de Next.js, bibliotecas como next-intl han emergido como el estándar de facto para gestionar traducciones, enrutamiento por locale y SEO en múltiples idiomas. Esta guía recorre patrones probados para construir aplicaciones React multilingües que rinden bien, posicionan en motores de búsqueda y respetan convenciones culturales incluyendo layouts de derecha a izquierda.

Diagrama que muestra la estructura de enrutamiento por locale con prefijos de idioma y rutas de contenido traducido
El enrutamiento basado en locale mapea cada idioma a un prefijo URL, permitiendo que crawlers y usuarios accedan directamente al contenido traducido.

Elegir una estrategia i18n para Next.js

El App Router cambió cómo Next.js maneja la internacionalización. La configuración integrada i18n del Pages Router ya no aplica, y los equipos deben elegir una biblioteca que se integre con el nuevo modelo de enrutamiento. next-intl es la opción más adoptada porque proporciona formateo de mensajes type-safe, navegación consciente del locale y detección de locale basada en middleware out of the box.

Al evaluar tu enfoque, considera estas decisiones arquitectónicas desde el inicio:

  • Estructura URL — Basada en prefijo (/en/about, /de/about) versus basada en dominio (en.example.com, de.example.com).
  • Manejo del locale predeterminado — Si el idioma predeterminado aparece en la URL o se infiere de los headers.
  • Almacenamiento de traducciones — Archivos JSON, integración CMS o plataforma de gestión de traducciones.
  • Comportamiento de fallback — Qué ocurre cuando falta una clave de traducción en un locale secundario.

El enrutamiento basado en prefijo es el predeterminado recomendado para la mayoría de aplicaciones. Mantiene el despliegue simple, funciona con hosting estático y da señales claras a los motores de búsqueda sobre variantes de idioma.

Configurar enrutamiento por locale

next-intl usa middleware de Next.js para detectar el locale preferido del usuario y reescribir solicitudes al segmento de locale apropiado. El middleware se ejecuta en el edge, añadiendo latencia negligible mientras asegura que cada página sirva contenido en el idioma correcto.

// middleware.ts
import createMiddleware from "next-intl/middleware";
import { routing } from "./i18n/routing";

export default createMiddleware(routing);

export const config = {
  matcher: ["/", "/(de|en|fr|ar)/:path*"],
};

// i18n/routing.ts
import { defineRouting } from "next-intl/routing";

export const routing = defineRouting({
  locales: ["en", "de", "fr", "ar"],
  defaultLocale: "en",
  localePrefix: "always",
});

La estructura de tu directorio app refleja la configuración de locale. Cada locale obtiene su propio segmento bajo app/[locale]/, y layouts compartidos envuelven páginas traducidas con navegación y footer consistentes.

Navegación consciente del locale

Nunca hardcodees rutas en enlaces. Usa los helpers de navegación proporcionados por next-intl para generar URLs con prefijo de locale automáticamente. Esto previene enlaces rotos cuando agregas o eliminas locales y asegura que el contexto del locale activo se propague en la navegación del lado del cliente.

import { Link } from "@/i18n/navigation";

export function NavBar() {
  return (
    <nav>
      <Link href="/about">About</Link>
      <Link href="/pricing">Pricing</Link>
    </nav>
  );
}

Organizar mensajes de traducción

Estructura tus archivos de mensajes para escalar con tu aplicación. Un archivo JSON plano funciona para proyectos pequeños, pero namespaces anidados previenen conflictos de merge y mejoran la discoverability cuando tu conteo de traducciones crece a miles.

  • Agrupa mensajes por funcionalidad o página — home.json, checkout.json, errors.json.
  • Usa nombres de clave descriptivos — checkout.payment.submitButton en lugar de btn1.
  • Mantén la sintaxis ICU message format consistente para pluralización e interpolación.
  • Establece un flujo de revisión con hablantes nativos antes de desplegar traducciones.

Carga solo los namespaces que una página necesita para evitar enviar traducciones no usadas. next-intl soporta lazy loading de módulos de mensajes, lo que mantiene los tamaños de bundle inicial manejables incluso con docenas de locales.

Soporte de layout de derecha a izquierda

Soportar idiomas RTL como árabe y hebreo requiere más que traducir cadenas. La dirección del layout, alineación de texto, espejado de iconos y dirección de animación deben adaptarse cuando el locale cambia a un idioma RTL.

Establece el atributo dir en tu elemento HTML raíz según el locale activo:

// app/[locale]/layout.tsx
import { getLocale, getMessages } from "next-intl/server";

const rtlLocales = ["ar", "he", "fa"];

export default async function LocaleLayout({ children, params }) {
  const locale = await getLocale();
  const direction = rtlLocales.includes(locale) ? "rtl" : "ltr";

  return (
    <html lang={locale} dir={direction}>
      <body>{children}</body>
    </html>
  );
}

Usa propiedades lógicas CSS en toda tu hoja de estilos. Reemplaza margin-left con margin-inline-start, padding-right con padding-inline-end, y text-align: left con text-align: start. Tailwind CSS v4 incluye utilities de propiedades lógicas (ms-4, pe-2) que manejan esto automáticamente.

SEO para sitios multilingües

Los motores de búsqueda necesitan señales explícitas para comprender las relaciones de idioma entre páginas. Sin etiquetas hreflang y URLs canónicas adecuadas, arriesgas penalizaciones por contenido duplicado e indexación incorrecta de locale.

Implementa estos esenciales de SEO:

  • Etiquetas link hreflang — Declara cada variante de idioma de cada página en el head del documento.
  • URLs canónicas — Cada página de locale debe canonicalizarse a sí misma, no al locale predeterminado.
  • Metadata traducida — Title, description y etiquetas Open Graph deben estar localizadas, no copiadas del locale predeterminado.
  • XML sitemaps — Genera sitemaps por locale o un sitemap unificado con anotaciones hreflang.
  • Structured data — Localiza markup JSON-LD schema para rich search results.

next-intl se integra con la metadata API de Next.js, permitiéndote definir títulos y descripciones específicos por locale en la función generateMetadata de cada página. Combina esto con un generador de sitemap que itere sobre tus locales y rutas para producir cobertura completa en motores de búsqueda.

Pruebas y aseguramiento de calidad

Los bugs de internacionalización son insidiosos porque a menudo aparecen solo en combinaciones específicas de locale. Una clave de traducción faltante puede renderizarse como cadena de clave cruda en producción. Un bug de layout RTL puede romper la navegación solo en árabe. Integra comprobaciones automatizadas en tu pipeline CI para detectar estos problemas temprano.

  • Valida que todos los locales contengan el mismo conjunto de claves de traducción.
  • Ejecuta pruebas de regresión visual contra locales RTL para detectar roturas de layout.
  • Prueba detección de locale con varias combinaciones de header Accept-Language.
  • Verifica que el formateo de fecha, número y moneda respete las convenciones del locale.
  • Comprueba el peso de página — asegúrate de que cargar todos los mensajes de locale no infle los bundles.

La internacionalización es un compromiso continuo, no una tarea de configuración única. A medida que tu aplicación crece, nuevas funcionalidades introducen nuevas cadenas, y mantener paridad de traducción entre locales requiere disciplina y herramientas. Al adoptar next-intl con enrutamiento estructurado por locale, soporte RTL y mejores prácticas SEO desde el inicio, construyes una base que escala con gracia a medida que tu audiencia se expande entre idiomas y regiones.

¿Te gustó la lectura?

¿Tienes un proyecto o una idea en mente? Me encantaría conocerla.

Contáctame