Table of Contents

Traductions

Documentation i18next Documentation react-i18next Documentation Intl

SmartCommon intègre i18next pour gérer les traductions de votre application.

Configuration rapide

Fichiers de traduction

Placez vos fichiers JSON dans public/locales/ :

public/
└── locales/
    ├── en.json
    └── fr.json

Structure d'un fichier

// public/locales/fr.json

{
  "common": {
    "save": "Enregistrer",
    "cancel": "Annuler",
    "delete": "Supprimer",
    "loading": "Chargement..."
  },
  "login": {
    "title": "Connexion",
    "email": "Adresse email",
    "password": "Mot de passe",
    "submit": "Se connecter",
    "error": "Identifiants incorrects"
  },
  "home": {
    "welcome": "Bienvenue {{name}} !",
    "items_count": "{{count}} élément",
    "items_count_plural": "{{count}} éléments"
  }
}
// public/locales/en.json

{
  "common": {
    "save": "Save",
    "cancel": "Cancel",
    "delete": "Delete",
    "loading": "Loading..."
  },
  "login": {
    "title": "Login",
    "email": "Email address",
    "password": "Password",
    "submit": "Sign in",
    "error": "Invalid credentials"
  },
  "home": {
    "welcome": "Welcome {{name}}!",
    "items_count": "{{count}} item",
    "items_count_plural": "{{count}} items"
  }
}

Configuration du Provider

// appConfig.js

export const config = {
  // Stocker la langue dans l'état global
  globalState: {
    reducers: {
      settings: { lng: "fr" },
    },
  },

  // Persister les settings
  storage: {
    local: ["settings"],
  },
};

Utilisation avec useIntl

Hook useIntl

Pour les traductions, utilisez useTranslation de react-i18next :

import { useTranslation } from 'react-i18next';

const MyComponent = () => {
  const { t, i18n } = useTranslation();

  return (
    <div>
      <h1>{t('login.title')}</h1>

      {/* Avec interpolation */}
      <p>{t('home.welcome', { name: 'Jean' })}</p>

      {/* Pluriel automatique */}
      <p>{t('home.items_count', { count: 5 })}</p>

      {/* Changer la langue */}
      <button onClick={() => i18n.changeLanguage('en')}>English</button>
      <button onClick={() => i18n.changeLanguage('fr')}>Français</button>
    </div>
  );
};

Pour le formatage de dates, SmartCommon fournit le hook useIntl :

import { useIntl } from '@cap-rel/smartcommon';

const { DateTimeFormat } = useIntl();

// Formate un timestamp avec Intl.DateTimeFormat
DateTimeFormat(Date.now());                    // "22/02/2026, 14:30:00"
DateTimeFormat(Date.now(), 'en-US');           // "02/22/2026, 02:30:00 PM"
DateTimeFormat(Date.now(), null, { dateStyle: 'full' }); // personnalise les options

Propriétés de useTranslation (react-i18next)

Propriété Type Description
t function Fonction de traduction
i18n object Instance i18next (langue, changeLanguage, etc.)

Propriétés de useIntl (SmartCommon)

Propriété Type Description
DateTimeFormat function Formater une date/heure via Intl.DateTimeFormat

Exemple complet : Page de login

// src/components/pages/public/LoginPage/index.jsx

import { useApi, useGlobalStates, useForm, useNavigation } from '@cap-rel/smartcommon';
import { Form, Input, Button } from '@cap-rel/smartcommon';
import { useTranslation } from 'react-i18next';

export const LoginPage = () => {
  const { t } = useTranslation();
  const api = useApi();
  const nav = useNavigation();
  const gst = useGlobalStates();

  const form = useForm({ defaultValues: { email: '', password: '' } });

  const handleSubmit = async (data) => {
    try {
      const user = await api.login(data);
      gst.local.set('session', user);
      nav.navigate('/');
    } catch (error) {
      console.error('Login failed:', error);
    }
  };

  return (
    <div className="fixed inset-0 bg-white flex justify-center items-center p-10">
      <Form form={form} onSubmit={handleSubmit} className="flex flex-col gap-6 w-full max-w-sm">
        <h1 className="text-2xl font-bold text-center">
          {t('login.title')}
        </h1>

        <Input
          name="email"
          type="email"
          label={t('login.email')}
          placeholder={t('login.email_placeholder')}
        />

        <Input
          name="password"
          type="password"
          label={t('login.password')}
          placeholder="●●●●●●●●"
        />

        <Button type="submit" loading={form.isFormSubmitting}>
          {t('login.submit')}
        </Button>
      </Form>
    </div>
  );
};

