Blog 18ways

Comment ajouter plusieurs langues à une application Next.js

Comment ajouter l’i18n multilingue et la localisation à une application Next.js existante sans nuire au SEO ni tout reconstruire de zéro.

I18n (internationalisation), l10n (localisation), support multilingue… peu importe la façon dont vous le dites, à un moment ou à un autre, vous devez mettre à jour votre application Next.js pour prendre en charge plus d’une langue.

La difficulté n’est pas de traduire une phrase. C’est d’ajouter plusieurs langues à une vraie application Next.js sans casser le SEO, sans embrouiller votre base de code, ni vous créer un problème de maintenance.

Si vous voulez passer directement au code, consultez les exemples GitHub 18ways-next.

Mettez d’abord votre infrastructure en place

Avant toute chose, nous devons installer nos paquets. Nous allons configurer les bibliothèques 18ways, mais des outils comme i18next fonctionnent bien si votre projet est simple et ne contient que du contenu statique.

bash
npm install @18ways/next @18ways/react

Créez votre fichier de configuration :

js
// 18ways.config.js
module.exports = {
  apiKey: 'pk_dummy_demo_token',
  baseLocale: 'en-GB',
  router: 'app', // 'app', or 'path' depending on which Next.js router you are using
};

Encapsulez votre configuration Next.js :

js
// next.config.js
const { withWays } = require('@18ways/next/config');
 
const nextConfig = {
  /*
   * your normal Next.js config here
   */ 
};
 
module.exports = withWays(nextConfig);

Ajoutez un proxy racine pour que / puisse rediriger vers la bonne locale :

js
// proxy.js
export { default, config } from '@18ways/next/proxy';

Ajoutez ensuite une mise en page localisée :

jsx
// app/layout.jsx
import './styles.css';
import { WaysRoot } from '@18ways/next/server';
 
export default function RootLayout({ children }) {
  return (
    <html lang="en">
      <body className="next-demo-body">
        <WaysRoot>{children}</WaysRoot>
      </body>
    </html>
  );
}

Et voilà, c’est terminé ! Vous avez maintenant :

Traduisez votre première page

Vous pouvez maintenant traduire votre première page. Des outils comme i18next vous demanderont d’extraire tout votre texte en clés de traduction, puis de les référencer dans votre code. La forme exacte que cela prendra dépendra de la bibliothèque que vous aurez choisie.

Si vous utilisez 18ways, vous pouvez simplement envelopper le texte que vous souhaitez traduire dans un composant <T> :

jsx
// src/components/MyExampleComponent.jsx
import { useT, T } from '@18ways/react';
 
export default function MyExampleComponent() {
  const t = useT();
 
  return (
    <div>
      <T>Hello world!</T>
      <img
        src="https://example.com/image.png"
        alt={t('Example image')}
      />
    </div>
  );
}

Vous voudrez probablement permettre aux utilisateurs de choisir leur langue :

jsx
// src/components/Footer.jsx
import { T, LanguageSwitcher } from '@18ways/react';
 
export default function Footer() {
  return (
    <footer>
      <T>My footer content</T>
      <LanguageSwitcher />
    </footer>
  );
}

Attention aux pièges

Une fois la plomberie en place, il y a plusieurs façons habituelles de faire dérailler ces projets.

Rendu côté serveur

Le rendu côté serveur (SSR) permet à votre application Next.js de pré-rendre le HTML côté serveur. C’est essentiel à la fois pour le SEO et pour éviter que l’utilisateur voie des contenus erronés ou une langue incorrecte au chargement.

Si vous utilisez une bibliothèque comme i18next, vous devez faire très attention à ce que vos traductions soient chargées et injectées pendant le rendu côté serveur. Vous pouvez le tester en vérifiant le code source de la page via view-source:, par exemple view-source:http://localhost:3000/. Vous devriez aussi vérifier cela en production, afin de vous assurer que cela fonctionne également dans votre version de production.

