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
Terminalnpm 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 :
TypeScript1import { 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
TypeScript1// 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
JavaScript1const withNextIntl = require('next-intl/plugin')(); 2 3module.exports = withNextIntl({ 4 // Votre configuration Next.js existante 5});
Étape 6 : Créer le Layout avec Locale
TSX1// 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
JSON1// 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}
JSON1// 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
TSX1// 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
TSX1'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
TSX1'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
TSX1// 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
TSX1import { 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
TSX1// 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
JSON1{ 2 "cart": { 3 "items": "{count, plural, =0 {Aucun article} =1 {1 article} other {# articles}}" 4 } 5}
TSXt('cart.items', { count: 5 }) // "5 articles"
Texte Riche
JSON{ "welcome": "Bonjour, <bold>{name}</bold>!" }
TSX1t.rich('welcome', { 2 name: 'John', 3 bold: (chunks) => <strong>{chunks}</strong> 4})
Dates et Nombres
TSX1import { 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
Terminal1# 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.
TypeScript1// 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 :
TypeScript1export 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 :
TypeScript1getRequestConfig(async ({ locale }) => ({ 2 messages: { 3 ...(await import(`./messages/en.json`)).default, // Repli 4 ...(await import(`./messages/${locale}.json`)).default, 5 }, 6}));
Conseils de Performance
- Utilisez la génération statique là où c'est possible
- Divisez le code des grands espaces de noms de traduction
- Mettez en cache les traductions avec ISR
- 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.
