IntlPull
Tutorial
12 min read

Internationalisation Next.js : Guide de Configuration Complet avec App Router (2026)

Tutoriel étape par étape pour ajouter l'internationalisation aux applications Next.js 14+ utilisant l'App Router, next-intl, et les meilleures pratiques pour 2026.

IntlPull Team
IntlPull Team
03 Feb 2026, 11:44 AM [PST]
On this page
Summary

Next.js 14+ avec l'App Router apporte de nouvelles capacités puissantes pour l'internationalisation.

Introduction

Next.js 14+ avec l'App Router apporte de nouvelles capacités puissantes pour l'internationalisation. Ce guide vous montre comment configurer une application Next.js entièrement internationalisée avec un routage approprié, le SEO et la gestion de la traduction.

Prérequis

  • Next.js 14 ou ultérieur
  • Compréhension de base de l'App Router
  • Node.js 18+

Étape 1 : Installer next-intl

Terminal
npm install next-intl

Étape 2 : Structure du Projet

/app
  /[locale]
    /layout.tsx
    /page.tsx
    /about
      /page.tsx
/messages
  /en.json
  /es.json
  /fr.json
/i18n.ts
/middleware.ts
/next.config.js

Étape 3 : Configurer next-intl

Créez i18n.ts à la racine de votre projet :

TypeScript
1import { getRequestConfig } from 'next-intl/server';
2
3export default getRequestConfig(async ({ locale }) => ({
4  messages: (await import(`./messages/${locale}.json`)).default
5}));

Étape 4 : Créer le Middleware

TypeScript
1// middleware.ts
2import createMiddleware from 'next-intl/middleware';
3
4export default createMiddleware({
5  locales: ['en', 'es', 'fr', 'de', 'ja'],
6  defaultLocale: 'en',
7  localePrefix: 'as-needed' // ou 'always' pour le style /en/about
8});
9
10export const config = {
11  matcher: ['/', '/(de|en|es|fr|ja)/:path*']
12};

Étape 5 : Mettre à jour next.config.js

JavaScript
1const withNextIntl = require('next-intl/plugin')();
2
3module.exports = withNextIntl({
4  // Votre configuration Next.js existante
5});

Étape 6 : Créer le Layout avec Locale

TSX
1// app/[locale]/layout.tsx
2import { NextIntlClientProvider } from 'next-intl';
3import { getMessages } from 'next-intl/server';
4
5export default async function LocaleLayout({
6  children,
7  params: { locale }
8}: {
9  children: React.ReactNode;
10  params: { locale: string };
11}) {
12  const messages = await getMessages();
13
14  return (
15    <html lang={locale}>
16      <body>
17        <NextIntlClientProvider messages={messages}>
18          {children}
19        </NextIntlClientProvider>
20      </body>
21    </html>
22  );
23}

Étape 7 : Créer les Fichiers de Traduction

JSON
1// messages/en.json
2{
3  "home": {
4    "title": "Welcome to our app",
5    "description": "The best solution for your needs",
6    "cta": "Get Started"
7  },
8  "navigation": {
9    "home": "Home",
10    "about": "About",
11    "contact": "Contact"
12  }
13}
JSON
1// messages/fr.json
2{
3  "home": {
4    "title": "Bienvenue sur notre application",
5    "description": "La meilleure solution pour vos besoins",
6    "cta": "Commencer"
7  },
8  "navigation": {
9    "home": "Accueil",
10    "about": "À propos",
11    "contact": "Contact"
12  }
13}

Étape 8 : Utiliser les Traductions dans les Pages

Server Components

TSX
1// app/[locale]/page.tsx
2import { getTranslations } from 'next-intl/server';
3
4export default async function HomePage() {
5  const t = await getTranslations('home');
6
7  return (
8    <main>
9      <h1>{t('title')}</h1>
10      <p>{t('description')}</p>
11      <button>{t('cta')}</button>
12    </main>
13  );
14}

Client Components

TSX
1'use client';
2
3import { useTranslations } from 'next-intl';
4
5export function LanguageSwitcher() {
6  const t = useTranslations('navigation');
7  // ... logique de changement de langue
8}

Étape 9 : Ajouter un Sélecteur de Langue

TSX
1'use client';
2
3import { useLocale } from 'next-intl';
4import { useRouter, usePathname } from 'next/navigation';
5
6const locales = ['en', 'es', 'fr', 'de', 'ja'];
7
8export function LanguageSwitcher() {
9  const locale = useLocale();
10  const router = useRouter();
11  const pathname = usePathname();
12
13  const switchLocale = (newLocale: string) => {
14    const newPath = pathname.replace(`/${locale}`, `/${newLocale}`);
15    router.push(newPath);
16  };
17
18  return (
19    <select value={locale} onChange={(e) => switchLocale(e.target.value)}>
20      {locales.map((loc) => (
21        <option key={loc} value={loc}>
22          {loc.toUpperCase()}
23        </option>
24      ))}
25    </select>
26  );
27}