Sélecteur de langue

Composant simple

import { useIntl } from '@cap-rel/smartcommon';

const LanguageSelector = () => {
  const { lng, setLng } = useIntl();

  const languages = [
    { code: 'fr', label: 'Français', flag: '🇫🇷' },
    { code: 'en', label: 'English', flag: '🇬🇧' },
  ];

  return (
    <div className="flex gap-2">
      {languages.map((lang) => (
        <button
          key={lang.code}
          onClick={() => setLng(lang.code)}
          className={`px-3 py-1 rounded ${
            lng === lang.code
              ? 'bg-primary text-white'
              : 'bg-gray-200'
          }`}
        >
          {lang.flag} {lang.label}
        </button>
      ))}
    </div>
  );
};

Avec Select SmartCommon

import { useIntl } from '@cap-rel/smartcommon';
import { Select } from '@cap-rel/smartcommon';

const LanguageSelect = () => {
  const { lng, setLng } = useIntl();

  return (
    <Select
      value={lng}
      onChange={(e) => setLng(e.target.value)}
      options={[
        { value: 'fr', label: 'Français' },
        { value: 'en', label: 'English' },
        { value: 'es', label: 'Español' },
      ]}
    />
  );
};

Formatage avancé

Dates

import { useIntl } from '@cap-rel/smartcommon';

const DateDisplay = ({ date }) => {
  const { formatDate } = useIntl();

  return (
    <div>
      {/* Format par défaut */}
      <p>{formatDate(date)}</p>

      {/* Format personnalisé */}
      <p>{formatDate(date, { dateStyle: 'full' })}</p>
      <p>{formatDate(date, { dateStyle: 'long', timeStyle: 'short' })}</p>
    </div>
  );
};

Nombres et devises

import { useIntl } from '@cap-rel/smartcommon';

const PriceDisplay = ({ amount }) => {
  const { formatNumber, formatCurrency } = useIntl();

  return (
    <div>
      {/* Nombre simple */}
      <p>{formatNumber(1234567.89)}</p>
      {/* fr: 1 234 567,89 */}
      {/* en: 1,234,567.89 */}

      {/* Devise */}
      <p>{formatCurrency(amount, 'EUR')}</p>
      {/* fr: 1 234,56 € */}
      {/* en: €1,234.56 */}
    </div>
  );
};

Interpolation et pluriels

Interpolation

// fr.json
{
  "greeting": "Bonjour {{name}}, vous avez {{count}} messages"
}

// Utilisation
t('greeting', { name: 'Jean', count: 5 })
// "Bonjour Jean, vous avez 5 messages"

Pluriels

// fr.json
{
  "item": "{{count}} article",
  "item_plural": "{{count}} articles",
  "item_zero": "Aucun article"
}

// Utilisation
t('item', { count: 0 })  // "Aucun article"
t('item', { count: 1 })  // "1 article"
t('item', { count: 5 })  // "5 articles"

Organisation avancée

Namespaces

Pour les grandes applications, organisez par namespace :

public/
└── locales/
    ├── fr/
    │   ├── common.json
    │   ├── login.json
    │   └── dashboard.json
    └── en/
        ├── common.json
        ├── login.json
        └── dashboard.json
// Utilisation avec namespace
t('login:title')
t('dashboard:stats.revenue')

Prefix automatique

Pour éviter de répéter le namespace :

import { useTranslation } from 'react-i18next';

const LoginPage = () => {
  // Utiliser un préfixe
  const { t } = useTranslation('login');

  return (
    <div>
      <h1>{t('title')}</h1>           {/* login.title */}
      <p>{t('description')}</p>        {/* login.description */}
    </div>
  );
};

Configuration manuelle (sans SmartCommon)

Si vous n'utilisez pas SmartCommon, voici la configuration manuelle :

// src/i18n/index.js

import i18n from "i18next";
import { initReactI18next } from "react-i18next";
import HttpBackend from "i18next-http-backend";

i18n
  .use(HttpBackend)
  .use(initReactI18next)
  .init({
    interpolation: {
      escapeValue: false,
    },
    backend: {
      loadPath: "/locales/{{lng}}.json",
    },
  });

export { i18n };

Voir aussi