wlib / i18n
Brings internationalization tools to your PHP project.
Requires
- php: >=7.1.0
- pomo/pomo: ^1.5
README
Paquet d'internationalisation (i18n) pour les applications PHP, basé sur la librairie pomo/pomo utilisée par WordPress.
Ce paquet simplifie la gestion des traductions dans vos applications en proposant une API intuitive et des fonctions inspirées de WordPress, tout en s'intégrant parfaitement à l'écosystème wlib.
🚀 Besoin d'un cadre prêt à l'emploi pour vos applications multilingues ? Installez sans plus attendre wlib/skeleton qui vous propose une structure de départ clé en main pour démarrer votre prochain projet.
Sommaire
- Installation
- Concepts clés
- Configuration rapide
- Utilisation
- Structure recommandée des fichiers
- Bonnes pratiques
- Intégration avec les frameworks
- Génération des fichiers .po
- Dépannage
- API avancée
- Exemple complet avec architecture MVC
- Contribution
- Licence
Installation
composer require wlib/i18n
Prérequis :
- PHP 7.4
- Extension PHP
gettextrecommandée (mais non obligatoire)
Concepts clés
Domaines de traduction
Un domaine permet d'organiser vos traductions par contexte fonctionnel. Par défaut, le domaine default est utilisé. Vous pouvez créer des domaines séparés pour :
- Les messages de l'application (
app) - Les messages d'erreur (
errors) - Les labels de formulaires (
forms) - etc.
Fichiers de traduction
Les traductions sont stockées dans des fichiers au format Gettext :
.po: fichier source editable (Portable Object).mo: fichier binaire compilé (Machine Object)
Configuration rapide
1. Initialisation du traducteur
use wlib\I18n\Translator; // Création de l'instance $translator = new Translator(); // Chargement des fichiers de traduction $translator->addTranslationsFile('/chemin/vers/translations/fr_FR.po', 'default'); $translator->addTranslationsFile('/chemin/vers/translations/en_US.po', 'default');
2. Avec wlib/dibox (recommandé)
use wlib\DiBox\DiBox; use wlib\I18n\Translator; // Enregistrement dans le conteneur DI DiBox::getInstance()->register(Translator::class, function() { $translator = new Translator(); $translator->addTranslationsFile(__DIR__.'/translations/fr_FR.po', 'default'); return $translator; });
Utilisation
Fonctions helpers (style WordPress)
Les fonctions suivantes sont automatiquement disponibles après l'instanciation du Translator :
| Fonction | Description | Exemple |
|---|---|---|
__() |
Traduction simple | __('Bonjour') |
_n() |
Singulier/Pluriel | _n('%d article', '%d articles', $count) |
_x() |
Traduction avec contexte | _x('Post', 'type de contenu', 'post-type') |
_nx() |
Singulier/Pluriel avec contexte | _nx('%d article', '%d articles', $count, 'blog') |
_s() |
Traduction + sprintf | _s('Bonjour %s', 'Jean') |
_ns() |
Singulier/Pluriel + sprintf | _ns('%d article pour %s', '%d articles pour %s', $count, $author) |
Exemple complet
use wlib\I18n\Translator; // Initialisation $translator = new Translator(); $translator->addTranslationsFile(__DIR__.'/locales/fr_FR.po'); // Utilisation des helpers echo __('Bienvenue sur notre site'); // "Welcome to our site" en anglais echo _n('1 résultat trouvé', '%d résultats trouvés', 5); // "5 résultats trouvés" echo _x('Mois', 'unité de temps', 'time'); // "Month" avec contexte echo _s('Bonjour %s', 'Marie'); // "Bonjour Marie"
Structure recommandée des fichiers
votre-projet/
├── locales/
│ ├── fr_FR/
│ │ ├── default.po
│ │ ├── default.mo
│ │ ├── errors.po
│ │ └── errors.mo
│ └── en_US/
│ ├── default.po
│ └── default.mo
└── ...
Bonnes pratiques
1. Organisation des traductions
- Un fichier par domaine : Séparez vos traductions par fonctionnalité
- Nommage des domaines : Utilisez des noms courts et explicites (
auth,validation,admin) - Hiérarchie des langues : Utilisez le format
langue_REGION(ex:fr_FR,en_US)
2. Dans votre code
// ✅ Bon - Chaînes traduisibles extraites echo __('Nom d\'utilisateur'); echo _n('1 élément', '%d éléments', $count); // ❌ À éviter - Concénation avant traduction echo __('Nom' . ' ' . 'd\'utilisateur'); // Ne sera pas traduit correctement // ✅ Bon - Avec contexte pour les homonymes echo _x('Post', 'article de blog', 'content'); echo _x('Post', 'méthode HTTP', 'http');
3. Variables dans les traductions
Utilisez des placeholders numérotés pour les interpolations :
// Dans le code PHP echo _s('Bonjour %1$s, vous avez %2$d nouveaux messages', 'Jean', 5); // Dans le fichier .po msgid "Hello %1$s, you have %2$d new messages" msgstr "Bonjour %1$s, vous avez %2$d nouveaux messages"
Intégration avec les frameworks
Avec wlib/application
use wlib\Application\Application; use wlib\I18n\Translator; $app = new Application(__DIR__.'/config'); // Le Translator est automatiquement disponible via DI $translator = $app->getContainer()->get(Translator::class); $translator->addTranslationsFile(__DIR__.'/locales/fr_FR.po');
Middleware de détection de langue
use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Message\ResponseInterface; use Psr\Http\Server\MiddlewareInterface; use Psr\Http\Server\RequestHandlerInterface; use wlib\I18n\Translator; class LocaleMiddleware implements MiddlewareInterface { public function __construct( private Translator $translator, private string $defaultLocale = 'fr_FR' ) {} public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface { // Détection de la langue depuis la requête $locale = $request->getHeaderLine('Accept-Language') ?: $this->defaultLocale; // Chargement du fichier de traduction correspondant $this->translator->addTranslationsFile( __DIR__."/locales/{$locale}/default.po", 'default' ); return $handler->handle($request); } }
Génération des fichiers .po
Je recommande chaudement l'utilisation de Poedit :
- Téléchargez Poedit
- Créez un nouveau catalogue
- Configurez les chemins sources de votre projet
- Extrayez les chaînes traduisibles
- Traduisez et enregistrez
Dépannage
Les traductions n'apparaissent pas
- Vérifiez que le fichier .po est bien chargé :
$translator->addTranslationsFile('/chemin/correct/fr_FR.po', 'default');
- Assurez-vous que le
Translatorest instancié avant l'appel aux helpers - Vérifiez que le fichier .po est valide (utilisez Poedit pour le valider)
Problèmes de caractères spéciaux
- Enregistrez vos fichiers .po en UTF-8 sans BOM
- Assurez-vous que votre éditeur utilise bien UTF-8
Les fonctions helpers ne fonctionnent pas
Les fonctions helpers nécessitent que le Translator soit instancié au moins une fois avant leur utilisation. Assurez-vous que :
$translator = new Translator(); // Cette ligne doit être exécutée avant echo __('Ma traduction'); // Fonctionne
API avancée
Gestion multiple de fichiers par domaine
$translator = new Translator(); // Ajout de plusieurs fichiers pour le même domaine // (les traductions sont fusionnées) $translator->addTranslationsFile(__DIR__.'/locales/fr_FR/annulations.po', 'admin'); $translator->addTranslationsFile(__DIR__.'/locales/fr_FR/annulations-supplementaires.po', 'admin');
Utilisation sans helpers
$translator = new Translator(); $translator->addTranslationsFile(__DIR__.'/locales/fr_FR.po'); // Traduction directe $translated = $translator->translate('Hello world', 'default'); // Traduction plurielle directe $translated = $translator->translatePlural( 'One item', '%d items', 5, 'default' );
Exemple complet avec architecture MVC
Structure du projet
mon-application/
├── config/
│ └── translations.php
├── locales/
│ ├── fr_FR/
│ │ └── default.po
│ └── en_US/
│ └── default.po
├── src/
│ ├── Controllers/
│ │ └── HomeController.php
│ └── bootstrap.php
└── public/
└── index.php
Fichier de configuration (config/translations.php)
<?php return [ 'default_locale' => 'fr_FR', 'fallback_locale' => 'en_US', 'locales_dir' => __DIR__ . '/../locales', 'domains' => [ 'default' => ['fr_FR', 'en_US'], 'admin' => ['fr_FR'], ], ];
Initialisation (src/bootstrap.php)
<?php use wlib\DiBox\DiBox; use wlib\I18n\Translator; $config = require __DIR__.'/../config/translations.php'; DiBox::getInstance()->register(Translator::class, function() use ($config) { $translator = new Translator(); foreach ($config['domains'] as $domain => $locales) { foreach ($locales as $locale) { $file = sprintf( '%s/%s/%s.po', $config['locales_dir'], $locale, $domain ); if (is_file($file)) { $translator->addTranslationsFile($file, $domain); } } } return $translator; });
Utilisation dans un contrôleur
<?php namespace App\Controllers; use wlib\Application\Controllers\Controller; use wlib\I18n\Translator; class HomeController extends Controller { public function __construct(private Translator $translator) { // Le Translator est injecté automatiquement par DI } public function index() { $title = __('Bienvenue sur notre application'); $description = _s( 'Il y a actuellement %d utilisateurs enregistrés.', $this->userRepository->count() ); return $this->render('home.index', [ 'title' => $title, 'description' => $description, ]); } }
Contribution
Les contributions sont les bienvenues ! Pour contribuer :
- Fork le projet
- Créez une branche pour votre fonctionnalité (
git checkout -b feature/nouvelle-fonctionnalité) - Committez vos changements (
git commit -m 'feat(i18n): ajoute telle fonctionnalité') - Poussez vers la branche (
git push origin feature/nouvelle-fonctionnalité) - Ouvrez une Pull Request
📜 Licence
Ce package est distribué sous la licence CeCILL 2.1, une licence open source française compatible avec la GPL.
CeCILL (CEA CNRS INRIA Logiciel Libre) est une licence qui garantit la liberté d'utiliser, modifier et redistribuer le logiciel.
Pour plus d'informations : http://www.cecill.info