Étape 10 : Optimisation SEO

Générer les Paramètres Statiques

TSX
1// app/[locale]/page.tsx
2export function generateStaticParams() {
3  return [
4    { locale: 'en' },
5    { locale: 'es' },
6    { locale: 'fr' },
7    { locale: 'de' },
8    { locale: 'ja' },
9  ];
10}

Métadonnées avec Traductions

TSX
1import { getTranslations } from 'next-intl/server';
2
3export async function generateMetadata({ params: { locale } }) {
4  const t = await getTranslations({ locale, namespace: 'home' });
5
6  return {
7    title: t('meta.title'),
8    description: t('meta.description'),
9    alternates: {
10      canonical: `https://example.com/${locale}`,
11      languages: {
12        'en': 'https://example.com/en',
13        'es': 'https://example.com/es',
14        'fr': 'https://example.com/fr',
15      },
16    },
17  };
18}

Balises Hreflang

TSX
1// app/[locale]/layout.tsx
2export default function Layout({ children, params: { locale } }) {
3  return (
4    <html lang={locale}>
5      <head>
6        <link rel="alternate" hrefLang="en" href="https://example.com/en" />
7        <link rel="alternate" hrefLang="es" href="https://example.com/es" />
8        <link rel="alternate" hrefLang="x-default" href="https://example.com" />
9      </head>
10      <body>{children}</body>
11    </html>
12  );
13}

Avancé : Pluralisation et Formatage

Pluriels

JSON
1{
2  "cart": {
3    "items": "{count, plural, =0 {Aucun article} =1 {1 article} other {# articles}}"
4  }
5}
TSX
t('cart.items', { count: 5 }) // "5 articles"

Texte Riche

JSON
{
  "welcome": "Bonjour, <bold>{name}</bold>!"
}
TSX
1t.rich('welcome', {
2  name: 'John',
3  bold: (chunks) => <strong>{chunks}</strong>
4})

Dates et Nombres

TSX
1import { useFormatter } from 'next-intl';
2
3function PriceDisplay({ amount, date }) {
4  const format = useFormatter();
5
6  return (
7    <div>
8      <p>{format.number(amount, { style: 'currency', currency: 'USD' })}</p>
9      <p>{format.dateTime(date, { dateStyle: 'full' })}</p>
10    </div>
11  );
12}

Gérer les Traductions à l'Échelle

Gérer manuellement les fichiers JSON devient rapidement pénible. Voici comment IntlPull aide :

Sync Bidirectionnel

Terminal
# Surveiller les changements et synchroniser automatiquement
npx @intlpullhq/cli sync --watch

Cela garde vos fichiers locaux synchronisés avec IntlPull en temps réel.

Upload & Download

Terminal
1# Tirer les dernières versions depuis IntlPull
2npx @intlpullhq/cli download --output ./messages
3
4# Pousser les nouvelles chaînes
5npx @intlpullhq/cli upload --source ./messages/en.json

Traduction IA

Lorsque vous ajoutez une nouvelle chaîne, IntlPull la traduit automatiquement dans toutes vos langues configurées en utilisant une IA consciente du contexte.

Problèmes Courants et Solutions

Problème : Inadéquation d'Hydratation

Solution : Assurez-vous que votre middleware et la détection de locale sont cohérents.

TypeScript
1// middleware.ts
2export default createMiddleware({
3  localePrefix: 'always', // Empêche les inadéquations
4});

Problème : Flash de la Mauvaise Langue

Solution : Utilisez des cookies pour la persistance :

TypeScript
1export default createMiddleware({
2  localeDetection: true,
3  // Stocke la préférence dans le cookie
4});

Problème : Traductions Manquantes en Production

Solution : Configurez la gestion de repli :

TypeScript
1getRequestConfig(async ({ locale }) => ({
2  messages: {
3    ...(await import(`./messages/en.json`)).default, // Repli
4    ...(await import(`./messages/${locale}.json`)).default,
5  },
6}));

Conseils de Performance

  1. Utilisez la génération statique là où c'est possible
  2. Divisez le code des grands espaces de noms de traduction
  3. Mettez en cache les traductions avec ISR
  4. Chargez paresseusement les traductions non critiques

Conclusion

Next.js avec next-intl fournit un moyen puissant et type-safe de construire des applications internationalisées. Combiné avec un système de gestion de traduction comme IntlPull, vous pouvez expédier des fonctionnalités localisées plus rapidement que jamais.

Prêt à simplifier votre i18n Next.js ? Essayez IntlPull gratuitement et automatisez votre flux de traduction.

Tags
nextjs
i18n
next-intl
app-router
tutorial
react
2026
IntlPull Team
IntlPull Team
Engineering

Building tools to help teams ship products globally. Follow us for more insights on localization and i18n.