Intégrer next-intl pour l’internationalisation dans Next.js 15 : Guide Complet
Tutoriel étape par étape pour intégrer next-intl pour la traduction dans un projet Next.js 15.

La traduction (i18n) est essentielle pour les applications web modernes. Avec Next.js 15, la méthode recommandée pour ajouter la traduction est d'utiliser next-intl
. Dans ce guide, vous apprendrez à configurer next-intl
dans un projet Next.js 15 de manière pratique et efficace.
1. Installer next-intl
Commencez par installer le package :
npm install next-intl
2. Configurer le routage
Créez une configuration de routage pour définir vos langues supportées. Par exemple, dans src/i18n/routing.ts
:
1// src/i18n/routing.ts
2import {defineRouting} from 'next-intl/routing';
3
4export const routing = defineRouting({
5 locales: ['en', 'fr', 'ar', 'es'],
6 defaultLocale: 'en'
7});
3. Ajouter les helpers de navigation
Configurez les helpers de navigation dans src/i18n/navigation.ts
:
// src/i18n/navigation.ts
import { createNavigation } from 'next-intl/navigation';
import { routing } from './routing';
export const { Link, redirect, usePathname, useRouter, getPathname } = createNavigation(routing);
4. Charger les messages côté serveur
Configurez le chargement des messages dans src/i18n/request.ts
:
1// src/i18n/request.ts
2import { getRequestConfig } from 'next-intl/server';
3import { hasLocale } from 'next-intl';
4import { routing } from './routing';
5
6export default getRequestConfig(async ({ requestLocale }) => {
7 const requested = await requestLocale;
8 const locale = hasLocale(routing.locales, requested)
9 ? requested
10 : routing.defaultLocale;
11
12 const [seoMessages] = await Promise.all([
13 import(`../messages/seo/${locale}.json`).then(module => module.default).catch(() => ({})),
14 ]);
15
16 return {
17 locale,
18 messages: {
19 seo: seoMessages,
20 }
21 };
22});
5. Fournir les messages à votre application
Encapsulez vos pages avec NextIntlClientProvider
. Par exemple, dans src/app/[locale]/about/layout.tsx
:
1// src/app/[locale]/about/layout.tsx
2import { NextIntlClientProvider } from "next-intl";
3
4export default async function AboutLayout({
5 children,
6 params,
7}: {
8 children: React.ReactNode;
9 params: Promise<{ locale: string }>;
10}) {
11 const { locale } = await params;
12
13 const [about] = await Promise.all([
14 import(`@/messages/about/${locale}.json`).then((mod) => mod.default),
15 ]);
16
17 const messages = { about };
18 return (
19 <NextIntlClientProvider locale={locale} messages={messages}>
20 {children}
21 </NextIntlClientProvider>
22 );
23}
6. Organiser vos fichiers de messages
Stockez vos fichiers de traduction dans src/messages/
, par exemple :
src/messages/about/en.json
src/messages/about/fr.json
src/messages/about/ar.json
src/messages/about/es.json
Chaque fichier contient les traductions pour un namespace, par exemple :
{
"header_story": "Notre histoire",
"header_title1": "Nous construisons"
// ...
}
7. Utiliser les traductions dans les composants
Utilisez le hook useTranslations
de next-intl
:
1import { useTranslations } from "next-intl";
2
3export default function AboutHeader() {
4 const t = useTranslations("about");
5 return <h1>{t("header_title1")}</h1>;
6}
8. Ajouter un sélecteur de langue
Voici le code complet du composant LanguageSelector
:
1// src/components/LanguageSelector/index.tsx
2"use client";
3
4import React, { useState, useRef, useEffect } from "react";
5import { useParams, useRouter } from "next/navigation";
6import { cn } from "@/lib/utils";
7import { IconLanguage, IconChevronDown } from "@/lib/icons";
8
9interface Language {
10 code: string;
11 name: string;
12 rtl: boolean;
13}
14
15const languages: Language[] = [
16 { code: "en", name: "English", rtl: false },
17 { code: "fr", name: "Français", rtl: false },
18 { code: "es", name: "Español", rtl: false },
19 { code: "ar", name: "العربية", rtl: true },
20];
21
22export default function LanguageSelector() {
23 const [isOpen, setIsOpen] = useState(false);
24 const dropdownRef = useRef<HTMLDivElement>(null);
25 const router = useRouter();
26 const params = useParams();
27 const currentLocale = typeof params.locale === "string" ? params.locale : "en";
28
29 const currentLanguage = languages.find((lang) => lang.code === currentLocale) || languages[0];
30
31 useEffect(() => {
32 const handleClickOutside = (event: MouseEvent) => {
33 if (
34 dropdownRef.current &&
35 !dropdownRef.current.contains(event.target as Node)
36 ) {
37 setIsOpen(false);
38 }
39 };
40
41 document.addEventListener("mousedown", handleClickOutside);
42 return () => {
43 document.removeEventListener("mousedown", handleClickOutside);
44 };
45 }, []);
46
47 const changeLanguage = (langCode: string) => {
48 const currentPath = window.location.pathname;
49 const pathSegments = currentPath.split('/');
50 pathSegments[1] = langCode;
51 const newPath = pathSegments.join('/');
52 router.push(newPath);
53 setIsOpen(false);
54 };
55
56 return (
57 <div className="relative" ref={dropdownRef}>
58 <button
59 onClick={() => setIsOpen(!isOpen)}
60 className="flex items-center cursor-pointer px-2 py-2 rounded-md hover:bg-gray-100 dark:hover:bg-gray-800 text-sm"
61 aria-expanded={isOpen}
62 aria-haspopup="true"
63 >
64 <IconLanguage className="w-4 h-4 mr-1" />
65 <span className="font-medium text-xs mr-1">{currentLanguage.code.toUpperCase()}</span>
66 <IconChevronDown className={cn("w-3 h-3 transition-transform", isOpen ? "rotate-180" : "")} />
67 </button>
68
69 {isOpen && (
70 <div className="absolute z-50 mt-1 bg-white dark:bg-neutral-900 border border-gray-200 dark:border-gray-700 rounded-md shadow-lg py-1 w-40 min-w-max right-0">
71 <ul className="py-1">
72 {languages.map((language) => (
73 <li key={language.code}>
74 <button
75 onClick={() => changeLanguage(language.code)}
76 className={cn(
77 "w-full text-left cursor-pointer px-4 py-2 text-sm hover:bg-gray-100 dark:hover:bg-gray-800 flex items-center",
78 language.code === currentLocale && "bg-gray-100 dark:bg-gray-800"
79 )}
80 >
81 <span className="inline-block me-2 w-max font-medium text-xs">{language.code.toUpperCase()}</span>
82 {language.name}
83 </button>
84 </li>
85 ))}
86 </ul>
87 </div>
88 )}
89 </div>
90 );
91}
9. Tester votre configuration
Démarrez votre serveur de développement et visitez /en/about
, /fr/about
, etc. Vous devriez voir les bonnes traductions et pouvoir changer de langue.
Conclusion
Avec next-intl
, ajouter la traduction à Next.js 15 est simple et puissant.
Références :