Comment optimiser le SEO d’une application Next.js multilingue
Pourquoi le SEO multilingue nécessite une attention particulière
La plupart des développeurs qui ajoutent de l'i18n à leur application Next.js se concentrent sur la traduction de l'UI et considèrent que c'est terminé. Mais si les moteurs de recherche ne peuvent pas trouver, indexer et associer correctement vos versions linguistiques, tout ce travail de traduction reste invisible.
Un site multilingue sans configuration SEO adaptée rencontre plusieurs problèmes :
- Google peut n'indexer qu'une seule version linguistique et ignorer les autres
- Les utilisateurs qui recherchent en espagnol se voient proposer la page en anglais
- Pénalités pour contenu dupliqué parce que Google voit
/en/aboutet/fr/aboutcomme la même page - Langue incorrecte affichée dans les extraits des résultats de recherche
La bonne nouvelle : bien configurer le SEO multilingue dans Next.js n'est pas compliqué. Il y a cinq points à maîtriser, et ce guide les couvre tous en utilisant gt-next.
1. Routage d’URL basé sur la locale
La base du SEO multilingue est d’avoir des URL distinctes pour chaque langue. Les moteurs de recherche ont besoin d’URL séparées et explorables pour indexer chaque version linguistique de manière indépendante.
Cela signifie locale dans l’URL — pas de cookies, pas de paramètres de requête, pas uniquement la détection Accept-Language.
✅ generaltranslation.com/en/about
✅ generaltranslation.com/fr/about
✅ generaltranslation.com/es/about
❌ generaltranslation.com/about?lang=fr
❌ generaltranslation.com/about (avec locale dans un cookie)
Configuration du routage par locale avec gt-next
Commencez par placer vos pages dans un segment dynamique [locale] :
app/
└── [locale]/
├── layout.tsx
├── page.tsx
└── about/
└── page.tsx
Créez ensuite un middleware à la racine de votre projet (proxy.ts pour Next.js 16+ ou middleware.ts pour Next.js 15 et versions antérieures) :
import { createNextMiddleware } from 'gt-next/middleware';
export default createNextMiddleware();
export const config = {
matcher: ['/((?!api|static|.*\\..*|_next).*)'],
};Cela vous donne automatiquement des URL préfixées par la locale.
Par défaut, la langue par défaut (par exemple, l’anglais) n’a pas de préfixe — /about reste inchangée,
tandis que les utilisateurs espagnols voient /es/about et les utilisateurs français voient /fr/about.
2. Définir l’attribut lang dans HTML
L’attribut lang sur votre balise <html> indique aux navigateurs et aux moteurs de recherche dans quelle langue la page est rédigée.
C’est l’une des actions les plus simples et les plus efficaces que vous puissiez mettre en place pour l’accessibilité et le SEO.
Sans cela, les lecteurs d’écran doivent deviner la langue (souvent à tort), et les moteurs de recherche ont moins confiance dans leur classification linguistique.
gt-next fournit le hook useLocale qui rend cela trivial dans votre layout racine :
import { useLocale, GTProvider } from 'gt-next';
export default function RootLayout({ children }: { children: React.ReactNode }) {
const locale = useLocale();
return (
<html lang={locale}>
<body>
<GTProvider>
{children}
</GTProvider>
</body>
</html>
);
}useLocale renvoie le code de langue au format BCP 47 (par exemple, en-US, ar, zh-Hans).
3. URLs canoniques
Les balises canoniques indiquent aux moteurs de recherche quelle URL est la version « principale » d’une page. Pour les sites multilingues, chaque version linguistique doit pointer vers elle‑même comme URL canonique :
<!-- Sur /fr/about -->
<link rel="canonical" href="https://example.com/fr/about" />Cela empêche les moteurs de recherche de considérer votre page en français comme un doublon de votre page en anglais.
Dans Next.js, vous définissez les URL canoniques via la Metadata API.
Combinez-la avec la fonction getLocale de gt-next pour générer l’URL canonique correcte pour chaque langue :
import { getLocale } from 'gt-next/server';
const BASE_URL = 'https://example.com';
export async function generateMetadata() {
const locale = await getLocale();
return {
alternates: {
canonical: `${BASE_URL}/${locale}/about`,
},
};
}
export default function AboutPage() {
return <h1>À propos de nous</h1>;
}Pour la locale par défaut, sans préfixe, procédez comme suit :
import { getLocale, getDefaultLocale } from 'gt-next/server';
export async function generateMetadata() {
const locale = await getLocale();
const defaultLocale = getDefaultLocale();
const path = '/about';
const prefix = locale === defaultLocale ? '' : `/${locale}`;
return {
alternates: {
canonical: `${BASE_URL}${prefix}${path}`,
},
};
}4. Balises hreflang
Les balises hreflang sont le signal le plus important pour le SEO multilingue. Elles indiquent aux moteurs de recherche : « cette page existe dans ces autres langues, et voici les URL. »
Sans hreflang, Google doit deviner quelle version linguistique afficher dans les résultats de recherche — et il se trompe souvent.
<link rel="alternate" hreflang="en" href="https://example.com/about" />
<link rel="alternate" hreflang="fr" href="https://example.com/fr/about" />
<link rel="alternate" hreflang="es" href="https://example.com/es/about" />
<link rel="alternate" hreflang="x-default" href="https://example.com/about" />La balise x-default indique aux moteurs de recherche quelle URL afficher lorsqu'aucune des langues spécifiées ne correspond à la langue de l'utilisateur.
Dans Next.js, vous pouvez ajouter hreflang via la propriété alternates.languages de l'API de métadonnées.
Voici un utilitaire réutilisable compatible avec gt-next :
import { getLocale, getDefaultLocale } from 'gt-next/server';
const BASE_URL = 'https://example.com';
const SUPPORTED_LOCALES = ['en', 'fr', 'es'];
export async function getI18NMetadata(path: string) {
const locale = await getLocale();
const defaultLocale = getDefaultLocale();
const getUrl = (loc: string) => {
const prefix = loc === defaultLocale ? '' : `/${loc}`;
return `${BASE_URL}${prefix}${path}`;
};
const languages: Record<string, string> = {};
for (const loc of SUPPORTED_LOCALES) {
languages[loc] = getUrl(loc);
}
languages['x-default'] = getUrl(defaultLocale);
return {
alternates: {
canonical: getUrl(locale),
languages,
},
};
}Utilisez-le ensuite dans n’importe quelle page :
import { getI18NMetadata } from '@/lib/i18n-metadata';
export async function generateMetadata() {
return await getI18NMetadata('/about');
}Génère la balise canonique ainsi que toutes les balises hreflang en un seul appel.
5. Métadonnées traduites
Les moteurs de recherche affichent le titre et la description de votre page dans les résultats. Si ceux-ci sont en anglais pour une page en français, les utilisateurs seront moins enclins à cliquer — et Google peut rétrograder le résultat.
Utilisez la fonction getGT de gt-next pour traduire les chaînes de métadonnées :
import { getGT } from 'gt-next/server';
import { getI18NMetadata } from '@/lib/i18n-metadata';
export async function generateMetadata() {
const t = await getGT();
const i18nMeta = await getI18NMetadata('/about');
return {
title: t('À propos'),
description: t('Découvrez notre mission et notre équipe.'),
...i18nMeta,
};
}Cela vous permet d’avoir des titres et des descriptions localisés dans les résultats de recherche, ce qui améliore considérablement les taux de clics pour les requêtes dans d’autres langues que l’anglais.
6. Sitemaps multilingues
Un sitemap permet aux moteurs de recherche de découvrir toutes vos pages, y compris chaque version dans chaque langue. Pour les sites multilingues, vous devez également inclure des annotations hreflang dans votre sitemap.
Next.js prend en charge la génération programmatique de sitemaps via un fichier sitemap.ts :
import { MetadataRoute } from 'next';
const BASE_URL = 'https://example.com';
const LOCALES = ['en', 'fr', 'es'];
const DEFAULT_LOCALE = 'en';
const PAGES = ['/', '/about', '/blog', '/contact'];
export default function sitemap(): MetadataRoute.Sitemap {
return PAGES.flatMap((path) => {
const getUrl = (locale: string) => {
const prefix = locale === DEFAULT_LOCALE ? '' : `/${locale}`;
return `${BASE_URL}${prefix}${path === '/' ? '' : path}`;
};
const languages: Record<string, string> = {};
for (const locale of LOCALES) {
languages[locale] = getUrl(locale);
}
return LOCALES.map((locale) => ({
url: getUrl(locale),
lastModified: new Date(),
alternates: { languages },
}));
});
}Cela génère un sitemap avec une entrée par page et par locale, et chaque entrée comprend des annotations hreflang pointant vers toutes les versions linguistiques.
Liste de contrôle
Voici un bref récapitulatif de tout ce que nous avons couvert :
| Exigence SEO | Implémentation |
|---|---|
| Locale dans l’URL | createNextMiddleware() avec segment dynamique [locale] |
Attribut HTML lang | useLocale() dans le layout racine |
| URL canoniques | getLocale() + API de métadonnées Next.js alternates.canonical |
| Balises hreflang | API de métadonnées Next.js alternates.languages avec toutes les locales prises en charge |
| Métadonnées traduites | getGT() pour les titres et descriptions de page |
| Sitemap multilingue | sitemap.ts avec des entrées par locale et des variantes hreflang |
Prochaines étapes
- Démarrage rapide avec gt-next pour configurer toute la stack i18n
- Guide middleware pour la configuration du routage
- Guide SSG pour générer des pages multilingues statiques
- Prise en charge RTL pour les langues s’écrivant de droite à gauche