Rechercher

React i18next


9 Mai 2019 | 9 mins Marie Minasyan

React i18next

React i18next est une librairie qui permet de mettre en place l’internationalisation sur votre site internet. Cette librairie est basée sur i18next.

Initialisation

La première chose que nous allons faire est d’ajouter la librairie à notre projet :

npm install react-i18next --save

Ensuite, nous avons besoin de configurer la façon dont nous allons l’utiliser. Pour cela, je vais créer un fichier helpers/i18n.js :

import i18n from 'i18next';   
import { initReactI18next } from 'react-i18next';  

i18n  
  .use(initReactI18next)  
  .init({
    debug: true, // pratique pour voir les événements dans la console pendant le développement
    lng: 'en',
    fallbackLng: 'en',
    resources: {
      en: {
        translations: { // namespace par défaut, on peut avoir autant de namespaces que l'on souhaite
          'home.hello': 'Hello! Welcome to my app!'
        },
      },
    },
  });
  
export default i18n;

Et je vais l’importer dans index.js de mon application :

import React from  'react';
import ReactDOM from 'react-dom';
  
import App from './App';
import i18n from './helpers/i18n'; // initialisation de i18next

ReactDOM.render(
  <App />,
  document.getElementById("root")
);

Utilisation

Traduction simple

Voici le fichier App.js avec un exemple simple :

import React from 'react';
import { useTranslation } from 'react-i18next';

const App = () => {
  const { t } = useTranslation();

  return (
    {t('home.hello')}
  );
};

export default App;

Ici, nous utilisons le hook useTranslation afin d’accéder au service de traduction, mais vous pouvez également utiliser HOC withTranslation :

import React from 'react';
import { withTranslation } from 'react-i18next';

const App = ({ t }) => {
  return (
    {t('home.hello')}
  );
};

export default withTranslation()(App);

Si vous souhaitez passer des variables, cela peut également être fait très facilement. Admettons que notre clé de traduction ressemble à ceci :

translations: {
  'home.hello': 'Hello, ! Welcome to my app!'
},

Pour passer la variable name dans notre clé de traduction nous pouvons faire :

{t('home.hello'), { name: 'Astronaute' }}

HTML

Si vous devez mettre du HTML dans la traduction, ou si votre clé de traduction en contient, vous pouvez utiliser le composant Trans :

import React from 'react';
import { Trans } from 'react-i18next';

const App = () => {
  return (
    <Trans values={{ name: 'Astronaute' }}><h1>home.hello</h1></Trans>
  );
};

export default App;

Pluralisation

Bien sûr, nous avons également besoin de prévoir le cas où la traduction varie selon un nombre ou une quantité.

translations: {
  'message': 'You have one message',
  'message_plural': 'You have several messages',
},

Dans ce cas, nous allons spécifier un argument supplémentaire, count, comme ceci :

{t('home.hello'), { count: 5 }}
<Trans count={5}><h1>home.hello</h1></Trans>

Détection de la langue de l’utilisateur

Étant donné que nous travaillons sur un site multi-langues, nous souhaitons que la langue préférée de l’utilisateur soit automatiquement détectée. De façon générale, il s’agît de la langue du navigateur. Pour ceci, nous allons ajouter une dépendance :

npm install i18next-browser-languagedetector --save

Et nous allons modifier notre configuration comme ceci :

import i18n from 'i18next';   
import { initReactI18next } from 'react-i18next';
import LanguageDetector from 'i18next-browser-languagedetector'; // <= new

i18n
  .use(LanguageDetector) // <= new
  .use(initReactI18next)
  // ...
;
export default i18n;

Changer de langue

Maintenant que notre application est capable de détecter la langue du navigateur de l’utilisateur, ce dernier peut souhaiter la modifier. Pour ceci, ajoutons un bouton :

import React from 'react';
import { useTranslation } from 'react-i18next';
import Button from '@material-ui/core/Button';

const LanguageSwitcher = () => {
  const { i18n } = useTranslation();

  const changeLanguage = async (lng) => {
    await i18n.changeLanguage(lng); // i18n.changeLanguage retourne une Promise
  };

  return (
    <div>
      <Button onClick={() => changeLanguage('en')}>English</Button>
      <Button onClick={() => changeLanguage('fr')}>Français</Button>
    </div>
  );
}  

