wlib/i18n

Brings internationalization tools to your PHP project.

Maintainers

Package info

github.com/SamRay1024/wlib-i18n

pkg:composer/wlib/i18n

Transparency log

Statistics

Installs: 32

Dependents: 1

Suggesters: 0

Stars: 0

Open Issues: 0

v1.0.0 2024-02-24 22:46 UTC

This package is auto-updated.

Last update: 2026-07-05 17:10:51 UTC


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

composer require wlib/i18n

Prérequis :

  • PHP 7.4
  • Extension PHP gettext recommandé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 :

  1. Téléchargez Poedit
  2. Créez un nouveau catalogue
  3. Configurez les chemins sources de votre projet
  4. Extrayez les chaînes traduisibles
  5. Traduisez et enregistrez

Dépannage

Les traductions n'apparaissent pas

  1. Vérifiez que le fichier .po est bien chargé :
    $translator->addTranslationsFile('/chemin/correct/fr_FR.po', 'default');
  2. Assurez-vous que le Translator est instancié avant l'appel aux helpers
  3. 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 :

  1. Fork le projet
  2. Créez une branche pour votre fonctionnalité (git checkout -b feature/nouvelle-fonctionnalité)
  3. Committez vos changements (git commit -m 'feat(i18n): ajoute telle fonctionnalité')
  4. Poussez vers la branche (git push origin feature/nouvelle-fonctionnalité)
  5. 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