Réponse Rapide
next-intl est la meilleure bibliothèque d'internationalisation (i18n) pour Next.js App Router. Installez avec npm install next-intl, créez un dossier [locale] dans votre répertoire app, configurez le middleware pour la détection de locale, et utilisez getTranslations() dans les Server Components ou useTranslations() dans les Client Components. next-intl est spécifiquement conçu pour Next.js 13-15+ avec un support natif des Server Components, un routage automatique et une taille de bundle de ~2KB.
Qu'est-ce que next-intl ?
next-intl est une bibliothèque d'internationalisation légère construite spécifiquement pour Next.js. Contrairement aux bibliothèques i18n React généralistes, next-intl a été conçu dès le départ pour l'App Router et les Server Components.
Pourquoi next-intl Plutôt que d'Autres Bibliothèques ?
| Fonctionnalité | next-intl | react-i18next | react-intl |
|---|---|---|---|
| Construit pour Next.js | Oui | Non (Général React) | Non (Général React) |
| Server Components | Natif | Nécessite un wrapper | Nécessite un wrapper |
| Taille du bundle | ~2KB | ~8KB | ~12KB |
| Support App Router | De premier ordre | Partiel | Partiel |
| TypeScript | Excellent | Bon | Bon |
| Intégration routage | Intégré | Manuel | Manuel |
Avantages clés :
- Zéro JS côté client pour les traductions rendues par le serveur - les traductions restent sur le serveur
- Routage locale automatique - /en/about, /fr/about gérés automatiquement
- Format de message ICU - pluralisation, genre et formatage corrects
- Type-safe - support TypeScript complet avec autocomplétion
Lorsque vous passez à plus grande échelle que les fichiers JSON, IntlPull s'intègre parfaitement avec next-intl pour la gestion de traduction, la traduction IA et la collaboration d'équipe.
Installation & Configuration
Étape 1 : Installer next-intl
Terminal1npm install next-intl 2# ou 3yarn add next-intl 4# ou 5pnpm add next-intl
Étape 2 : Structure du Projet
Créez cette structure de dossier :
/votre-app-nextjs
├── /app
│ └── /[locale] # Segment locale dynamique
│ ├── layout.tsx # Layout conscient de la locale
│ ├── page.tsx # Page d'accueil
│ └── /about
│ └── page.tsx # Page à propos
├── /messages # Fichiers de traduction
│ ├── en.json
│ ├── es.json
│ └── de.json
├── /i18n
│ ├── config.ts # Configuration locale
│ └── request.ts # Config côté serveur
├── middleware.ts # Détection locale & routage
└── next.config.js
Étape 3 : Configurer les Locales
Créez i18n/config.ts:
TypeScript1export const locales = ['en', 'es', 'de', 'fr', 'ja'] as const; 2export type Locale = (typeof locales)[number]; 3export const defaultLocale: Locale = 'en'; 4 5// Optionnel : métadonnées locale pour UI 6export const localeNames: Record<Locale, string> = { 7 en: 'English', 8 es: 'Español', 9 de: 'Deutsch', 10 fr: 'Français', 11 ja: '日本語', 12};
Étape 4 : Créer la Configuration de Requête
Créez i18n/request.ts:
TypeScript1import { getRequestConfig } from 'next-intl/server'; 2import { notFound } from 'next/navigation'; 3import { locales } from './config'; 4 5export default getRequestConfig(async ({ locale }) => { 6 // Valider que la locale entrante est supportée 7 if (!locales.includes(locale as any)) { 8 notFound(); 9 } 10 11 return { 12 messages: (await import(`../messages/${locale}.json`)).default, 13 }; 14});
Étape 5 : Configurer next.config.js
JavaScript1const createNextIntlPlugin = require('next-intl/plugin'); 2 3const withNextIntl = createNextIntlPlugin('./i18n/request.ts'); 4 5/** @type {import('next').NextConfig} */ 6const nextConfig = { 7 // Votre autre config Next.js 8}; 9 10module.exports = withNextIntl(nextConfig);
Étape 6 : Créer le Middleware
Créez middleware.ts à la racine de votre projet :
TypeScript1import createMiddleware from 'next-intl/middleware'; 2import { locales, defaultLocale } from './i18n/config'; 3 4export default createMiddleware({ 5 locales, 6 defaultLocale, 7 localePrefix: 'always', // ou 'as-needed' ou 'never' 8}); 9 10export const config = { 11 // Correspondre à tous les chemins sauf 12 // - Routes API 13 // - _next (internes Next.js) 14 // - Fichiers statiques (images, etc.) 15 matcher: ['/((?!api|_next|.*\\..*).*)'], 16};
Étape 7 : Créer les Fichiers de Traduction
Créez messages/en.json:
JSON1{ 2 "common": { 3 "welcome": "Welcome to our app", 4 "loading": "Loading...", 5 "error": "Something went wrong" 6 }, 7 "home": { 8 "title": "Home", 9 "description": "This is the home page", 10 "cta": "Get Started" 11 }, 12 "navigation": { 13 "home": "Home", 14 "about": "About", 15 "contact": "Contact" 16 } 17}
Créez messages/fr.json:
JSON1{ 2 "common": { 3 "welcome": "Bienvenue sur notre application", 4 "loading": "Chargement...", 5 "error": "Une erreur est survenue" 6 }, 7 "home": { 8 "title": "Accueil", 9 "description": "Ceci est la page d'accueil", 10 "cta": "Commencer" 11 }, 12 "navigation": { 13 "home": "Accueil", 14 "about": "À propos", 15 "contact": "Contact" 16 } 17}
Étape 8 : Créer le Layout Racine
Créez app/[locale]/layout.tsx:
TypeScript1import { NextIntlClientProvider } from 'next-intl'; 2import { getMessages } from 'next-intl/server'; 3import { notFound } from 'next/navigation'; 4import { locales } from '@/i18n/config'; 5 6export function generateStaticParams() { 7 return locales.map((locale) => ({ locale })); 8} 9 10export default async function LocaleLayout({ 11 children, 12 params: { locale }, 13}: { 14 children: React.ReactNode; 15 params: { locale: string }; 16}) { 17 // Valider la locale 18 if (!locales.includes(locale as any)) { 19 notFound(); 20 } 21 22 // Obtenir les messages pour la locale actuelle 23 const messages = await getMessages(); 24 25 return ( 26 <html lang={locale}> 27 <body> 28 <NextIntlClientProvider messages={messages}> 29 {children} 30 </NextIntlClientProvider> 31 </body> 32 </html> 33 ); 34}
Utiliser les Traductions
Server Components (Recommandé)
Dans les Server Components, utilisez getTranslations():
TypeScript1import { getTranslations } from 'next-intl/server'; 2 3export default async function HomePage() { 4 const t = await getTranslations('home'); 5 6 return ( 7 <main> 8 <h1>{t('title')}</h1> 9 <p>{t('description')}</p> 10 <button>{t('cta')}</button> 11 </main> 12 ); 13}
C'est l'approche recommandée car :
- Les traductions sont rendues sur le serveur
- Zéro JavaScript envoyé au client pour ces chaînes
- Meilleure performance et SEO
Client Components
Pour les composants interactifs, utilisez useTranslations():
TypeScript1'use client'; 2 3import { useTranslations } from 'next-intl'; 4 5export function AddToCartButton() { 6 const t = useTranslations('product'); 7 8 const handleClick = () => { 9 // Logique côté client 10 }; 11 12 return ( 13 <button onClick={handleClick}> 14 {t('addToCart')} 15 </button> 16 ); 17}
Accéder à Plusieurs Espaces de Noms
TypeScript1import { getTranslations } from 'next-intl/server'; 2 3export default async function Page() { 4 const t = await getTranslations('home'); 5 const tCommon = await getTranslations('common'); 6 7 return ( 8 <div> 9 <h1>{t('title')}</h1> 10 <p>{tCommon('welcome')}</p> 11 </div> 12 ); 13}
Variables et Interpolation
Variables de Base
JSON1{ 2 "greeting": "Hello, {name}!", 3 "items": "You have {count} items in your cart" 4}
TypeScriptt('greeting', { name: 'John' }) // "Hello, John!" t('items', { count: 5 }) // "You have 5 items in your cart"
Pluralisation (Format ICU)
next-intl utilise le format de message ICU pour une pluralisation correcte :
JSON{ "cartItems": "{count, plural, =0 {Your cart is empty} one {# item in cart} other {# items in cart}}" }
TypeScriptt('cartItems', { count: 0 }) // "Your cart is empty" t('cartItems', { count: 1 }) // "1 item in cart" t('cartItems', { count: 5 }) // "5 items in cart"
Catégories plurielles ICU par langue :
| Langue | Catégories |
|---|---|
| Anglais | one, other |
| Français | one, other (parfois many) |
| Russe | one, few, many, other |
| Arabe | zero, one, two, few, many, other |
| Japonais | other (pas de pluriels) |
next-intl gère tout cela automatiquement basé sur la locale.
Select (Genre, Statut, etc.)
JSON{ "userStatus": "{status, select, online {User is online} offline {User is offline} away {User is away} other {Unknown status}}" }
TypeScriptt('userStatus', { status: 'online' }) // "User is online"
Texte Riche (HTML/Composants)
JSON{ "terms": "By signing up, you agree to our <terms>Terms of Service</terms> and <privacy>Privacy Policy</privacy>." }
TypeScript1import { useTranslations } from 'next-intl'; 2import Link from 'next/link'; 3 4function SignupForm() { 5 const t = useTranslations('auth'); 6 7 return ( 8 <p> 9 {t.rich('terms', { 10 terms: (chunks) => <Link href="/terms">{chunks}</Link>, 11 privacy: (chunks) => <Link href="/privacy">{chunks}</Link>, 12 })} 13 </p> 14 ); 15}
Formatage Nombre, Date et Devise
Formatage de Nombre
TypeScript1import { useFormatter } from 'next-intl'; 2 3function PriceDisplay({ price }: { price: number }) { 4 const format = useFormatter(); 5 6 return ( 7 <span> 8 {format.number(price, { style: 'currency', currency: 'USD' })} 9 </span> 10 ); 11} 12 13// en-US: "$1,234.56" 14// de-DE: "1.234,56 $" 15// ja-JP: "$1,234.56"
Formatage de Date
TypeScript1import { useFormatter } from 'next-intl'; 2 3function DateDisplay({ date }: { date: Date }) { 4 const format = useFormatter(); 5 6 return ( 7 <> 8 <p>{format.dateTime(date, { dateStyle: 'full' })}</p> 9 <p>{format.dateTime(date, { timeStyle: 'short' })}</p> 10 <p>{format.relativeTime(date)}</p> 11 </> 12 ); 13} 14 15// en-US: "Friday, January 17, 2026" 16// de-DE: "Freitag, 17. Januar 2026"
Temps Relatif
TypeScript1const format = useFormatter(); 2 3format.relativeTime(new Date('2026-01-10')) // "7 days ago" 4format.relativeTime(new Date('2026-01-20')) // "in 3 days"
Formatage de Liste
TypeScript1const format = useFormatter(); 2 3format.list(['Apple', 'Banana', 'Orange'], { type: 'conjunction' }) 4// en: "Apple, Banana, and Orange" 5// de: "Apple, Banana und Orange"
Routage et Navigation
Liens Conscients de la Locale
Utilisez next-intl/link pour le préfixage automatique de locale :
TypeScript1import Link from 'next-intl/link'; 2 3function Navigation() { 4 return ( 5 <nav> 6 <Link href="/">Home</Link> 7 <Link href="/about">About</Link> 8 <Link href="/contact">Contact</Link> 9 </nav> 10 ); 11} 12// Rends automatiquement comme /en/about, /fr/about basé sur la locale actuelle
useRouter Conscient de la Locale
TypeScript1'use client'; 2 3import { useRouter } from 'next-intl/client'; 4 5function SearchForm() { 6 const router = useRouter(); 7 8 const handleSearch = (query: string) => { 9 router.push(`/search?q=${query}`); 10 // Inclut automatiquement le préfixe locale 11 }; 12 13 return (/* form */); 14}
Sélecteur de Langue
TypeScript1'use client'; 2 3import { useLocale } from 'next-intl'; 4import { useRouter, usePathname } from 'next-intl/client'; 5import { locales, localeNames } from '@/i18n/config'; 6 7export function LanguageSwitcher() { 8 const locale = useLocale(); 9 const router = useRouter(); 10 const pathname = usePathname(); 11 12 const handleChange = (newLocale: string) => { 13 router.replace(pathname, { locale: newLocale }); 14 }; 15 16 return ( 17 <select value={locale} onChange={(e) => handleChange(e.target.value)}> 18 {locales.map((loc) => ( 19 <option key={loc} value={loc}> 20 {localeNames[loc]} 21 </option> 22 ))} 23 </select> 24 ); 25}
Stratégies d'URL
Configurez dans le middleware :
TypeScript1// Option 1 : Toujours montrer le préfixe locale 2// /en/about, /es/about, /de/about 3localePrefix: 'always' 4 5// Option 2 : Cacher la locale par défaut 6// /about (Anglais), /es/about (Espagnol) 7localePrefix: 'as-needed' 8 9// Option 3 : Ne jamais montrer le préfixe (utiliser cookies/headers) 10localePrefix: 'never'
Recommandation : Utilisez 'always' pour le meilleur SEO et les URL les plus claires.
Intégration TypeScript
Traductions Type-Safe
Créez global.d.ts:
TypeScript1import en from './messages/en.json'; 2 3type Messages = typeof en; 4 5declare global { 6 interface IntlMessages extends Messages {} 7}
Maintenant vous obtenez l'autocomplétion et la vérification de type :
TypeScript1const t = useTranslations('home'); 2 3t('title') // ✅ Valide 4t('nonExistent') // ❌ Erreur TypeScript
Clés Imbriquées
TypeScript// Pour les messages profondément imbriqués const t = useTranslations('settings.account.privacy'); t('title') // settings.account.privacy.title
SEO et Métadonnées
Métadonnées Conscientes de la Locale
TypeScript1import { getTranslations } from 'next-intl/server'; 2import { Metadata } from 'next'; 3 4export async function generateMetadata({ 5 params: { locale }, 6}: { 7 params: { locale: string }; 8}): Promise<Metadata> { 9 const t = await getTranslations({ locale, namespace: 'meta' }); 10 11 return { 12 title: t('title'), 13 description: t('description'), 14 }; 15}
Balises Hreflang
TypeScript1import { locales } from '@/i18n/config'; 2 3export async function generateMetadata({ 4 params: { locale }, 5}: { 6 params: { locale: string }; 7}) { 8 const baseUrl = 'https://example.com'; 9 10 return { 11 alternates: { 12 canonical: `${baseUrl}/${locale}`, 13 languages: Object.fromEntries( 14 locales.map((loc) => [loc, `${baseUrl}/${loc}`]) 15 ), 16 }, 17 }; 18}
Cela génère des balises hreflang appropriées pour le SEO :
HTML<link rel="alternate" hreflang="en" href="https://example.com/en" /> <link rel="alternate" hreflang="es" href="https://example.com/es" /> <link rel="alternate" hreflang="de" href="https://example.com/de" />
Optimisation de la Performance
Division de Code Basée sur l'Espace de Noms
Chargez seulement les traductions nécessaires pour chaque page :
TypeScript1// Dans i18n/request.ts 2export default getRequestConfig(async ({ locale }) => { 3 return { 4 messages: { 5 // Charger seulement l'espace de noms commun par défaut 6 common: (await import(`../messages/${locale}/common.json`)).default, 7 }, 8 }; 9}); 10 11// Dans les pages spécifiques, charger des espaces de noms supplémentaires 12import { getMessages } from 'next-intl/server'; 13 14export default async function CheckoutPage() { 15 const messages = await getMessages({ namespace: 'checkout' }); 16 // ... 17}
Génération Statique
Assurez-vous que toutes les pages locale sont générées statiquement :
TypeScriptexport function generateStaticParams() { return locales.map((locale) => ({ locale })); }
Éviter les Inadéquations d'Hydratation
Passez l'heure serveur pour éviter les inadéquations date/heure :
TypeScript1<NextIntlClientProvider 2 messages={messages} 3 timeZone="Europe/Berlin" 4 now={new Date()} 5> 6 {children} 7</NextIntlClientProvider>
Gérer les Traductions à l'Échelle
À mesure que votre app grandit, gérer les fichiers JSON manuellement devient problématique :
- Traductions manquantes difficiles à suivre
- Conflits de fusion quand plusieurs développeurs éditent les fichiers de traduction
- Pas de contexte pour les traducteurs
- Flux lent avec export/import manuel
Intégration IntlPull
IntlPull fournit une intégration next-intl transparente :
Terminal1# Initialiser IntlPull dans votre projet 2npx @intlpullhq/cli init 3 4# Télécharger vos traductions existantes (upload) 5npx @intlpullhq/cli upload 6 7# Obtenir des traductions IA pour de nouvelles langues 8npx @intlpullhq/cli translate --languages es,de,fr,ja 9 10# Tirer les traductions vers votre projet (download) 11npx @intlpullhq/cli download 12 13# Mode surveillance pour sync temps réel pendant le développement 14npx @intlpullhq/cli watch
Avantages :
- Traduction alimentée par IA avec GPT-4, Claude, DeepL
- Éditeur visuel avec contexte par capture d'écran
- Collaboration d'équipe avec flux de révision
- Sync automatique avec votre base de code
- Mémoire de traduction pour la cohérence
Commencez gratuitement avec IntlPull →
Modèles Courants
États de Chargement avec Suspense
TypeScript1import { Suspense } from 'react'; 2 3export default function Page() { 4 return ( 5 <Suspense fallback={<Skeleton />}> 6 <TranslatedContent /> 7 </Suspense> 8 ); 9}
Frontières d'Erreur (Error Boundaries)
TypeScript1import { useTranslations } from 'next-intl'; 2 3function ErrorBoundary({ error }: { error: Error }) { 4 const t = useTranslations('errors'); 5 6 return ( 7 <div> 8 <h2>{t('title')}</h2> 9 <p>{t('message')}</p> 10 </div> 11 ); 12}
Routes Dynamiques avec Locales
TypeScript1// app/[locale]/blog/[slug]/page.tsx 2import { getTranslations } from 'next-intl/server'; 3 4export default async function BlogPost({ 5 params: { locale, slug }, 6}: { 7 params: { locale: string; slug: string }; 8}) { 9 const t = await getTranslations('blog'); 10 11 // Récupérer le contenu localisé 12 const post = await getPost(slug, locale); 13 14 return ( 15 <article> 16 <h1>{post.title}</h1> 17 <p>{t('readTime', { minutes: post.readTime })}</p> 18 </article> 19 ); 20}
Support Langue RTL
Pour l'Arabe, l'Hébreu, le Persan et autres langues RTL :
TypeScript1import { locales } from '@/i18n/config'; 2 3const rtlLocales = ['ar', 'he', 'fa']; 4 5export default async function LocaleLayout({ 6 children, 7 params: { locale }, 8}: { 9 children: React.ReactNode; 10 params: { locale: string }; 11}) { 12 const dir = rtlLocales.includes(locale) ? 'rtl' : 'ltr'; 13 14 return ( 15 <html lang={locale} dir={dir}> 16 <body>{children}</body> 17 </html> 18 ); 19}
Utilisez les propriétés logiques CSS :
CSS1/* Au lieu de margin-left, utilisez : */ 2margin-inline-start: 1rem; 3 4/* Au lieu de padding-right, utilisez : */ 5padding-inline-end: 1rem; 6 7/* Au lieu de text-align: left, utilisez : */ 8text-align: start;
Tests
Tests Unitaires de Composants
TypeScript1import { render, screen } from '@testing-library/react'; 2import { NextIntlClientProvider } from 'next-intl'; 3import messages from '../messages/en.json'; 4import { HomePage } from './page'; 5 6function renderWithIntl(ui: React.ReactNode, locale = 'en') { 7 return render( 8 <NextIntlClientProvider locale={locale} messages={messages}> 9 {ui} 10 </NextIntlClientProvider> 11 ); 12} 13 14test('renders home page title', () => { 15 renderWithIntl(<HomePage />); 16 expect(screen.getByText('Home')).toBeInTheDocument(); 17});
Tester Plusieurs Locales
TypeScript1const locales = ['en', 'es', 'de']; 2 3describe.each(locales)('HomePage in %s', (locale) => { 4 test('renders without crashing', () => { 5 const messages = require(`../messages/${locale}.json`); 6 renderWithIntl(<HomePage />, locale, messages); 7 // Affirmer basé sur les traductions attendues 8 }); 9});
Migrer vers next-intl
Depuis react-i18next
TypeScript1// Avant (react-i18next) 2import { useTranslation } from 'react-i18next'; 3const { t } = useTranslation(); 4t('home.title') 5 6// Après (next-intl) 7import { useTranslations } from 'next-intl'; 8const t = useTranslations('home'); 9t('title')
Depuis next-translate
TypeScript1// Avant (next-translate) 2import useTranslation from 'next-translate/useTranslation'; 3const { t } = useTranslation('common'); 4 5// Après (next-intl) 6import { useTranslations } from 'next-intl'; 7const t = useTranslations('common');
Le format de fichier de traduction est similaire — la plupart des fichiers JSON fonctionnent sans changement.
next-intl vs Alternatives
| Aspect | next-intl | react-i18next | next-translate |
|---|---|---|---|
| Optimisé Next.js | Oui | Non | Oui |
| Server Components | Natif | Wrapper nécessaire | Natif |
| Taille du bundle | ~2KB | ~8KB | ~1.5KB |
| Format ICU | Complet | Plugin nécessaire | Basique |
| TypeScript | Excellent | Bon | Basique |
| Routage | Intégré | Manuel | Intégré |
| Développement actif | Très actif | Actif | Modéré |
| Documentation | Excellent | Bon | Bon |
Recommandation : Utilisez next-intl pour tout nouveau projet Next.js App Router. C'est l'option la plus complète et la mieux maintenue spécifiquement conçue pour Next.js.
Dépannage
Erreurs "Missing message"
TypeScript1// En développement, montrer le nom de la clé ; en production, retourner vide 2<NextIntlClientProvider 3 messages={messages} 4 onError={(error) => { 5 if (process.env.NODE_ENV === 'development') { 6 console.error(error); 7 } 8 }} 9 getMessageFallback={({ key }) => { 10 return process.env.NODE_ENV === 'development' ? `[${key}]` : ''; 11 }} 12>
Inadéquation d'Hydratation
Généralement causée par des différences de date/heure entre serveur et client :
TypeScript1// Passer l'heure serveur explicitement 2<NextIntlClientProvider 3 messages={messages} 4 now={new Date()} 5 timeZone="UTC" 6>
Middleware Ne S'exécutant Pas
Vérifiez votre modèle matcher :
TypeScript1export const config = { 2 matcher: [ 3 // Correspondre à tous les chemins sauf fichiers statiques et API 4 '/((?!api|_next/static|_next/image|favicon.ico|.*\\..*).*)', 5 ], 6};
Foire Aux Questions
Qu'est-ce que next-intl ?
next-intl est une bibliothèque d'internationalisation (i18n) spécifiquement construite pour Next.js. Elle fournit la gestion de traduction, le routage basé sur la locale, le formatage date/nombre, et un support complet pour les Server Components et l'App Router. C'est la solution i18n recommandée pour les projets Next.js 13+ en raison de son intégration native et de sa petite taille de bundle (~2KB).
next-intl est-il meilleur que react-i18next pour Next.js ?
Oui, next-intl est meilleur que react-i18next pour les projets Next.js App Router. next-intl a été conçu spécifiquement pour Next.js avec un support natif des Server Components, un routage intégré, et zéro configuration pour l'App Router. react-i18next est une bibliothèque React générale qui nécessite une configuration supplémentaire pour les Server Components et ne s'intègre pas aussi parfaitement avec le routage Next.js.
Comment configurer next-intl avec Next.js App Router ?
Pour configurer next-intl : (1) Installez avec npm install next-intl, (2) Créez un dossier [locale] dans le répertoire app, (3) Créez des fichiers JSON de traduction dans un dossier messages, (4) Configurez middleware.ts pour la détection locale, (5) Créez i18n/request.ts pour la config serveur, (6) Enveloppez votre app avec NextIntlClientProvider. Voir la section configuration complète ci-dessus pour le code détaillé.
next-intl fonctionne-t-il avec les Server Components ?
Oui, next-intl a un support natif des Server Components. Utilisez getTranslations() de next-intl/server dans les Server Components — les traductions sont rendues sur le serveur avec zéro JavaScript côté client. C'est un avantage majeur par rapport à react-i18next qui nécessite des composants wrapper pour le support Server Component.
Comment ajouter un sélecteur de langue avec next-intl ?
Utilisez useLocale() et useRouter() de next-intl : Obtenez la locale actuelle avec useLocale(), obtenez le chemin avec usePathname() de next-intl/client, puis appelez router.replace(pathname, { locale: newLocale }) pour changer de langue. Voir la section Sélecteur de Langue ci-dessus pour le code complet.
Comment gérer la pluralisation dans next-intl ?
next-intl utilise le format de message ICU pour la pluralisation. Définissez les messages pluriels comme : "{count, plural, =0 {No items} one {# item} other {# items}}". Puis appelez t('key', { count: 5 }). next-intl sélectionne automatiquement la forme plurielle correcte basée sur la locale — gérant les langues avec 2 formes (Anglais) jusqu'à 6 formes (Arabe) correctement.
Puis-je utiliser next-intl avec TypeScript ?
Oui, next-intl a un excellent support TypeScript. Créez un fichier global.d.ts qui étend IntlMessages avec vos types de messages, et vous obtiendrez l'autocomplétion complète et la vérification de type pour les clés de traduction. Cela attrape les clés manquantes ou mal orthographiées à la compilation plutôt qu'à l'exécution.
Comment gérer les traductions à l'échelle avec next-intl ?
Utilisez un Système de Gestion de Traduction (TMS) comme IntlPull quand vous avez 500+ chaînes ou 3+ langues. IntlPull fournit : sync CLI (npx @intlpullhq/cli upload/download), traduction alimentée par IA, collaboration d'équipe, contexte visuel pour traducteurs, et intégration next-intl transparente. Cela élimine la gestion manuelle de fichiers JSON et les conflits de fusion.
Quelle est la taille du bundle de next-intl ?
next-intl ajoute environ 2KB à votre bundle client (gzippé). C'est significativement plus petit que react-i18next (~8KB) ou react-intl (~12KB). De plus, les traductions rendues dans les Server Components ajoutent zéro octet au bundle client puisqu'elles sont rendues côté serveur.
next-intl supporte-t-il les langues RTL ?
Oui, next-intl supporte pleinement les langues RTL comme l'Arabe, l'Hébreu et le Persan. Vous devez définir l'attribut dir="rtl" sur votre élément HTML basé sur la locale et utiliser les propriétés logiques CSS (margin-inline-start au lieu de margin-left). Voir la section Support Langue RTL pour les détails d'implémentation.
Comment tester les composants utilisant next-intl ?
Enveloppez les composants avec NextIntlClientProvider dans les tests. Créez une fonction d'aide qui enveloppe votre composant avec le provider, la locale et les messages. Utilisez cela pour les tests unitaires et les tests d'intégration. Vous pouvez tester plusieurs locales en utilisant describe.each() pour assurer que les traductions fonctionnent dans toutes les langues supportées.
Résumé
next-intl est le meilleur choix pour l'internationalisation Next.js en 2026. Points clés à retenir :
- Construit pour Next.js - Support natif App Router et Server Component
- Léger - Bundle ~2KB, zéro JS pour les traductions rendues serveur
- Complet - Routage, formatage, pluralisation, TypeScript
- Bien maintenu - Développement actif, documentation excellente
Pour commencer :
npm install next-intl- Créez la structure de dossier
[locale] - Configurez middleware et i18n/request.ts
- Utilisez
getTranslations()dans les Server Components - Passez à l'échelle avec IntlPull pour la traduction IA et la collaboration d'équipe
Prêt à gérer les traductions next-intl professionnellement ? Commencez gratuitement avec IntlPull — traduction IA, éditeur visuel, et sync CLI inclus.
