React
Next.js
Internacionalização
i18n
Frontend

Bonnes pratiques d'internationalisation (i18n) dans React et Next.js en 2025

Bonnes pratiques d'internationalisation (i18n) dans React et Next.js en 2025

Dans un monde numérique de plus en plus connecté, développer des applications Web destinées aux utilisateurs du monde entier n’est plus un facteur de différenciation, mais une nécessité. L'internationalisation (i18n) est devenue un élément essentiel du développement d'applications modernes, permettant à votre produit d'atteindre des publics divers, quelles que soient la langue, la région ou les préférences culturelles.

Dans cet article, nous explorerons les meilleures pratiques, outils et stratégies pour mettre en œuvre une internationalisation efficace dans les applications React et Next.js en 2025, en fournissant des exemples pratiques et des solutions aux défis courants.

Fondamentaux de l'internationalisation

Qu'est-ce qu'i18n et pourquoi est-ce important ?

Le terme « i18n » est une abréviation de « internationalisation » (la lettre « i » suivie de 18 lettres et se terminant par « n »). Il s'agit du processus de conception et de développement d'applications logicielles qui peuvent être adaptées à différentes langues et régions sans modifications d'ingénierie ou de code.

En 2025, l’importance de l’i18n est amplifiée par plusieurs facteurs :

  1. Portée mondiale : les applications avec support multilingue peuvent atteindre les marchés internationaux et élargir leur base d'utilisateurs.
  2. Expérience utilisateur : les utilisateurs se sentent plus à l'aise et engagés lorsqu'ils interagissent avec des applications dans leur langue maternelle.
  3. Conformité réglementaire : de nombreux pays ont des exigences légales pour que les systèmes numériques prennent en charge les langues locales.
  4. Avantage concurrentiel : les applications bien internationalisées se démarquent sur les marchés mondiaux concurrentiels.

Différence entre i18n, l10n et g11n

Il est important de comprendre la distinction entre les termes fréquemment utilisés dans ce domaine :

  • Internationalisation (i18n) : Processus de conception et de développement d'un produit afin qu'il puisse être adapté à différentes langues et régions.
  • Localisation (l10n) : Processus d'adaptation d'un produit internationalisé à un emplacement ou un marché spécifique, y compris la traduction de textes et l'adaptation d'éléments culturels.
  • Globalisation (g11n) : stratégie commerciale qui couvre à la fois i18n et l10n, prenant en compte tous les aspects de la commercialisation d'un produit sur les marchés mondiaux.

Bibliothèques et outils modernes pour i18n dans React

Écosystème i18n en 2025

L’écosystème d’internationalisation de React a considérablement évolué ces dernières années. Voici les bibliothèques les plus populaires et les plus avancées en 2025 :

1. React-i18next

Le react-i18next continue d'être l'une des solutions les plus robustes et les plus populaires, évoluant pour répondre aux besoins modernes :

// Configuração básica do react-i18next em 2025 import i18n from 'i18next'; import { initReactI18next, useTranslation } from 'react-i18next'; import LanguageDetector from 'i18next-browser-languagedetector'; import Backend from 'i18next-http-backend'; i18n // Carregamento sob demanda de traduções .use(Backend) // Detecção automática de idioma .use(LanguageDetector) // Integração com React .use(initReactI18next) .init({ fallbackLng: 'pt-BR', supportedLngs: ['pt-BR', 'en-US', 'es', 'fr', 'zh-CN'], // Novo em 2025: Detecção avançada de idioma com preferências de usuário detection: { order: ['localStorage', 'navigator', 'querystring', 'cookie'], caches: ['localStorage'], cookieExpirationDate: new Date(Date.now() + 1000 * 60 * 60 * 24 * 365), lookupQuerystring: 'lng', lookupCookie: 'i18n', }, // Novo em 2025: Cache inteligente com estratégia adaptativa backend: { loadPath: '/locales//.json', requestOptions: { cache: 'smart-default', // Nova estratégia de cache adaptativa }, }, interpolation: { escapeValue: false, // React já escapa por padrão format: function(value, format, lng) { // Novo suporte para formatação avançada if (format === 'uppercase') return value.toUpperCase(); if (format === 'currency') return new Intl.NumberFormat(lng, { style: 'currency', currency: 'BRL' }).format(value); return value; } }, // Novo em 2025: Análise de uso de traduções para otimização telemetry: { enabled: process.env.NODE_ENV === 'development', endpoint: '/api/i18n-telemetry', sampleRate: 0.1 } }); export default i18n;

2. Format ICU avec réagir-intl

Le format ICU (International Components for Unicode) est devenu le standard prédominant pour le traitement des textes internationalisés :

