Translations

Documentation i18next Documentation react-i18next Documentation Intl

SmartCommon integrates i18next to manage your application translations.

Quick Configuration

Translation Files

Place your JSON files in public/locales/:

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

File Structure

// 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"
  }
}

Provider Configuration

// appConfig.js

export const config = {
  // Store language in global state
  globalState: {
    reducers: {
      settings: { lng: "fr" },
    },
  },

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

Usage

useTranslation (react-i18next)

For translations, use useTranslation from react-i18next:

import { useTranslation } from 'react-i18next';

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

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

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

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

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

For date formatting, SmartCommon provides the useIntl hook:

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

const { DateTimeFormat } = useIntl();

// Format a timestamp with 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' }); // customize options

useTranslation Properties (react-i18next)

Property Type Description
t function Translation function
i18n object i18next instance (language, changeLanguage, etc.)

useIntl Properties (SmartCommon)

Property Type Description
DateTimeFormat function Format a date/time via Intl.DateTimeFormat

Complete Example: Login Page

// 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>
  );
};

Language Selector

Simple Component

import { useTranslation } from 'react-i18next';

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

  const languages = [
    { code: 'fr', label: 'Français' },
    { code: 'en', label: 'English' },
  ];

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

With SmartCommon Select

import { useTranslation } from 'react-i18next';
import { Select } from '@cap-rel/smartcommon';

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

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

Advanced Formatting

Dates with useIntl

useIntl returns only { DateTimeFormat } which uses Intl.DateTimeFormat internally.

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

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

  return (
    <div>
      {/* Default format: dd/MM/yyyy HH:mm:ss */}
      <p>{DateTimeFormat(date)}</p>

      {/* Custom format */}
      <p>{DateTimeFormat(date, null, { dateStyle: 'full' })}</p>
      <p>{DateTimeFormat(date, 'fr-FR', { dateStyle: 'long', timeStyle: 'short' })}</p>
    </div>
  );
};

Numbers and Currencies

For number and currency formatting, use the browser's Intl API directly:

const PriceDisplay = ({ amount }) => {
  const formatNumber = (n) => new Intl.NumberFormat(navigator.language).format(n);
  const formatCurrency = (n, currency) =>
    new Intl.NumberFormat(navigator.language, { style: 'currency', currency }).format(n);

  return (
    <div>
      {/* Simple number */}
      <p>{formatNumber(1234567.89)}</p>
      {/* fr: 1 234 567,89 */}

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

Interpolation and Plurals

Interpolation

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

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

Plurals

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

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

Advanced Organization

Namespaces

For large applications, organize by namespace:

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

Automatic Prefix

To avoid repeating the namespace:

import { useTranslation } from 'react-i18next';

const LoginPage = () => {
  // Use a prefix
  const { t } = useTranslation('login');

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

Manual Configuration (without SmartCommon)

If you're not using SmartCommon, here's the manual configuration:

// 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 };

See Also