Réponse Rapide
react-i18next est la bibliothèque i18n la plus populaire pour React avec 3,5M+ téléchargements hebdomadaires npm. Installez avec npm install react-i18next i18next, créez des fichiers de traduction JSON, configurez i18next avec i18n.init(), puis utilisez le hook useTranslation : const { t } = useTranslation(); return <h1>{t('welcome')}</h1>. Pour les applications en production, combinez avec IntlPull pour la gestion de traduction alimentée par IA et les mises à jour OTA.
Qu'est-ce que react-i18next ?
react-i18next est une liaison React pour i18next, le framework d'internationalisation JavaScript le plus éprouvé au combat. Il fournit :
- Hooks React (
useTranslation) pour composants fonctionnels - HOCs et render props pour composants de classe
- Support Suspense pour le chargement asynchrone de traduction
- Compatibilité SSR avec Next.js, Remix et Gatsby
- Écosystème de plugins pour backends, détection de langue et mise en cache
Pourquoi Choisir react-i18next ?
| Fonctionnalité | react-i18next | react-intl | next-intl |
|---|---|---|---|
| Téléchargements Hebdo | 3.5M+ | 1.2M | 800K |
| Taille Bundle | ~15KB | ~25KB | ~12KB |
| Framework | Tout React | Tout React | Next.js seulement |
| Courbe d'Apprentissage | Faible | Moyenne | Faible |
| Système Plugin | Étendu | Limité | N/A |
| TypeScript | Excellent | Bon | Excellent |
Installation et Configuration
Étape 1 : Installer les Dépendances
Terminal1npm install react-i18next i18next 2# ou 3yarn add react-i18next i18next 4# ou 5pnpm add react-i18next i18next
Plugins optionnels :
Terminal1# Détection de langue 2npm install i18next-browser-languagedetector 3 4# Charger les traductions depuis le backend 5npm install i18next-http-backend 6 7# Pour React Native 8npm install react-i18next i18next @os-team/i18next-react-native-language-detector
Étape 2 : Créer les Fichiers de Traduction
src/
├── locales/
│ ├── en/
│ │ ├── common.json
│ │ └── home.json
│ ├── es/
│ │ ├── common.json
│ │ └── home.json
│ └── fr/
│ ├── common.json
│ └── home.json
└── i18n.ts
en/common.json:
JSON1{ 2 "welcome": "Welcome to our app", 3 "nav": { 4 "home": "Home", 5 "about": "About", 6 "contact": "Contact" 7 }, 8 "buttons": { 9 "submit": "Submit", 10 "cancel": "Cancel", 11 "save": "Save" 12 } 13}
fr/common.json:
JSON1{ 2 "welcome": "Bienvenue sur notre application", 3 "nav": { 4 "home": "Accueil", 5 "about": "À propos", 6 "contact": "Contact" 7 }, 8 "buttons": { 9 "submit": "Envoyer", 10 "cancel": "Annuler", 11 "save": "Enregistrer" 12 } 13}
Étape 3 : Configurer i18next
TypeScript1// src/i18n.ts 2import i18n from 'i18next'; 3import { initReactI18next } from 'react-i18next'; 4import LanguageDetector from 'i18next-browser-languagedetector'; 5 6// Importer les traductions 7import enCommon from './locales/en/common.json'; 8import enHome from './locales/en/home.json'; 9import frCommon from './locales/fr/common.json'; 10import frHome from './locales/fr/home.json'; 11 12i18n 13 .use(LanguageDetector) 14 .use(initReactI18next) 15 .init({ 16 resources: { 17 en: { 18 common: enCommon, 19 home: enHome, 20 }, 21 fr: { 22 common: frCommon, 23 home: frHome, 24 }, 25 }, 26 defaultNS: 'common', 27 fallbackLng: 'en', 28 supportedLngs: ['en', 'fr'], 29 30 interpolation: { 31 escapeValue: false, // React échappe déjà 32 }, 33 34 detection: { 35 order: ['localStorage', 'navigator', 'htmlTag'], 36 caches: ['localStorage'], 37 }, 38 }); 39 40export default i18n;
Étape 4 : Initialiser dans Votre App
TSX1// src/main.tsx ou src/index.tsx 2import React from 'react'; 3import ReactDOM from 'react-dom/client'; 4import './i18n'; // Importer la configuration i18n 5import App from './App'; 6 7ReactDOM.createRoot(document.getElementById('root')!).render( 8 <React.StrictMode> 9 <App /> 10 </React.StrictMode> 11);
Utiliser le Hook useTranslation
Le hook useTranslation est le moyen principal d'accéder aux traductions dans les composants fonctionnels.
Usage de Base
TSX1import { useTranslation } from 'react-i18next'; 2 3function Header() { 4 const { t } = useTranslation(); 5 6 return ( 7 <header> 8 <h1>{t('welcome')}</h1> 9 <nav> 10 <a href="/">{t('nav.home')}</a> 11 <a href="/about">{t('nav.about')}</a> 12 </nav> 13 </header> 14 ); 15}
Avec Espaces de Noms
TSX1import { useTranslation } from 'react-i18next'; 2 3function HomePage() { 4 // Charger un espace de noms spécifique 5 const { t } = useTranslation('home'); 6 7 return <h1>{t('hero.title')}</h1>; 8} 9 10function MultiNamespace() { 11 // Charger plusieurs espaces de noms 12 const { t } = useTranslation(['common', 'home']); 13 14 return ( 15 <div> 16 <h1>{t('home:hero.title')}</h1> 17 <button>{t('common:buttons.submit')}</button> 18 </div> 19 ); 20}
Changer de Langue
TSX1import { useTranslation } from 'react-i18next'; 2 3function LanguageSwitcher() { 4 const { i18n } = useTranslation(); 5 6 const changeLanguage = (lng: string) => { 7 i18n.changeLanguage(lng); 8 }; 9 10 return ( 11 <div> 12 <button onClick={() => changeLanguage('en')}>English</button> 13 <button onClick={() => changeLanguage('fr')}>Français</button> 14 </div> 15 ); 16}
Interpolation de Variable
Variables de Base
JSON1{ 2 "greeting": "Bonjour, {{name}} !", 3 "itemCount": "Vous avez {{count}} articles dans votre panier" 4}
TSX1function Greeting({ user }) { 2 const { t } = useTranslation(); 3 4 return ( 5 <div> 6 <h1>{t('greeting', { name: user.name })}</h1> 7 <p>{t('itemCount', { count: user.cartItems })}</p> 8 </div> 9 ); 10}
Formatage des Valeurs
JSON1{ 2 "price": "Total : {{price, currency}}", 3 "date": "Créé le {{date, datetime}}" 4}
TypeScript1// i18n.ts - Ajouter formateurs 2i18n.init({ 3 // ... autre config 4 interpolation: { 5 escapeValue: false, 6 format: (value, format, lng) => { 7 if (format === 'currency') { 8 return new Intl.NumberFormat(lng, { 9 style: 'currency', 10 currency: 'USD', 11 }).format(value); 12 } 13 if (format === 'datetime') { 14 return new Intl.DateTimeFormat(lng).format(value); 15 } 16 return value; 17 }, 18 }, 19});
Pluralisation
Pluriels de Base
JSON1{ 2 "item_one": "{{count}} article", 3 "item_other": "{{count}} articles", 4 "item_zero": "Aucun article" 5}
TSX1function CartCount({ count }) { 2 const { t } = useTranslation(); 3 4 return <span>{t('item', { count })}</span>; 5 // count=0: "Aucun article" 6 // count=1: "1 article" 7 // count=5: "5 articles" 8}
Pluriels Complexes (Russe, Arabe, etc.)
JSON1{ 2 "item_zero": "нет товаров", 3 "item_one": "{{count}} товар", 4 "item_few": "{{count}} товара", 5 "item_many": "{{count}} товаров", 6 "item_other": "{{count}} товаров" 7}
Ordinaux
JSON1{ 2 "place_ordinal_one": "{{count}}er place", 3 "place_ordinal_two": "{{count}}nd place", 4 "place_ordinal_few": "{{count}}ème place", 5 "place_ordinal_other": "{{count}}ème place" 6}
TSXt('place', { count: 1, ordinal: true }); // "1er place" t('place', { count: 2, ordinal: true }); // "2nd place" t('place', { count: 3, ordinal: true }); // "3ème place"
Texte Riche et Composants
Utiliser le Composant Trans
JSON1{ 2 "terms": "En vous inscrivant, vous acceptez nos <link>Conditions d'Utilisation</link>", 3 "welcome": "Bienvenue <bold>{{name}}</bold> sur notre plateforme !" 4}
TSX1import { Trans, useTranslation } from 'react-i18next'; 2 3function TermsText() { 4 return ( 5 <Trans 6 i18nKey="terms" 7 components={{ 8 link: <a href="/terms" className="text-blue-600" />, 9 }} 10 /> 11 ); 12} 13 14function WelcomeMessage({ name }) { 15 return ( 16 <Trans 17 i18nKey="welcome" 18 values={{ name }} 19 components={{ 20 bold: <strong className="font-bold" />, 21 }} 22 /> 23 ); 24}
HTML Imbriqué
JSON{ "description": "Découvrez nos <link>nouvelles fonctionnalités</link> et <button>commencez</button> aujourd'hui !" }
TSX1<Trans 2 i18nKey="description" 3 components={{ 4 link: <a href="/features" />, 5 button: <button onClick={handleStart} />, 6 }} 7/>
Chargement Paresseux des Traductions
Pour les grandes apps, chargez les traductions à la demande pour réduire la taille initiale du bundle.
Backend HTTP
TypeScript1import i18n from 'i18next'; 2import { initReactI18next } from 'react-i18next'; 3import HttpBackend from 'i18next-http-backend'; 4import LanguageDetector from 'i18next-browser-languagedetector'; 5 6i18n 7 .use(HttpBackend) 8 .use(LanguageDetector) 9 .use(initReactI18next) 10 .init({ 11 fallbackLng: 'en', 12 ns: ['common'], 13 defaultNS: 'common', 14 15 backend: { 16 loadPath: '/locales/{{lng}}/{{ns}}.json', 17 }, 18 19 react: { 20 useSuspense: true, 21 }, 22 }); 23 24export default i18n;
Avec Suspense
TSX1import { Suspense } from 'react'; 2 3function App() { 4 return ( 5 <Suspense fallback={<LoadingSpinner />}> 6 <MainContent /> 7 </Suspense> 8 ); 9}
Charger l'Espace de Noms à la Demande
TSX1import { useTranslation } from 'react-i18next'; 2import { Suspense, lazy } from 'react'; 3 4// Chargement paresseux du composant avec son espace de noms 5const SettingsPage = lazy(() => import('./SettingsPage')); 6 7function App() { 8 return ( 9 <Suspense fallback={<LoadingSpinner />}> 10 <SettingsPage /> 11 </Suspense> 12 ); 13} 14 15// SettingsPage.tsx 16function SettingsPage() { 17 const { t, ready } = useTranslation('settings', { useSuspense: false }); 18 19 if (!ready) return <LoadingSpinner />; 20 21 return <h1>{t('title')}</h1>; 22}
Intégration TypeScript
Traductions Type-Safe
TypeScript1// src/types/i18next.d.ts 2import 'i18next'; 3import common from '../locales/en/common.json'; 4import home from '../locales/en/home.json'; 5 6declare module 'i18next' { 7 interface CustomTypeOptions { 8 defaultNS: 'common'; 9 resources: { 10 common: typeof common; 11 home: typeof home; 12 }; 13 } 14}
Maintenant TypeScript autocomplétera les clés de traduction :
TSXconst { t } = useTranslation(); t('nav.home'); // ✅ Autocomplète t('nav.invalid'); // ❌ Erreur TypeScript
Avec Plusieurs Espaces de Noms
TSX1const { t } = useTranslation(['common', 'home']); 2 3t('buttons.submit'); // De common (défaut) 4t('home:hero.title'); // De l'espace de noms home
Rendu Côté Serveur (SSR)
Routeur Pages Next.js
TypeScript1// next-i18next.config.js 2module.exports = { 3 i18n: { 4 defaultLocale: 'en', 5 locales: ['en', 'es', 'fr'], 6 }, 7};
TSX1// pages/index.tsx 2import { serverSideTranslations } from 'next-i18next/serverSideTranslations'; 3import { useTranslation } from 'next-i18next'; 4 5export default function Home() { 6 const { t } = useTranslation('common'); 7 return <h1>{t('welcome')}</h1>; 8} 9 10export async function getStaticProps({ locale }) { 11 return { 12 props: { 13 ...(await serverSideTranslations(locale, ['common'])), 14 }, 15 }; 16}
Routeur App Next.js
Pour le Routeur App Next.js, nous recommandons d'utiliser next-intl à la place. Voir notre guide next-intl.
Remix
TSX1// app/entry.server.tsx 2import { createInstance } from 'i18next'; 3import { initReactI18next } from 'react-i18next'; 4 5export default async function handleRequest(request, ...) { 6 const i18n = createInstance(); 7 await i18n.use(initReactI18next).init({ 8 lng: locale, 9 resources: { [locale]: translations }, 10 }); 11 12 // Rendre avec instance i18n 13}
Tester avec react-i18next
Configuration Mock
TypeScript1// test/setup.ts 2import i18n from 'i18next'; 3import { initReactI18next } from 'react-i18next'; 4 5i18n.use(initReactI18next).init({ 6 lng: 'en', 7 fallbackLng: 'en', 8 resources: { 9 en: { 10 common: { 11 welcome: 'Welcome', 12 'buttons.submit': 'Submit', 13 }, 14 }, 15 }, 16}); 17 18export default i18n;
Tester les Composants
TSX1import { render, screen } from '@testing-library/react'; 2import { I18nextProvider } from 'react-i18next'; 3import i18n from '../test/setup'; 4import Header from './Header'; 5 6describe('Header', () => { 7 it('renders translated welcome message', () => { 8 render( 9 <I18nextProvider i18n={i18n}> 10 <Header /> 11 </I18nextProvider> 12 ); 13 14 expect(screen.getByText('Welcome')).toBeInTheDocument(); 15 }); 16});
Meilleures Pratiques de Production
1. Organisation par Espace de Noms
locales/
├── en/
│ ├── common.json # UI partagée (boutons, nav, erreurs)
│ ├── auth.json # Connexion, inscription, réinit mot de passe
│ ├── dashboard.json # Spécifique au tableau de bord
│ ├── settings.json # Page de paramètres
│ └── errors.json # Messages d'erreur
2. Convention de Nommage des Clés
JSON1{ 2 "page.section.element": "Valeur", 3 "auth.login.title": "Se connecter", 4 "auth.login.button": "Continuer", 5 "dashboard.stats.totalUsers": "Utilisateurs Totaux", 6 "errors.network.timeout": "Délai de requête dépassé" 7}
3. Gestion des Clés Manquantes
TypeScript1i18n.init({ 2 saveMissing: process.env.NODE_ENV === 'development', 3 missingKeyHandler: (lng, ns, key) => { 4 console.warn(`Missing translation: ${lng}/${ns}/${key}`); 5 // Signaler au suivi des erreurs 6 }, 7});
4. Optimisation de la Performance
TypeScript1// Précharger les espaces de noms critiques 2i18n.loadNamespaces(['common', 'auth']); 3 4// Ne pas re-rendre au changement de langue pour le contenu statique 5const { t } = useTranslation('common', { useSuspense: false });
Mise à l'Échelle avec IntlPull
À mesure que votre application grandit, gérer les fichiers JSON manuellement devient douloureux. IntlPull fournit :
Intégration CLI
Terminal1# Initialiser IntlPull dans votre projet 2npx @intlpullhq/cli init 3 4# Extraire les clés du code 5npx @intlpullhq/cli extract 6 7# Télécharger sur IntlPull 8npx @intlpullhq/cli upload 9 10# Télécharger les traductions 11npx @intlpullhq/cli download
Traduction IA
IntlPull traduit automatiquement vos clés en utilisant GPT-4, Claude ou DeepL tout en maintenant le contexte et la voix de la marque.
Mises à Jour OTA
Mettez à jour les traductions sans sorties d'application :
TypeScript1import { IntlPullBackend } from '@intlpull/i18next-backend'; 2 3i18n.use(IntlPullBackend).init({ 4 backend: { 5 projectId: 'your-project-id', 6 apiKey: process.env.INTLPULL_API_KEY, 7 }, 8});
Modèles Courants
Traductions Conditionnelles
JSON1{ 2 "greeting_morning": "Good morning!", 3 "greeting_afternoon": "Good afternoon!", 4 "greeting_evening": "Good evening!" 5}
TSX1function Greeting() { 2 const { t } = useTranslation(); 3 const hour = new Date().getHours(); 4 5 const getGreetingKey = () => { 6 if (hour < 12) return 'greeting_morning'; 7 if (hour < 18) return 'greeting_afternoon'; 8 return 'greeting_evening'; 9 }; 10 11 return <h1>{t(getGreetingKey())}</h1>; 12}
Clés Dynamiques
TSX1function StatusBadge({ status }: { status: 'pending' | 'approved' | 'rejected' }) { 2 const { t } = useTranslation(); 3 4 // Clés : status.pending, status.approved, status.rejected 5 return <span>{t(`status.${status}`)}</span>; 6}
Contenu de Repli
TSX1const { t } = useTranslation(); 2 3// Fournir un repli 4const title = t('page.title', 'Default Title'); 5 6// Vérifier si la clé existe 7const { exists } = useTranslation(); 8if (exists('optional.feature')) { 9 // Montrer le contenu spécifique à la fonctionnalité 10}
Dépannage
Avertissements "Translation not found"
TypeScript1// Vérifier si l'espace de noms est chargé 2i18n.hasLoadedNamespace('myNamespace'); 3 4// S'assurer que la clé existe 5i18n.exists('myKey'); 6 7// Mode débogage 8i18n.init({ debug: true });
Composant ne se re-rendant pas au changement de langue
TSX1// Utiliser le hook correctement - il s'abonne aux changements 2const { t, i18n } = useTranslation(); 3 4// Forcer le re-rendu avec la clé 5<MyComponent key={i18n.language} />
Inadéquation d'hydratation SSR
Assurez-vous que l'état initial correspond au serveur. Passez la locale initiale et les messages à votre provider pour éviter les différences.
Conclusion
react-i18next est un outil puissant pour construire des applications React mondiales. En suivant ces meilleures pratiques et en utilisant des outils comme IntlPull, vous pouvez créer des expériences localisées sans faille.
Prêt à commencer ? Configurez IntlPull gratuitement et gérez vos traductions react-i18next avec facilité.
