Lier les versions linguistiques alternatives (hreflang)
Indiquer aux moteurs de recherche vos pages localisées
Problème
Une application dispose d'un contenu identique disponible à /en/page et /fr/page. Les moteurs de recherche considèrent ces pages comme deux pages distinctes et concurrentes. Sans mécanisme pour les lier, le classement dans les résultats de recherche est divisé, et les utilisateurs en France peuvent voir la page anglaise dans les résultats de recherche au lieu de la page française.
Solution
Utilisez la propriété alternates dans la fonction generateMetadata de Next.js. En fournissant une liste de toutes les langues disponibles pour une page donnée, Next.js générera automatiquement des balises <link rel="alternate" hreflang="..." /> dans le <head> du document, signalant la relation entre ces pages aux moteurs de recherche.
Étapes
1. Définir l'URL de base de votre site
Les balises hreflang nécessitent des URL absolues, et non relatives. Stockez l'URL de base canonique de votre site dans un fichier de configuration.
// i18n-config.ts
export const locales = ['en', 'es', 'fr'];
export const defaultLocale = 'en';
export const siteBaseUrl = 'https://www.example.com'; // Your production URL
2. Ajouter alternates à generateMetadata
Dans votre app/[lang]/layout.tsx (pour l'appliquer à toutes les pages) ou un fichier de page spécifique, exportez une fonction generateMetadata.
// 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>
);
}
Ce code génère un objet languages qui associe chaque locale à son URL de base absolue (par exemple, en: 'https://www.example.com/en'). Il définit également une URL canonical pour la page actuelle et une URL x-default, qui indique aux moteurs de recherche quelle version afficher pour les utilisateurs dans des langues non spécifiées.
3. Gérer alternates sur les pages imbriquées
Pour les pages imbriquées comme /about, vous devez vous assurer que la fonction de métadonnées inclut le chemin complet.
// 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>;
}