Documentation i18next Documentation react-i18next Documentation Intl
SmartCommon intègre i18next pour gérer les traductions de votre application.
Placez vos fichiers JSON dans public/locales/ :
public/
└── locales/
├── en.json
└── fr.json
// 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"
}
}
// appConfig.js
export const config = {
// Stocker la langue dans l'état global
globalState: {
reducers: {
settings: { lng: "fr" },
},
},
// Persister les settings
storage: {
local: ["settings"],
},
};
SmartCommon expose le hook useIntl qui simplifie l'utilisation des traductions :
import { useIntl } from '@cap-rel/smartcommon';
const MyComponent = () => {
const { t, lng, setLng, formatDate, formatNumber } = useIntl();
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>
{/* Formatage de date */}
<p>{formatDate(new Date())}</p>
{/* Formatage de nombre */}
<p>{formatNumber(1234.56)}</p>
{/* Changer la langue */}
<button onClick={() => setLng('en')}>English</button>
<button onClick={() => setLng('fr')}>Français</button>
</div>
);
};
| Propriété | Type | Description |
|---|---|---|
t | function | Fonction de traduction |
lng | string | Langue actuelle |
setLng | function | Changer la langue |
formatDate | function | Formater une date selon la locale |
formatNumber | function | Formater un nombre selon la locale |
formatCurrency | function | Formater une devise |
// src/components/pages/public/LoginPage/index.jsx
import { useApi, useGlobalStates, useForm, useNavigation, useIntl } from '@cap-rel/smartcommon';
import { Form, Input, Button } from '@cap-rel/smartcommon';
import { z } from 'zod';
export const LoginPage = () => {
const { t } = useIntl();
const api = useApi();
const navigate = useNavigation();
const [, setSession] = useGlobalStates('session');
const schema = z.object({
email: z.string().email(t('login.error_email')),
password: z.string().min(1, t('login.error_password')),
});
const form = useForm({ schema });
const handleSubmit = async (data) => {
const response = await api.public.post('login', { json: data });
if (response.success) {
setSession(response.data);
navigate('/');
}
};
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.formState.isSubmitting}>
{t('login.submit')}
</Button>
</Form>
</div>
);
};
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>
);
};
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' },
]}
/>
);
};
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>
);
};
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>
);
};
// fr.json
{
"greeting": "Bonjour {{name}}, vous avez {{count}} messages"
}
// Utilisation
t('greeting', { name: 'Jean', count: 5 })
// "Bonjour Jean, vous avez 5 messages"
// 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"
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')
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>
);
};
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 };