Pular para o conteúdo principal

Internationalisation (i18n)

Cette page documente le support multilingue de l'application PCH-SIG.

Vue d'ensemble​

PCH-SIG supporte plusieurs langues pour s'adapter aux utilisateurs de differentes regions :

LangueCodeStatut
FrancaisfrPrincipal
PortugaisptComplet
AnglaisenComplet

Le kriol (créole de Guinée-Bissau) n'est pas une langue de l'interface : c'est une langue de l'assistant Bemba seulement, avec son propre réglage. Son ancien dictionnaire d'interface a été retiré le 12 octobre 2026. Un navigateur réglé en créole reçoit l'interface en portugais.

Architecture​

Frontend (React)​

src/
├── i18n/
│ ├── index.ts # Configuration i18next
│ └── locales/
│ ├── fr.json # Francais
│ ├── pt.json # Portugais
│ ├── en.json # Anglais

Backend (Symfony)​

translations/
├── messages.fr.yaml
├── messages.pt.yaml
├── messages.en.yaml
├── api_messages.{en,pt}.yaml # messages des réponses de l'API
└── vbg_analytics.{fr,en,pt}.yaml

Frontend​

Configuration i18next​

Fichier : frontend/src/i18n/index.ts.

RéglageValeur
Languesfr, pt, en (LANGUES_SUPPORTEES) ; repli sur fr
Détectionchoix enregistré dans localStorage (clé i18nextLng), sinon langue du navigateur
Variantespt-BR donne pt ; un navigateur en créole (kea, pov) donne pt

Le client API envoie la langue courante dans l'en-tête Accept-Language de chaque requête.

Structure des fichiers de traduction​

// fr.json
{
"common": {
"save": "Enregistrer",
"cancel": "Annuler",
"delete": "Supprimer",
"edit": "Modifier",
"loading": "Chargement..."
},
"menu": {
"dashboard": "Tableau de bord",
"menages": "Menages",
"beneficiaires": "Beneficiaires",
"paiements": "Paiements",
"plaintes": "Plaintes"
},
"menages": {
"title": "Liste des menages",
"create": "Nouveau menage",
"fields": {
"code": "Code menage",
"tailleMenage": "Taille du menage",
"region": "Region"
}
}
}

Utilisation dans les composants​

import { useTranslation } from 'react-i18next';

function MenagesList() {
const { t } = useTranslation();

return (
<div>
<h1>{t('menages.title')}</h1>
<button>{t('menages.create')}</button>
<table>
<thead>
<tr>
<th>{t('menages.fields.code')}</th>
<th>{t('menages.fields.tailleMenage')}</th>
</tr>
</thead>
</table>
</div>
);
}

Changement de langue​

i18n.changeLanguage(lang) suffit : le détecteur enregistre le choix dans localStorage (clé i18nextLng). Ne pas écrire une autre clé à la main.

Backend​

Configuration Symfony​

# config/packages/translation.yaml
framework:
default_locale: fr
translator:
default_path: '%kernel.project_dir%/translations'
fallbacks:
- fr

Fichiers de traduction​

# translations/messages.fr.yaml
menage:
created: "Menage cree avec succes"
updated: "Menage mis a jour"
deleted: "Menage supprime"

error:
not_found: "Element non trouve"
validation: "Erreur de validation"

Utilisation dans les controleurs​

use Symfony\Contracts\Translation\TranslatorInterface;

class MenageController extends AbstractController
{
public function __construct(
private TranslatorInterface $translator
) {}

public function create(): JsonResponse
{
// ...
return $this->json([
'success' => true,
'message' => $this->translator->trans('menage.created')
]);
}
}

Detection de la langue​

backend/src/EventListener/RequestLocaleListener.php fixe la langue de chaque requête, par ordre de priorité :

  1. paramètre ?locale= ;
  2. en-tête X-Locale ;
  3. en-tête Accept-Language (envoyé par le frontend) ;
  4. fr par défaut.

Seules fr, en et pt sont acceptées.

Documentation (Docusaurus)​

Configuration i18n​

// docusaurus.config.js
module.exports = {
i18n: {
defaultLocale: 'fr',
locales: ['fr', 'en'],
localeConfigs: {
fr: {
label: 'Francais',
},
en: {
label: 'English',
},
},
},
};

Structure des fichiers​

documentation/
├── docs/ # Francais (defaut)
│ └── guide/
│ └── introduction.md
└── i18n/
└── en/
└── docusaurus-plugin-content-docs/
└── current/
└── guide/
└── introduction.md

Bonnes pratiques​

Institution des cles​

{
"module": {
"action": {
"label": "Texte"
}
}
}

Exemple :

{
"menages": {
"create": {
"title": "Creer un menage",
"submit": "Enregistrer"
}
}
}

Pluralisation​

{
"menages": {
"count": "{{count}} menage",
"count_plural": "{{count}} menages"
}
}
t('menages.count', { count: 5 }) // "5 menages"

Variables​

{
"welcome": "Bienvenue, {{name}}!"
}
t('welcome', { name: user.nom }) // "Bienvenue, John!"

Workflow de traduction​

1. Ajouter une nouvelle cle​

  1. Ajoutez la cle dans fr.json (langue principale)
  2. Copiez dans les autres fichiers de langue
  3. Traduisez chaque fichier

2. Scripts d'aide​

# Inventaire : clés manquantes, textes en dur, clés orphelines (depuis la racine du dépôt)
node frontend/scripts/i18n-audit.js . --json audit-i18n.json

# Clés construites dynamiquement (préfixe + variable)
node frontend/scripts/i18n-dynamic-check.js .

Outils recommandes​

OutilUsage
i18next-parserExtraction automatique des cles
BabelEditEdition visuelle JSON
CrowdinPlateforme de traduction collaborative

Checklist nouvelle langue​

  • Creer le fichier de traduction frontend
  • Creer le fichier de traduction backend
  • Ajouter le code dans LANGUES_SUPPORTEES (frontend) et dans RequestLocaleListener::LOCALES (backend)
  • Ajouter l'option dans le selecteur de langue
  • Mettre a jour la documentation
  • Tester l'affichage RTL si necessaire
  • Verifier les formats de date/nombre

Formats regionaux​

Dates​

// Utiliser date-fns avec locale
import { format } from 'date-fns';
import { fr, pt, enUS } from 'date-fns/locale';

const locales = { fr, pt, en: enUS };

format(new Date(), 'PPP', {
locale: locales[i18n.language]
});

Nombres​

new Intl.NumberFormat(i18n.language, {
style: 'currency',
currency: 'XOF'
}).format(15000);
// "15 000 XOF" (fr)
// "15,000 XOF" (en)