Cómo enlazar versiones en idiomas alternativos en Next.js (Pages Router) v16

Enlaza alternativas de idioma para motores de búsqueda

Problema

Cuando un sitio web ofrece el mismo contenido en múltiples idiomas, los motores de búsqueda encuentran URLs separadas para cada versión sin entender su relación. Un usuario francés que realiza una búsqueda podría ver la versión en inglés clasificada más alta que la francesa, aunque ambas existan. De manera similar, una página en inglés y su traducción al francés podrían ser tratadas como duplicados competidores en lugar de alternativas coordinadas. Sin señales explícitas que conecten estas versiones de idioma, los motores de búsqueda no pueden servir con confianza la versión más apropiada a los usuarios según sus preferencias de idioma, lo que lleva a una fragmentación de la autoridad de clasificación y una mala experiencia de usuario.

Solución

Añadir elementos link con atributos hreflang a la sección head de cada página, listando todas las versiones de idioma, incluida la página misma. Cada variante de página debe incluir un conjunto idéntico de enlaces que referencien cada versión de idioma disponible. Este enlace bidireccional asegura que los motores de búsqueda reconozcan las páginas como traducciones en lugar de duplicados, permitiéndoles servir la versión correcta del idioma a los usuarios basándose en las preferencias del navegador y el contexto de búsqueda.

Pasos

1. Crear un componente para generar enlaces de idiomas alternativos

Importa el componente Head desde next/head para modificar los metadatos de la página. Accede a la información de localización a través del hook useRouter para construir enlaces para todas las versiones de idioma disponibles.

import Head from "next/head";
import { useRouter } from "next/router";

interface AlternateLinksProps {
  path?: string;
}

export default function AlternateLinks({ path }: AlternateLinksProps) {
  const router = useRouter();
  const { locales, locale: currentLocale, asPath } = router;
  const canonicalPath = path || asPath;

  if (!locales) {
    return null;
  }

  return (
    <Head>
      {locales.map((locale) => (
        <link
          key={locale}
          rel="alternate"
          hrefLang={locale}
          href={`${process.env.NEXT_PUBLIC_SITE_URL}/${locale}${canonicalPath}`}
        />
      ))}
    </Head>
  );
}

El componente recorre los idiomas disponibles desde el router y genera elementos link dinámicamente para todas las páginas.

2. Añadir el componente a las páginas que tienen traducciones

Incluye el componente AlternateLinks en cada componente de página que existe en múltiples idiomas.

import AlternateLinks from "@/components/AlternateLinks";

export default function AboutPage() {
  return (
    <>
      <AlternateLinks />
      <main>
        <h1>About Us</h1>
      </main>
    </>
  );
}

El componente asegura que cada página incluya el conjunto completo de enlaces alternativos, satisfaciendo el requisito de que todas las variantes se referencien entre sí.

3. Incluir un enlace de auto-referencia para el idioma actual

Cada página debe incluir una etiqueta hreflang de auto-referencia que indique su propio idioma. El componente ya maneja esto al iterar a través de todos los idiomas, incluido el actual.

export default function AlternateLinks({ path }: AlternateLinksProps) {
  const router = useRouter();
  const { locales, asPath } = router;
  const canonicalPath = path || asPath;

  if (!locales) {
    return null;
  }

  return (
    <Head>
      {locales.map((locale) => (
        <link
          key={locale}
          rel="alternate"
          hrefLang={locale}
          href={`${process.env.NEXT_PUBLIC_SITE_URL}/${locale}${canonicalPath}`}
        />
      ))}
    </Head>
  );
}

Cada página ahora incluye etiquetas hreflang que apuntan a sí misma junto con etiquetas para otras versiones.

4. Añadir un enlace de respaldo x-default

Añade un enlace x-default para especificar qué versión deben ver los usuarios cuando su idioma no está disponible.

export default function AlternateLinks({ path }: AlternateLinksProps) {
  const router = useRouter();
  const { locales, defaultLocale, asPath } = router;
  const canonicalPath = path || asPath;

  if (!locales || !defaultLocale) {
    return null;
  }

  return (
    <Head>
      {locales.map((locale) => (
        <link
          key={locale}
          rel="alternate"
          hrefLang={locale}
          href={`${process.env.NEXT_PUBLIC_SITE_URL}/${locale}${canonicalPath}`}
        />
      ))}
      <link
        rel="alternate"
        hrefLang="x-default"
        href={`${process.env.NEXT_PUBLIC_SITE_URL}/${defaultLocale}${canonicalPath}`}
      />
    </Head>
  );
}

El enlace x-default dirige a los usuarios cuyas preferencias de idioma no coinciden con el idioma predeterminado.