// Exemplo usando react-intl com sintaxe ICU moderna import React from 'react'; import { FormattedMessage, useIntl } from 'react-intl'; function ProductDetails({ product, inventory, lastUpdated }) { const intl = useIntl(); return ( <div className="product-card"> <h2>{product.name}</h2> {/* Pluralização avançada */} <FormattedMessage id="product.inventory" defaultMessage="{inventory, plural, =0 {Fora de estoque} one {Última unidade disponível!} other {# unidades em estoque}}" values= /> {/* Formatação de data relativa */} <FormattedMessage id="product.lastUpdated" defaultMessage="Atualizado {lastUpdated, relativeTime, style=long}" values= /> {/* Formatação de valores variáveis dependentes de idioma */} <p> {intl.formatMessage( { id: 'product.price', defaultMessage: 'Preço: {price, number, currency}' }, { price: product.price } )} </p> {/* Formatação condicional com seleção */} <FormattedMessage id="product.status" defaultMessage="{status, select, new {Novo} sale {Promoção} limited {Edição limitada} other {Regular}}" values= /> </div> ); }

3. LinguiJS : Une alternative puissante

LinguiJS a gagné en popularité pour sa simplicité et ses performances :

// Exemplo com Lingui v5 (versão 2025) import React from 'react'; import { Trans, Plural, t } from '@lingui/macro'; function ShoppingCart({ items, totalPrice, lastUpdated }) { return ( <div className="shopping-cart"> <h2><Trans id="cart.title">Seu Carrinho</Trans></h2> <Plural value={items.length} zero={<Trans id="cart.empty">Seu carrinho está vazio</Trans>} one={<Trans id="cart.oneItem">1 item no carrinho</Trans>} other={<Trans id="cart.items">{items.length} itens no carrinho</Trans>} /> {items.map(item => ( <div key={item.id} className="cart-item"> <span>{item.name}</span> <span>{t({ id: 'cart.item.price', message: 'Preço: {price, number, currency}', values: { price: item.price } })}</span> </div> ))} <div className="cart-footer"> <div className="total"> <Trans id="cart.total" values=> Total: {totalPrice, number, currency} </Trans> </div> <div className="updated"> <Trans id="cart.updated" values=> Atualizado em {lastUpdated, date, long} </Trans> </div> </div> </div> ); }

Implémentation d'i18n dans Next.js

Next.js s'est imposé comme l'un des frameworks React les plus populaires, et ses capacités d'internationalisation ont considérablement évolué en 2025.

Configuration i18n moderne dans Next.js

L'approche intégrée de Next.js pour i18n est devenue plus robuste :

// next.config.js em 2025 /** @type {import('next').NextConfig} */ const nextConfig = { i18n: { // Idiomas suportados locales: ['pt-BR', 'en-US', 'es', 'fr', 'zh-CN'], // Idioma padrão defaultLocale: 'pt-BR', // Novos recursos em 2025 localeDetection: true, // Detecção automática de idioma automaticLocalePrefix: true, // Novo em 2025 - prefixos de URL simplificados // Domínios específicos por idioma (para SEO otimizado) domains: [ { domain: 'meuapp.com.br', defaultLocale: 'pt-BR', }, { domain: 'myapp.com', defaultLocale: 'en-US', }, { domain: 'miapp.es', defaultLocale: 'es', }, ], // Novo em 2025: estratégia de fallback para conteúdo parcialmente traduzido fallbackStrategy: 'partial', // Padrões de URL que não devem ser traduzidos excludeFromTranslation: [ '/api/*', '/admin/*', '/static/*', ], }, // Outras configurações do Next.js }; module.exports = nextConfig;

Utilisation des traductions dans les composants serveur et client

Next.js App Router a introduit une séparation claire entre les composants serveur et client, ce qui nécessite des approches spécifiques à i18n :

// Exemplo para componentes Server no Next.js App Router // Em /app/[lang]/layout.tsx import { Locale } from '@/i18n/config'; import { getTranslations } from '@/i18n/server'; export default async function RootLayout({ children, params: { lang } }: { children: React.ReactNode; params: { lang: Locale }; }) { // Obter traduções no servidor const { t } = await getTranslations(lang, 'common'); return ( <html lang={lang}> <body> <header> <h1>{t('site.title')}</h1> <nav> <ul> <li>{t('nav.home')}</li> <li>{t('nav.products')}</li> <li>{t('nav.contact')}</li> </ul> </nav> </header> <main>{children}</main> <footer>{t('site.footer')}</footer> </body> </html> ); } // Para componentes Client 'use client'; import { useTranslation } from '@/i18n/client'; import { useParams } from 'next/navigation'; export default function LanguageSwitcher() { const params = useParams(); const lang = params.lang as Locale; const { t } = useTranslation(lang, 'common'); return ( <div className="language-selector"> <p>{t('language.select')}</p> <select> <option value="pt-BR">{t('language.portuguese')}</option> <option value="en-US">{t('language.english')}</option> <option value="es">{t('language.spanish')}</option> </select> </div> ); }