Si vous utilisez 18ways, ne vous inquiétez pas pour cela. Tout est pris en charge pour vous.

Clés de traduction

Si vous utilisez 18ways, vous n’avez pas du tout à vous en soucier. 18ways n’a pas besoin de clés de traduction, vous pouvez laisser votre texte en place, comme d’habitude.

De nombreux systèmes i18n exigent de découper votre code en clés de traduction. Il est important de faire attention à la façon dont vous nommez ces clés.

Les mauvaises clés sont vagues :

js
// bad-keys.en-GB.js
module.exports = {
  title: 'Continue',
  button: 'Pay now',
  label: 'Home',
};

Ces clés n’indiquent presque rien aux traducteurs et aux développeurs sur l’endroit où le texte apparaît.

Les meilleures clés incluent le contexte :

js
// better-keys.en-GB.js
module.exports = {
  'checkout.payment.primaryButton': 'Pay now',
  'checkout.payment.stepTitle': 'Complete your payment',
  'account.sidebar.homeLink': 'Home',
};

Évitez aussi de générer des clés dynamiquement :

js
// bad-dynamic-keys.js
const key = `checkout.${status}.${buttonType}`;
const translatedText = t(key);

Cela cassera les outils IDE qui essaient de rendre les clés de traduction moins pénibles. Cela rendra aussi extrêmement difficile la recherche et le nettoyage d’anciennes clés de traduction.

js
// better-dynamic-keys.js
const keyMap = {
  success: {
    primary: t('checkout.success.primary'),
    default: t('checkout.success.default'),
  },
  error: {
    primary: t('checkout.error.primary'),
    default: t('checkout.error.default'),
  },
};
 
const translatedText = keyMap[status][buttonType];

C’est moins DRY, mais c’est la meilleure façon d’éviter que les clés de traduction deviennent ingérables.

Mieux encore, utilisez un outil comme 18ways : vous n’aurez alors jamais besoin de clés de traduction :

jsx
// app/[lang]/checkout/page.jsx
<T>Pay now</T>

Détection de la langue

La détection de la langue consiste à décider quelle langue un utilisateur doit voir avant qu’il ne fasse lui-même son choix.

Cela implique généralement une combinaison de :

Dans un middleware Next.js classique, cela ressemble souvent à ceci :

js
// middleware.js
 
/**
 * You don't need any of this if you're using 18ways 
 */
 
import { NextResponse } from 'next/server';
 
const acceptedLocales = ['en-GB', 'fr-FR'];
 
export function middleware(request) {
  const savedLocale =
    request.cookies.get('preferred-locale')?.value;
  const browserLocale =
    request.headers
      .get('accept-language')
      ?.split(',')[0] || 'en-GB';
 
  const locale = acceptedLocales.includes(savedLocale)
    ? savedLocale
    : acceptedLocales.includes(browserLocale)
      ? browserLocale
      : 'en-GB';
 
  if (request.nextUrl.pathname === '/') {
    return NextResponse.redirect(
      new URL(`/${locale}`, request.url)
    );
  }
 
  return NextResponse.next();
}

Certaines bibliothèques proposent des assistants pour faciliter cela, à des degrés divers. Avec 18ways, la détection initiale et la couche de redirection sont gérées pour vous.

Dates et primitives spécifiques à la locale

Les différentes locales formatent différemment les dates, les nombres et l’argent.

Par exemple :

En JavaScript brut, vous gérez cela vous-même avec Intl :

js
// formatting-dates-and-currency.js
const myLocale = getCurrentLocale(); // depends on your lib
 
const someTimestamp = new Date('2026-04-13T09:00:00Z');
const dateLabel = new Intl.DateTimeFormat('fr-FR', {
  dateStyle: 'long',
}).format(someTimestamp);
 
const someMoney = {
  amount: 1999,
  currency: 'EUR',
};
const moneyLabel = new Intl.NumberFormat('de-DE', {
  style: 'currency',
  currency: someMoney.currency,
}).format(someMoney.amount);
 
