Ir al contenido

Internacionalización (i18n)

Usamos next-intl para manejar múltiples idiomas.

import createMiddleware from "next-intl/middleware";
export default createMiddleware({
locales: ["es", "en"],
defaultLocale: "es",
localeDetection: true,
});
export const config = {
matcher: ["/((?!api|_next|_vercel|.*\\..*).*)"],
};
import createNextIntlPlugin from "next-intl/plugin";
const withNextIntl = createNextIntlPlugin();
const nextConfig = {};
export default withNextIntl(nextConfig);

Carga dinámicamente los mensajes del locale activo.

Petición a /
middleware.ts
- Lee Accept-Language del navegador
- Detecta locale (o usa "es" por defecto)
- Redirige a /[locale]/...
app/[locale]/layout.tsx (server component)
- Lee locale de los params de la URL
- Carga mensajes con getMessages()
- Envuelve children en <NextIntlClientProvider>
- Setea <html lang={locale}>
Componentes cliente (Header, LoginPage, etc.)
- useTranslations("namespace")("key")
- useLocale() para construir links

Los mensajes están en apps/web/messages/:

  • en.json — Inglés
  • es.json — Español

Ambos archivos tienen la misma estructura de keys. Las keys principales:

nav hero features auth.login auth.register
footer locale home.features.* home.status.*
"use client";
import { useTranslations, useLocale } from "next-intl";
export default function MiComponente() {
const t = useTranslations("auth.login");
const locale = useLocale();
return <h1>{t("title")}</h1>;
}
import { getTranslations } from "next-intl/server";
export default async function ServerComponent() {
const t = await getTranslations("nav");
return <h1>{t("features")}</h1>;
}
const locale = useLocale();
const pathname = usePathname();
const otherLocale = locale === "es" ? "en" : "es";
const pathWithoutLocale = pathname.replace(/^\/(en|es)/, "") || "/";
const switchHref = `/${otherLocale}${pathWithoutLocale}`;
  1. Crear apps/web/messages/fr.json (copiar y traducir desde en.json)
  2. Añadir "fr" al array locales en middleware.ts
  3. Actualizar el locale switcher si hay más de 2 idiomas