export default LanguageSwitcher;

Fichiers de traduction

Évidemment, nous allons vouloir mettre les traductions dans des fichiers dédiés à cela, plutôt que de les garder dans la configuration directement. Les fichiers de traduction sont de simples fichiers JSON. Nous pouvons imaginer la structure suivante dans notre projet :

public/
  locales/
    en/
      common.json
      translations.json
      other.json
    fr/
      common.json
      translations.json
      other.json

Namespaces

i18next fonctionne avec des namespaces, et on peut avoir plusieurs namespaces par langue. Pour rappel, le namespace par défaut est translations. Dans notre exemple, common, translations et other représentent des namespaces.

Dans ce cas, à chaque fois que nous voulons accéder aux clés qui se trouvent dans un namespace particulier, nous allons faire :

const { t } = useTranslation(['ns1',  'ns2',  'ns3']);
t('key'); // chargé depuis le namespace 'ns1'
t('ns2:key'); // chargé depuis le namespace 'ns2'

Nous pouvons également définir un namespace par défaut spécifique dans la configuration :

import i18n from 'i18next';   
import { initReactI18next } from 'react-i18next';
import LanguageDetector from 'i18next-browser-languagedetector';

i18n
  .use(LanguageDetector)
  .use(initReactI18next)
  .init({
    defaultNS: 'common', // <= new
    // ...
  });
;

export default i18n;

Chargement des fichiers locaux

Maintenant que les traductions sont dans des fichiers JSON séparés, nous avons besoin d’indiquer dans la configuration i18next comment les récupérer. Pour cela, nous allons utiliser i18next-xhr-backend :

npm install i18next-xhr-backend --save

Nous modifions le fichier de configuration comme ceci :

import i18n from 'i18next';   
import { initReactI18next } from 'react-i18next';
import LanguageDetector from 'i18next-browser-languagedetector';
import Backend from 'i18next-xhr-backend'; // <= new

i18n
  .use(LanguageDetector)
  .use(Backend) // <= new
  .use(initReactI18next)
  .init({
    backend: { // <= new
      loadPath: '/locales//.json'
    }
    // ...
  });
;

export default i18n;

Notez que le chemin indiqué dans loadPath est dans le dossier public à la racine de votre projet.

Fichiers hébergés sur un serveur distant

Nous avons présenté dans un article précédent que nous utilisons le service Localise.biz et enregistrons les fichiers de traduction sur un serveur dans le cloud. Ainsi, nous avons besoin de récupérer les fichiers hébergés sur un serveur distant :

import i18n from 'i18next';   
import { initReactI18next } from 'react-i18next';
import LanguageDetector from 'i18next-browser-languagedetector';
import Backend from 'i18next-xhr-backend';

i18n
  .use(LanguageDetector)
  .use(Backend)
  .use(initReactI18next)
  .init({
    debug: true,
    lng: 'en',
    fallbackLng: 'en',
    defaultNS: 'common',
    backend: {
      loadPath: `${process.env.TRANSLATIONS_ENDPOINT_URI}/..json`, // nous indiquons tout simplement une URL complète
    }
  });
;

export default i18n;

SSR

React i18next peut être configuré pour fonctionner avec les SSR. Voici la page qui explique comment mettre cela en place : https://react.i18next.com/latest/ssr

Néanmoins, nous avons eu un soucis en activant le SSR - le chargement des traductions depuis le serveur distant ne se faisait pas côté serveur. Ceci arrive parce que i18next-xhr-backend utilise fetch pour récupérer les fichiers, et fetch n’est pas disponible côté serveur.

Par conséquence, nous avons eu besoin d’écrire un backend custom en nous basant sur la documentation ici : https://www.i18next.com/misc/creating-own-plugins#backend. Nous avons utilisé la librairie cross-fetch qui fonctionne aussi bien côté client que côté serveur.

Auteur(s)

Marie Minasyan

Astronaute Raccoon @ *ElevenLabs*_🚀 De retour dans la Galaxie.

Utilisation hors-ligne disponible