Mise en place d'une solution complète

Pour montrer comment tout cela se déroule, voici une implémentation plus complète d'App Router de Next.js :

// Em /i18n/config.ts export const defaultLocale = 'pt-BR'; export const locales = ['pt-BR', 'en-US', 'es', 'fr', 'zh-CN'] as const; export type Locale = typeof locales[number]; // Em /i18n/server.ts import { createInstance } from 'i18next'; import resourcesToBackend from 'i18next-resources-to-backend'; import { initReactI18next } from 'react-i18next/initReactI18next'; import { Locale, defaultLocale } from './config'; export async function getTranslations(locale: Locale, namespace: string) { const i18nInstance = createInstance(); await i18nInstance .use(initReactI18next) .use(resourcesToBackend((language: string, ns: string) => import(`./locales/${language}/${ns}.json`))) .init({ lng: locale, fallbackLng: defaultLocale, supportedLngs: locales, defaultNS: 'common', ns: namespace, fallbackNS: 'common', }); return { t: i18nInstance.getFixedT(locale, namespace), i18n: i18nInstance }; } // Em /middleware.ts - para roteamento e redirecionamento baseado em idioma import { NextRequest, NextResponse } from 'next/server'; import { match as matchLocale } from '@formatjs/intl-localematcher'; import Negotiator from 'negotiator'; import { locales, defaultLocale } from './i18n/config'; function getLocale(request: NextRequest): string { // Simulando cabeçalhos para o negotiator const headers = { 'accept-language': request.headers.get('accept-language') || defaultLocale }; const languages = new Negotiator({ headers }).languages(); // Usar @formatjs/intl-localematcher para escolher o melhor idioma const locale = matchLocale(languages, locales, defaultLocale); return locale; } export function middleware(request: NextRequest) { const pathname = request.nextUrl.pathname; // Verificar se a URL já inclui uma localidade const pathnameHasLocale = locales.some( locale => pathname.startsWith(`/${locale}/`) || pathname === `/${locale}` ); if (pathnameHasLocale) return NextResponse.next(); // Redirecionar se a localidade não estiver na URL const locale = getLocale(request); const newUrl = new URL(`/${locale}${pathname}`, request.url); return NextResponse.redirect(newUrl); } export const config = { matcher: [ // Excluir arquivos estáticos e API '/((?!api|_next/static|_next/image|favicon.ico).*)', ], };

Décisions architecturales qui comptent

Au-delà des bibliothèques, bien s’internationaliser est avant tout une décision architecturale. Trois choix définissent la pérennité de l'opération multilingue dans le temps.

La première consiste à séparer le contenu du code dès le début. Les chaînes ne doivent jamais être intégrées dans des composants ; ils appartiennent à des fichiers de traduction versionnés, organisés par espace de noms, afin que les traducteurs et les développeurs travaillent sans empiéter sur le territoire des autres. La seconde consiste à traiter la pluralisation, le genre et le formatage des nombres, des dates et des devises comme la responsabilité de la bibliothèque i18n, et non comme des conditions réparties dans le code, car ces règles varient d'une langue à l'autre d'une manière qui ne peut pas être résolue par la concaténation de chaînes. La troisième consiste à définir, au niveau du routage, une stratégie claire pour la détection de la langue, les préfixes d'URL et le repli pour le contenu partiellement traduit, garantissant que chaque utilisateur arrive dans la bonne langue sans pages cassées en cours de route.

Dans l'App Router de Next.js, cela signifie décider consciemment de ce qui est traduit sur le serveur et de ce qui est livré au client, tout en conservant autant de traduction sur le serveur pour réduire le JavaScript envoyé au navigateur.

Conclusion

L’internationalisation n’est plus une fonctionnalité facultative mais fait désormais partie du fondement de tout produit ayant une ambition mondiale. Dans React et Next.js, l'écosystème 2025 propose des outils matures, react-i18next, react-intl avec ICU et LinguiJS, capables de tout couvrir, de la pluralisation avancée au formatage sensible aux paramètres régionaux.

La différence réside cependant rarement dans la bibliothèque choisie, mais dans la discipline de l'architecture : séparer le contenu du code, déléguer les règles linguistiques à ceux qui savent les gérer et concevoir le routage linguistique en tant que citoyen de premier ordre. Les équipes qui traitent i18n comme une décision stratégique dès le premier engagement atteignent de nouveaux marchés plus rapidement et avec moins de retouches que celles qui le laissent plus tard.

A lire aussi