Verknüpfung alternativer Sprachversionen (hreflang)

Suchmaschinen über Ihre lokalisierten Seiten informieren

Problem

Eine Anwendung verfügt über identische Inhalte unter /en/page und /fr/page. Suchmaschinen betrachten diese als zwei separate, konkurrierende Seiten. Ohne einen Mechanismus zur Verknüpfung werden die Suchrankings aufgeteilt, und Nutzern in Frankreich wird möglicherweise die englische Seite in den Suchergebnissen angezeigt anstelle der französischen.

Lösung

Verwenden Sie die alternates-Eigenschaft innerhalb der Next.js-generateMetadata-Funktion. Durch Bereitstellung einer Liste aller verfügbaren Sprachen für eine bestimmte Seite generiert Next.js automatisch <link rel="alternate" hreflang="..." />-Tags im <head> des Dokuments und signalisiert Suchmaschinen die Beziehung zwischen diesen Seiten.

Schritte

1. Definieren Sie die Basis-URL Ihrer Website

hreflang-Tags erfordern absolute, nicht relative URLs. Speichern Sie die kanonische Basis-URL Ihrer Website in einer Konfigurationsdatei.

// i18n-config.ts

export const locales = ['en', 'es', 'fr'];
export const defaultLocale = 'en';
export const siteBaseUrl = 'https://www.example.com'; // Your production URL

2. Fügen Sie alternates zu generateMetadata hinzu

Exportieren Sie in Ihrer app/[lang]/layout.tsx (um auf alle Seiten anzuwenden) oder einer spezifischen Seitendatei eine generateMetadata-Funktion.

// app/[lang]/layout.tsx
import { locales, siteBaseUrl } from '@/i18n-config';
import type { Metadata } from 'next';

type Props = {
  params: { lang: string };
  children: React.ReactNode;
};

export async function generateMetadata({ params }: Props): Promise<Metadata> {
  const { lang } = params;

  // Create language alternates
  const alternatesMap = locales.reduce((acc, locale) => {
    acc[locale] = `${siteBaseUrl}/${locale}`;
    return acc;
  }, {} as Record<string, string>);

  return {
    alternates: {
      canonical: `${siteBaseUrl}/${lang}`,
      languages: {
        ...alternatesMap,
        'x-default': `${siteBaseUrl}/${defaultLocale}`,
      },
    },
  };
}

// Rest of your layout component
export default function RootLayout({ children, params }: Props) {
  return (
    <html lang={params.lang}>
      <body>{children}</body>
    </html>
  );
}

Dieser Code generiert ein languages-Objekt, das jede Locale ihrer absoluten Basis-URL zuordnet (z. B. en: 'https://www.example.com/en'). Es legt außerdem eine canonical-URL für die aktuelle Seite und eine x-default-URL fest, die Suchmaschinen mitteilt, welche Version Nutzern in nicht spezifizierten Sprachen angezeigt werden soll.

3. Behandeln Sie alternates auf verschachtelten Seiten

Für verschachtelte Seiten wie /about müssen Sie sicherstellen, dass die Metadaten-Funktion den vollständigen Pfad enthält.

// app/[lang]/about/page.tsx
import { locales, siteBaseUrl, defaultLocale } from '@/i18n-config';
import type { Metadata } from 'next';

type Props = {
  params: { lang: string };
};

export async function generateMetadata({ params }: Props): Promise<Metadata> {
  const { lang } = params;
  const path = '/about'; // The path for this page

  // Create language alternates
  const alternatesMap = locales.reduce((acc, locale) => {
    acc[locale] = `${siteBaseUrl}/${locale}${path}`;
    return acc;
  }, {} as Record<string, string>);

  return {
    title: 'About Us', // Add your translated title
    alternates: {
      canonical: `${siteBaseUrl}/${lang}${path}`,
      languages: {
        ...alternatesMap,
        'x-default': `${siteBaseUrl}/${defaultLocale}${path}`,
      },
    },
  };
}

export default function AboutPage() {
  return <div>About page content</div>;
}