const translatedText = t(
  'my.translation.key',
  { dateLabel, moneyLabel }
);

Si vous utilisez 18ways, c’est pris en charge pour vous :

jsx
const someTimestamp = new Date('2026-04-13T09:00:00Z');
const someMoney = {
  amount: 1999,
  currency: 'EUR',
};
 
<T>My text with {{ someTimestamp }} and {{ someMoney }}</T>

Assembler des chaînes

Ne construisez pas une interface traduite en assemblant des fragments :

jsx
// bad-string-joining.jsx
const clickHereText = t('click.here');
const toGetStartedText = t('to.get.started')
<p><a href="#">{clickHereText}</a> {toGetStartedText}.</p>

Cela cassera dans plusieurs langues.

Vous pourriez dire « 👉👉Cliquez ici👈👈 pour commencer » en anglais, mais en français il est plus naturel de dire « Pour commencer, 👉👉cliquez ici👈👈 ». Certaines langues comme le japonais auront même besoin de mots à la fois avant et après, comme « 始めるには👉👉こちら👈👈をクリックしてください ».

La structure de la phrase peut changer, donc la découper en morceaux rend une bonne traduction bien plus difficile.

Les phrases complètes se traduisent mieux, car les traducteurs peuvent réordonner naturellement les mots et voir le sens comme un tout.

Si vous traduisez du JSX comme ceci :

jsx
// rich-text-message.jsx
<p>
  Something <strong>that we want to be bold</strong>
</p>

vous devrez consulter la documentation de votre bibliothèque i18n pour savoir comment gérer cela, car la prise en charge varie beaucoup d’une bibliothèque à l’autre.

Si vous utilisez 18ways, vous pouvez simplement traduire tout le bloc JSX comme d’habitude.

jsx
// rich-text-message.jsx
<p>
  <T>Something <strong>that we want to be bold</strong></T>
</p>

Variables

Les variables vous permettent de conserver la phrase intacte tout en insérant des valeurs au moment de l’exécution.

La plupart des bibliothèques d’i18n prennent cela en charge d’une manière ou d’une autre. Avec 18ways :

jsx
// app/[lang]/page.jsx
<T>Hello {{ name: 'Ada' }}</T>

Ce modèle fonctionne bien pour :

La phrase reste lisible, et la valeur reste explicite.

Si vous devez aussi traduire la variable elle-même, veillez à l’envelopper dans un t(...) :

jsx
const animal = t('dog');
<T>Favourite animal: {{ animal }}</T>

Pluriels

Les règles de pluriel varient selon la langue. L’anglais n’a qu’une forme de pluriel (1 year, 2 years, etc.). Le polonais en a plusieurs (1 rok, 2 lata, 5 lat). Le japonais n’a pas du tout de forme plurielle (1 年, 2 年, 3 年).

La plupart des bibliothèques d’i18n vous permettent de spécifier une syntaxe de type ICU pour gérer les pluriels :

jsx
// app/[lang]/page.jsx
<T>
  {{
    unreadCount,
    format:
      'plural, =0{No unread messages} =1{One unread message} other{{unreadCount} unread messages}',
  }}
</T>

Ce qui précède fonctionnera dans 18ways, mais dans la plupart des cas, vous pouvez aussi simplement faire ceci :

jsx
// app/[lang]/page.jsx
<T>{{ unreadCount }} unread messages</T>

18ways gère les pluriels pour vous !

Commencez maintenant

Ajouter plusieurs langues à une application Next.js ne doit pas forcément devenir un projet de réécriture.

Si vous n’utilisez pas une solution comme 18ways, veillez à bien configurer votre routage et votre SSR, puis travaillez à déplacer votre contenu vers des clés de traduction, en évitant les pièges i18n courants.

Si vous utilisez 18ways, tout cela est pris en charge pour vous, et il vous suffit de commencer à envelopper votre texte dans des blocs <T> !

Comment ajouter plusieurs langues à une application Next.js | Blog 18ways