IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Contribuez / Téléchargez Sources et Outils PHP Discussion :

Internationalisation (i18n) des textes


Sujet :

Contribuez / Téléchargez Sources et Outils PHP

  1. #1
    Expert éminent sénior
    Avatar de rawsrc
    Homme Profil pro
    Dev indep
    Inscrit en
    Mars 2004
    Messages
    6 142
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Dev indep

    Informations forums :
    Inscription : Mars 2004
    Messages : 6 142
    Points : 16 545
    Points
    16 545
    Billets dans le blog
    12
    Par défaut Internationalisation (i18n) des textes
    Bonjour à tous,

    Cherchant une solution simple d'internationalisation de textes, j'ai fait le tour du web sans vraiment être emballé.

    Il faut dire aussi que j'étais exigeant :
    - pas de fichiers texte à rallonge
    - système évolutif et très modulaire
    - traductions statiques et paramétrables
    - pas de parseur autre que celui du PHP
    - support de l'UTF-8
    - intégration dans une IDE afin de profiter de l'autocomplétion

    Bref un truc qui ressemble au nirvana !

    Finalement, étant partisan du yaka se servir soi-même (vu que l'on est jamais mieux servi que par soi-même...) je me suis attelé à la tâche et j'ai créé un système qui répond à tous les besoins cités ci-dessus.

    Allez c'est parti :-)


    MINIMUM REQUIS
    La souplesse du système de traduction repose sur les dernières fonctionnalités du PHP 5.3+ : Late Static Binding et __callStatic.
    Donc si vous ne développez pas au minimum sous la version 5.3 de PHP, ce qui suit ne fonctionnera pas.


    IMPORTANT
    Afin d'illustrer la logique, les explications seront accompagnées d'un exemple réel provenant de mon framework d'entreprise.
    J'ai choisi le module fournissant les messages d'erreurs au moteur d'exécution des requêtes de la base de données.
    La classe d'exemple s'appelle : i18nData (dans le fichier i18nData.php)


    PRINCIPE
    Le concept : avoir des classes qui se chargent de la traduction à partir d'une classe pilote qui dérive de la classe abstraite i18n (classe en charge du mécanisme de traduction à la volée).
    Le développeur ne manipule que la classe pilote.

    Voici comment s'organisent les classes (et accessoirement les fichiers) :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    abstract class i18n                 # Mécanisme de traduction à la volée
     
    class i18nData extends i18n         # Classe pilote contenant une liste de messages d'erreur
    class i18nData_fr                   # Classe de traduction contenant la version française des messages d'erreur
    class i18nData_en                   # Classe de traduction contenant la version anglaise des messages d'erreur
    class i18nData_de                   # Classe de traduction contenant la version allemande des messages d'erreur
     
    class i18nValidating extends i18n   # Classe pilote contenant une liste de messages d'erreur (relatifs à la validation des données de l'utilisateur)
    class i18nValidating_fr             # Classe de traduction contenant la version française des messages d'erreur
    class i18nValidating_en             # Classe de traduction contenant la version anglaise des messages d'erreur
    class i18nValidating_de             # Classe de traduction contenant la version allemande des messages d'erreur
    La système de traduction repose sur 3 constantes globales :
    - GCT_LANG__USER : langue du client (lecture du header du browser) ou langue explicitement demandée par le client
    - GCT_LANG__DEFAULT : langue par défaut du système d'affichage
    - GCT_LANG__TRANSLATION_MISSING : si pour n'importe quelle raison, la traduction n'a pas pu être faite, la routine renverra au final le contenu de cette constante


    REGLES DE CODAGE
    - Les messages simple utilisent des constantes de classe
    - Les messages paramétrés utilisent des fonctions statiques
    - Toutes les classes (pilote et traduction) doivent obligatoirement définir cette constante :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    const __SELF__ = __CLASS__;
    - Les classes pilotes doivent toutes dériver de la classe i18n
    - Les classes pilotes peuvent être dérivées les unes des autres
    - Les classes pilotes et leurs classes de traduction doivent partager le même namespace
    - Une constante ou une fonction statique dans une classe pilote NE DOIT SURTOUT PAS ETRE REDEFINIE dans une classe dérivée
    -> En clair : une fois une traduction faite, on ne doit pas y revenir dessus
    - Le nommage des classes de traduction est strict : ClassePilote_Langue (voir l'exemple d'organisation des fichiers)
    - Afin d'éviter tout impair, les classes de traduction doivent être finales (non dérivables)
    - Les classes de traduction peuvent être dérivées de la classe pilote mais cela n'est pas nécessaire
    Ex :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    class i18nData_fr extends i18nData { }
    class i18nData_fr { } # fonctionne aussi
    - Pour les messages paramétrés le traitement de la logique linguistique est déporté dans les classes de traduction :
    -> En conséquence, le code dans la classe pilote doit se résumer à ceci :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    static function myParamMsg($paramA, $paramB, $lang = GCT_LANG__USER) {
       return parent::translate(__FUNCTION__, array($paramA, $paramB), $lang);
    }
    Dans les classes de traduction le paramètre $lang n'a pas lieu d'être
    Ex: dans un fichier de traduction quelconque ce que l'on pourrait trouver :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    static function myParamMsg($paramA, $paramB) {
       return "$paramA est invalide car le maximum $paramB est dépassé";
    }
    - Les traductions multi-lignes doivent être ramenée sur une seule ligne


    FONCTIONNEMENT
    - Tout d'abord, il faut que le traitement normal d'une requête définisse les constantes globales :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    - GCT_LANG__USER
    - GCT_LANG__DEFAULT
    - GCT_LANG__TRANSLATION_MISSING
    - Ne pas oublier que l'on appelle les message à partir des classes pilotes et non des classes de traduction.

    - Pour traduire un message simple (défini par une constante de classe)
    - il suffit de transformer la constante en fonction par l'adjonction de parenthèses ()
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    Ex: constante  => i18nData::DATA_NOT_FOUND
    Ex: traduction => i18nData::DATA_NOT_FOUND()
    - Il est tout à fait possible de forcer la traduction d'un message simple dans une langue donnée en passant l'identifiant de la langue désirée en paramètre :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    Ex: i18nData::DATA_NOT_FOUND('fr')
        i18nData::INTEGRITY_VIOLATION('en')
    - Si aucun paramètre n'est passé, le système se réfère dans l'ordre aux constantes GCT_LANG__USER et GCT_LANG__DEFAULT

    - Pour traduire un message paramétré, il suffit d'appeler la fonction statique correspondante en n'oubliant pas de lui passer les paramètres requis (9 maximum)


    INTERCEPTION DES ERREURS :
    - Si pour n'importe quelle raison (fichier de traduction manquant, fonction ou constante manquante...), la traduction repart sur le langage par défaut (GCT_LANG__DEFAULT)
    - Et si rien de tout cela ne fonctionne, la traduction renverra le contenu de la constante globale GCT_LANG__TRANSLATION_MISSING


    CODE SOURCE DE LA CLASSE i18n
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
     
    /**
     * Copyright (C) 2011+ Martin Lacroix
     * 
     * This program is free software: you can redistribute it and/or modify
     * it under the terms of the GNU General Public License as published by
     * the Free Software Foundation, either version 3 of the License, or
     *  (at your option) any later version.
     *
     * This program is distributed in the hope that it will be useful,
     * but WITHOUT ANY WARRANTY; without even the implied warranty of
     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
     * GNU General Public License for more details.
     *
     * @license GNU General Public License Version 3 (GPLv3)
     * @link http://www.gnu.org/licenses/
     */
     
    /**
     * PHP_VER   : 5.3+ 
     * LIBRARIES : 
     * KEYWORDS  : INTERNATIONALIZATION I18N TRANSLATION
     *             INTERNATIONALISATION I18N TRADUCTION
     * 
     * Class helping for internationalization (i18n) of texts
     * 
     * Classe fournissant un mécanisme d'internationalisation des textes
     * 
     * @package tools
     * @version 1.0.0
     * @author Martin Lacroix
     */
    abstract class i18n {
     
       const __SELF__ = __CLASS__;
     
       /**
        * Renvoie la valeur de la constante traduite
        * Il est possible de forcer la traduction dans une langue en passant son identifiant un argument
        *    Ex: 'fr' 'en' 'de'
        * @param string $name
        * @param array $args
        * @return string|NULL
        */
       static function __callStatic($name, $args = array()) {
          # on récupère la classe propriétaire de la constante (pilotClass)
          $caller  = static::__SELF__;
          $parents = self::treeClassParents($caller);
          $owner   = $caller; # par défaut
     
          if (count($parents)) {
             # on parcourt les parents à partir de la racine des dérivées et on évalue la constante
             foreach($parents as $parent) {
                $msg = @constant($parent . '::' . $name);
                if (NULL !== $msg) {
                   $owner = $parent;
                   break;
                }
             }
          }
     
          if (NULL === $owner) {
             return GCT_LANG__TRANSLATION_MISSING;
          }
     
          # langue de traduction
          $lang = (empty($args)) ? GCT_LANG__USER : $args[0];
     
          if (NULL === $lang) {
             if (NULL === GCT_LANG__DEFAULT) {
                return GCT_LANG__TRANSLATION_MISSING;
             }
             $lang = GCT_LANG__DEFAULT;
          }
     
          $msg = @constant($owner . '_' . $lang . '::' . $name);
     
          # si pas de traduction trouvée, récupère la traduction avec le langage par défaut
          if ((NULL === $msg) && ($lang !== GCT_LANG__DEFAULT)) {
             $msg = @constant($owner . '_' . GCT_LANG__DEFAULT . '::' . $name);
          }
     
          return (NULL === $msg) ? GCT_LANG__TRANSLATION_MISSING : $msg;
       }
     
       /**
        * Renvoie l'arborescence des classes parents
        * @param object $p
        * @param  bool $reflectionInsteadOfNames renvoyer les instances ReflectionClass à la place du nom de la classe
        * @return array Array([] => parentClassName)
        */
       static private function treeClassParents($p, $reflectionInsteadOfNames = FALSE) {
          # vu que les redéfinitions des constantes ou des fonctions statiques sont interdites
          # on renverse le tableau des parents de manière à le parcourir dans le sens racine->enfant->enfant...
          # afin d'isoler la classe qui définit physiquement la constante ou la fonction statique 
          $parents = array();
          $rc = new \ReflectionClass($p);
          while($rc = $rc->getParentClass()) {
             $parents[] = ($reflectionInsteadOfNames) ? $rc : $rc->getName();
          }
          # on écrête le tableau de la classe racine i18n
          array_pop($parents);
          return array_reverse($parents);
       }
     
       /**
        * Détermine la classe qui doit exécuter la fonction en fonction de la langue
        * Langue définie par GCT_LANG__USER ou GCT_LANG__DEFAULT
        * @param string $funcName
        * @param array $params Liste ordonnée de paramètres (9 max) Array([] => param)
        * @param string $lang
        * @return string|NULL
        */
       static protected function translate($funcName, array $params = array(), $lang = GCT_LANG__USER) {      
          # on récupère la classe propriétaire de la fonction statique
          $caller  = static::__SELF__;
          $parents = self::treeClassParents($caller, TRUE);
          $owner   = $caller; # par défaut
     
          if (count($parents)) {
             # on parcourt les parents à partir de la racine des dérivées 
             # et on détermine si la fonction appelée est présente
             foreach($parents as $parent) {
                if ($parent->hasMethod($funcName)) {
                   $owner = $parent->getName();
                   break;
                }
             }
          }
     
          if (NULL === $owner) {
             return GCT_LANG__TRANSLATION_MISSING;
          }
     
          # langue de traduction      
          if (NULL === $lang) {
             if (NULL === GCT_LANG__DEFAULT) {
                return GCT_LANG__TRANSLATION_MISSING;
             }
             $lang = GCT_LANG__DEFAULT;
          }
     
          $msg = self::callFunc($owner . '_' . $lang . '::' . $funcName, $params);
     
          # si pas de message en retour avec la langue courante, on réessaye avec la langue par défaut
          if ((NULL === $msg) && ($lang !== GCT_LANG__DEFAULT)) {
             $msg  = self::callFunc($owner . '_' . GCT_LANG__DEFAULT . '::' . $funcName, $params);
          }
     
          return (NULL === $msg) ? GCT_LANG__TRANSLATION_MISSING : $msg;
       }
     
       /**
        * Lance l'éxecution d'une fonction de traduction paramétrable (9 paramètres maximum)
        * @param string $func
        * @param array $params
        * @return string|NULL
        */
       static private function callFunc($func, array $params = array()) {
          $nb = count($params);
     
          if ($nb === 0) { 
             return @call_user_func($func); 
          }
     
          if ($nb === 1) { 
             return @call_user_func(
                $func, 
                $params[0]
             );
          }
     
          if ($nb === 2) { 
             return @call_user_func(
                $func, 
                $params[0], $params[1]
             );
          }
     
          if ($nb === 3) { 
             return @call_user_func(
                $func, 
                $params[0], $params[1], $params[2]
             );
          }
     
          if ($nb === 4) { 
             return @call_user_func(
                $func, 
                $params[0], $params[1], $params[2], $params[3]
             );
          }
     
          if ($nb === 5) { 
             return @call_user_func(
                $func, $params[0], $params[1], $params[2], $params[3], $params[4]
             );
          }
     
          if ($nb === 6) { 
             return @call_user_func(
                $func, $params[0], $params[1], $params[2], $params[3], $params[4], $params[5]
             );
          }
     
          if ($nb === 7) { 
             return @call_user_func(
                $func, 
                $params[0], $params[1], $params[2], $params[3], $params[4], $params[5], $params[6]
             ); 
          }
     
          if ($nb === 8) { 
             return @call_user_func(
                $func, 
                $params[0], $params[1], $params[2], $params[3], $params[4], $params[5], $params[6], $params[7]
             );
          }
     
          if ($nb === 9) { 
             return @call_user_func(
                $func, 
                $params[0], $params[1], $params[2], $params[3], $params[4], $params[5], $params[6], $params[7], $params[8]
             );
          }
       }
    }

    EXEMPLE SIMPLE
    Classe pilote : i18nData
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    <?php
    class i18nData extends i18n {
     
       const __SELF__ = __CLASS__;
     
       const UNAVAILABLE_CONNEXION               = 0;
       const UNABLE_TO_GET_A_STATEMENT           = 1;
       const UNABLE_TO_LINK_A_VALUE_TO_A_TAG     = 2;
       const EXECUTION_ERROR                     = 3;
       const INTEGRITY_VIOLATION                 = 4;
       const UNABLE_TO_EXTRACT_THE_DATA          = 5;
       const DATA_NOT_FOUND                      = 6;
       const EMPTY_RECORD                        = 7;
       const UNABLE_TO_RETRIEVE_THE_NEW_ID       = 8;
       const UNABLE_TO_BUILD_THE_SELECT_CLAUSE   = 9;
       const UNABLE_TO_BUILD_THE_FROM_CLAUSE     = 10;
       const UNABLE_TO_BUILD_THE_ORDER_BY_CLAUSE = 11;
       const UNABLE_TO_BUILD_THE_GROUP_BY_CLAUSE = 12;
       const UNABLE_TO_BUILD_THE_WHERE_CLAUSE    = 13;
       const UNABLE_TO_BUILD_THE_HAVING_CLAUSE   = 14;
     
       static function unableToLinkAValueToTheTag($tag, $lang = GCT_LANG__USER) {
          return parent::translate(__FUNCTION__, array($tag), $lang);
       }
    }
    ?>
    Classe de traduction française :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    <?php
    final class i18nData_fr {
     
       const __SELF__ = __CLASS__;
     
       const UNAVAILABLE_CONNEXION               = 'connexion indisponible';
       const UNABLE_TO_GET_A_STATEMENT           = 'impossible de récupérer un statement';
       const UNABLE_TO_LINK_A_VALUE_TO_A_TAG     = 'impossible de rattacher une valeur à son tag';
       const EXECUTION_ERROR                     = "erreur à l'exécution";
       const INTEGRITY_VIOLATION                 = "erreur d'intégrité référentielle";
       const UNABLE_TO_EXTRACT_THE_DATA          = "impossible d'extraire les données";
       const DATA_NOT_FOUND                      = 'données non trouvées';
       const EMPTY_RECORD                        = 'enregistrement vide';
       const UNABLE_TO_RETRIEVE_THE_NEW_ID       = 'impossible de récupérer le nouvel id';
       const UNABLE_TO_BUILD_THE_SELECT_CLAUSE   = 'impossible de générer la clause select';
       const UNABLE_TO_BUILD_THE_FROM_CLAUSE     = 'impossible de générer la clause from';
       const UNABLE_TO_BUILD_THE_ORDER_BY_CLAUSE = 'impossible de générer la clause order by';
       const UNABLE_TO_BUILD_THE_GROUP_BY_CLAUSE = 'impossible de générer la clause group by';
       const UNABLE_TO_BUILD_THE_WHERE_CLAUSE    = 'impossible de générer la clause where';
       const UNABLE_TO_BUILD_THE_HAVING_CLAUSE   = 'impossible de générer la clause having';
     
       static function unableToLinkAValueToTheTag($tag) {
          return 'impossible de rattacher une valeur au tag : ' . $tag;
       }
    }
    ?>
    Classe de traduction anglaise :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    <?php
    final class i18nData_en {
     
       const __SELF__ = __CLASS__;
     
       const UNAVAILABLE_CONNEXION               = 'unavailable connexion';
       const UNABLE_TO_GET_A_STATEMENT           = 'unable to get a statement';
       const UNABLE_TO_LINK_A_VALUE_TO_A_TAG     = 'unable to link a value to a tag';
       const EXECUTION_ERROR                     = 'execution error';
       const INTEGRITY_VIOLATION                 = 'integrity violation';
       const UNABLE_TO_EXTRACT_THE_DATA          = 'unable to extract the data';
       const DATA_NOT_FOUND                      = 'data not found';
       const EMPTY_RECORD                        = 'empty record';
       const UNABLE_TO_RETRIEVE_THE_NEW_ID       = 'unable to retrieve the new id';
       const UNABLE_TO_BUILD_THE_SELECT_CLAUSE   = 'unable to build the select clause';
       const UNABLE_TO_BUILD_THE_FROM_CLAUSE     = 'unable to build the from clause';
       const UNABLE_TO_BUILD_THE_ORDER_BY_CLAUSE = 'unable to build the order by clause';
       const UNABLE_TO_BUILD_THE_GROUP_BY_CLAUSE = 'unable to build the group clause';
       const UNABLE_TO_BUILD_THE_WHERE_CLAUSE    = 'unable to build the where clause';
       const UNABLE_TO_BUILD_THE_HAVING_CLAUSE   = 'unable to build the having clause';
     
       static function unableToLinkAValueToTheTag($tag) {
          return 'unable to link a value to the tag: ' . $tag;
       }
    }
    ?>
    Utilisation :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    <?php
     
    define('GCT_LANG__USER', 'fr');
    define('GCT_LANG__DEFAULT', 'en');
    define('GCT_LANG__TRANSLATION_MISSING', 'notTranslated');
     
    echo i18nData::UNABLE_TO_BUILD_THE_FROM_CLAUSE(), '<br />';
    echo i18nData::UNABLE_TO_BUILD_THE_FROM_CLAUSE('en'), '<br />';
    echo i18nData::UNABLE_TO_BUILD_THE_FROM_CLAUSE('de'), '<br />';
    echo i18nData::unableToLinkAValueToTheTag('monTag'), '<br />';
    echo i18nData::unableToLinkAValueToTheTag('monTag', 'en'), '<br />';
    echo i18nData::unableToLinkAValueToTheTag('monTag', 'de'), '<br />';
    ?>

    EXEMPLE AVANCE MONTRANT UNE ARBORESCENCE DE CLASSES PILOTES
    On suppose qu'on a défini une classe de base à tous les formulaires regroupant quelques contrôles très communs et que l'on doive maintenant développer un formulaire plus complexe bâti sur les fondations du formulaire de base.
    Voici comment internationaliser tout ça :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    <?php
    # Classe pilote du formulaire de base
    class i18nFormBase extends i18n {
     
       const __SELF__ = __CLASS__;
     
       const CREATE = 1;
       const UPDATE = 2;
       const DELETE = 3;
       const SELECT = 4;
    }
     
    # Une classe de traduction du formulaire de base
    final class i18nFormBase_fr {
     
       const __SELF__ = __CLASS__;
     
       const CREATE = 'créer';
       const UPDATE = 'modifier';
       const DELETE = 'supprimer';
       const SELECT = 'sélectionner';
    }
     
    # Une classe de traduction du formulaire de base
    final class i18nFormBase_en {
     
       const __SELF__ = __CLASS__;
     
       const CREATE = 'create';
       const UPDATE = 'update';
       const DELETE = 'delete';
       const SELECT = 'select';
    }
     
     
    # Classe pilote du formulaire complexe bâti sur le formulaire de base
    class i18nFormMail extends i18nFormBase {
     
       const __SELF__ = __CLASS__;
       # comme vous pouvez le constater, on  n'ajoute que ce qui est nécessaire
       const SEND = 1;
       const SPAM = 2;
    }
     
    # Une classe de traduction du formulaire complexe
    class i18nFormMail_fr {
     
       const __SELF__ = __CLASS__;
       # comme vous pouvez le constater, on  ne traduit que ce qui est nécessaire
       const SEND = 'expédier';
       const SPAM = 'courrier indésirable';
    }
     
    # Une classe de traduction du formulaire complexe
    class i18nFormMail_en {
     
       const __SELF__ = __CLASS__;
       # comme vous pouvez le constater, on  ne traduit que ce qui est nécessaire
       const SEND = 'send';
       const SPAM = 'spam';
    }
    ?>
    Et maintenant le code de test :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    <?php
    define('GCT_LANG__USER', 'fr');
    define('GCT_LANG__DEFAULT', 'en');
    define('GCT_LANG__TRANSLATION_MISSING', 'notTranslated');
     
    echo i18nFormBase::CREATE(), '<br />';
    echo i18nFormMail::CREATE(), '<br />';
    echo i18nFormMail::SEND(), '<br />';
    ?>
    Si vous utilisez un EDI moderne, vous pourrez constater que l'autocomplétion fonctionne sans problèmes particuliers et vous récupérez les bonnes données, ouf

    Dans tous les cas, si vous avez des questions pour l'implémentation, n'hésitez pas. Et si malgré tout mon attention, une banane s'était glissée dans le code, je suis preneur de toute remarque et commentaire qui va bien. Merci et j'espère que cela vous servira un jour.

  2. #2
    Expert confirmé
    Avatar de Thes32
    Homme Profil pro
    Développeur PHP, .Net, T-SQL
    Inscrit en
    Décembre 2006
    Messages
    2 379
    Détails du profil
    Informations personnelles :
    Sexe : Homme

    Informations professionnelles :
    Activité : Développeur PHP, .Net, T-SQL

    Informations forums :
    Inscription : Décembre 2006
    Messages : 2 379
    Points : 4 853
    Points
    4 853
    Par défaut
    Salut,

    Joli boulot !
    J'ai juste une question que reproches tu aux modèles basés sur des fichiers xml/json/yaml ? Je trouve quand même assez gênant de pouvoir ajouter les textes à traduire dans des classes php...
    Développeur | Zend Certified Engineer

    Étapes Pour mieux se servir du forum:
    1. Commencez par lire les cours et tutoriels ;
    2. Faites une recherche;
    3. Faites un post si rien trouvé dans les deux étapes précédentes en respectant les règles;

    Nix>_Rien n'est plus pratique que la théorie

  3. #3
    Membre émérite
    Profil pro
    Inscrit en
    Janvier 2007
    Messages
    1 448
    Détails du profil
    Informations personnelles :
    Âge : 39
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Janvier 2007
    Messages : 1 448
    Points : 2 284
    Points
    2 284
    Par défaut
    bah c'est bien tout de ce code, mais en refusant d'utiliser les librairies standards tu te coupes de bien des outils pratiquent qu'elles proposent pour gérer la pluralisation par exemple.

    cf http://www.developpez.net/forums/d11...n/#post6184152

    Après, je suis fondamentalement en désaccord avec ton principe de base de tout mettre dans des classes pour avoir l’auto complétion.
    Car avec ta solution je dois me taper tous les fichiers clients à intégrer dans des classes et les maintenir.
    Alors qu'avec du xml ou du yaml, tu leurs donnes un template bien fournit, ils le remplissent, tu le remplaces sur le serveur et basta.

    a+

  4. #4
    Modérateur
    Avatar de grunk
    Homme Profil pro
    Lead dév - Architecte
    Inscrit en
    Août 2003
    Messages
    6 691
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 39
    Localisation : France, Côte d'Or (Bourgogne)

    Informations professionnelles :
    Activité : Lead dév - Architecte
    Secteur : Industrie

    Informations forums :
    Inscription : Août 2003
    Messages : 6 691
    Points : 20 222
    Points
    20 222
    Par défaut
    Pas convaincu non plus par le principe.

    Si je ne m'abuse , si tu déclares 50 constantes de traduction dans ta classe c'est autant d'espace mémoire utilisé , alors que les données ne seront potentiellement pas utilisées.

    Placer tes traductions dans des fichiers php pour l'auto complétion c'est un choix , mais tu risques de vite le regretter le jour ou la traduction sera faite par des non informaticiens ou un organisme de traduction.
    Soit tu te fais une mécanique pour extraire les chaînes à traduire , soit tu récupères des fichiers bourrés d'erreurs 9 fois sur 10.

    Perso j'avais eu un peu de mal à me mettre à gettext , mais depuis je pourrais pas faire sans.
    Pry Framework php5 | N'oubliez pas de consulter les FAQ Java et les cours et tutoriels Java

  5. #5
    Expert éminent sénior
    Avatar de rawsrc
    Homme Profil pro
    Dev indep
    Inscrit en
    Mars 2004
    Messages
    6 142
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Dev indep

    Informations forums :
    Inscription : Mars 2004
    Messages : 6 142
    Points : 16 545
    Points
    16 545
    Billets dans le blog
    12
    Par défaut
    Bonjour,

    Je voulais un système de traduction léger et facile à prendre en main : plus léger c'est difficilement faisable : une classe mère et zou c'est parti.

    Les langues humaines étant ce qu'elles sont, les systèmes de traduction sont tous basés sur une approche clé-valeur, mon approche ne diffère pas sur ce point.
    Ensuite, j'aurais tendance à vous dire que j'affiche toujours des messages laconiques (dans 99% des cas, c'est une simple alerte) du genre 'date invalide', 'numérique attendu'...
    Je ne mets jamais un truc du genre :
    - Cher utilisateur, le nom que vous avez tapé est incorrect
    - L'âge du capaitaine est bidon car il semblerait qu'il ne soit pas assez vieux !

    En restant laconqiue, les besoin de pluralisation...

    Les messages techniques sont très très laconiques. Je ne remonte aucune information sensible du genre :
    - Après cinq malheureuse(s) tentative(s) de connexion à la base de données mysql, mes bits et moi-même sommes désolés de ne pas pouvoir traîter votre demande

    La traduction se limite qu'à des mots ou des phrases simples. Lorsque vous ne pouvez pas utiliser de librairie externe c'est un moyen facile d'y palier.
    Cette approche fonctionne parfaitement dans de nombreux cas sans se prendre la tête.

    Comme il est possible de faire un dictionnaire de traduction monolithique (tmx, yaml...), il est tout à fait possible de faire pareil avec mon approche.
    Une classe unique pour tout, bien que cela ne soit pas souhaitable. D'où la raison de la possibilité de dériver les classes de traductions les unes des autres.
    Si vous ne voulez pas consommer trop de ressources avec des constantes, il suffit de les remplacer par des fonctions statiques qui elles ne sont chargées que lorsqu'elles sont appelées.
    Et si cela devient trop fastidieux, il ne reste plus qu'à identifier les messages par des codes...

    Ensuite pour les très gros besoins de traduction comme par exemple pour des articles, des notices..., les systèmes automatiques sont totalement dépassés (la faute à la grammaire et aux innombrables cas particuliers lingusitiques...), il faut faire appel à un traducteur en chair et en os et là, c'est une autre problématique.

    Enfin, il va de soi que je n'ai jamais eu la prétention de remplacer des systèmes bien plus complets comme ICU ou gettext.
    C'est un détournement original des possibilités du langage PHP, c'est tout et rien de plus.

    Attention, classe dangereuse : à utiliser avec précautions

  6. #6
    Expert éminent
    Avatar de Benjamin Delespierre
    Profil pro
    Développeur Web
    Inscrit en
    Février 2010
    Messages
    3 929
    Détails du profil
    Informations personnelles :
    Âge : 36
    Localisation : France, Alpes Maritimes (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur Web
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Février 2010
    Messages : 3 929
    Points : 7 762
    Points
    7 762
    Par défaut
    Hello

    Je trouve ta solution un peux complexe pour ce qu'elle propose: elle suppose la création de classes supplémentaires pour ajouter des langues, ce qui n'est pas bien pratique. En plus elle demande nécessairement PHP 5.3 et tous les hébergeurs ne le proposent pas encore. D'ailleurs, quitte à utiliser PHP 5.3 pourquoi ne pas se servir des namespaces ?

    Je posterai plus tard dans un thread séparé, mais si ça t'inspire voici ma classe de gestion i18n:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    265
    266
    267
    268
    269
    270
    271
    272
    273
    274
    275
    276
    277
    278
    279
    280
    281
    282
    283
    284
    285
    286
    287
    288
    289
    290
    291
    292
    293
    294
    295
    296
    297
    298
    299
    300
    301
    302
    303
    304
    305
    306
    307
    308
    309
    310
    311
    312
    313
    314
    315
    316
    317
    318
    319
    320
    321
    322
    323
    324
    325
    326
    327
    328
    329
    330
    331
    332
    333
    334
    335
    336
    337
    338
    339
    340
    341
    342
    343
    344
    345
    346
    347
    348
    349
    350
    351
    352
    353
    354
    355
    356
    357
    358
    359
    360
    361
    362
    363
    <?php
    /**
     * PHP AXIOM
     *
     * @license LGPL
     * @author Benjamin DELESPIERRE <benjamin.delespierre@gmail.com>
     * @category libAxiom
     * @package library
     * $Date: 2011-07-27 18:44:09 +0200 (mer., 27 juil. 2011) $
     * $Id: Lang.class.php 46 2011-07-27 16:44:09Z TchernoBen@gmail.com $
     */
     
    /**
     * Lang Managemnt Class
     *
     * @author Delespierre
     * @version $Rev: 46 $
     * @subpackage Lang
     */
    class Lang {
     
        /**
         * Configuration
         * @var array
         */
        protected static $_config = array();
     
        /**
         * Translation cache
         * @var array
         */
        protected static $_translations = array();
     
        /**
         * Accepted language cache
         * @var array
         */
        protected static $_accepted_languages_cache;
     
        /**
         * Set configuration.
         * If the $load_language parameter is set to true,
         * this method will initialize translations directly.
         * @param array $config = array()
         * @param boolean $load_loanguage = true
         * @return void
         */
        public static function setConfig ($config = array()) {
            $default = array(
                'locale' => 'fr',
                'locales' => array('fr'),
                'date_format' => 'd/m/y h:i:s',
                'lang_dir' => dirname(dirname(__FILE__)) . '/application/locale/langs',
                'lang_file' => 'fr.ini',
                'base_url' => '/',
            );
     
            self::$_config = array_merge($default, $config);
     
            if (isset(self::$_config['locale']) && self::$_config['locale'] === 'auto') {
                $lngs = self::getAcceptedLanguages();
                $found = false;
                if (!empty($lngs)) {
                    while ($lng = array_shift($lngs)) {
                        if (in_array($lng, self::$_config['locales'])) {
                            self::$_config['locale'] = $lng;
                            self::$_config['lang_file'] = "$lng.ini";
                            break;
                        }
                    }
                }
            }
        }
     
        /**
         * Load language
         * @param string $lang_file = null
         * @throws MissingFileException
         * @throws RuntimeException
         * @return void
         */
        public static function loadLanguage ($lang_file = null) {
            if (!isset($lang_file))
                $lang_file = self::$_config['lang_dir'] . '/' . self::$_config['lang_file'];
     
            if (!file_exists($lang_file)) {
                throw new MissingFileException($lang_file);
            }
     
            if (!$desc = parse_ini_file($lang_file, true)) {
                throw new RuntimeException("Could not parse $lang_file");
            }
     
            self::$_config = array_merge(self::$_config, $desc['settings']);
            self::$_translations = array_merge(self::$_translations, $desc['translations']);
        }
     
        /**
         * Get current locale
         * @return string
         */
        public static function getLocale () {
            return self::$_config['locale'];
        }
     
        /**
         * Set current locale
         * @param string $lang
         * @param boolean $reload_translations = true
         * @return string
         */
        public static function setLocale ($lang, $reload_translations = true) {
            if ($lang !== self::getLocale() && in_array($lang, self::$_config['locales'])) {
                self::$_config['locale'] = $lang;
                self::$_config['lang_file'] = "$lang.ini";
     
                if ($reload_translations)
                    self::loadLanguage();
            }
     
            return self::$_config['locale'];
        }
     
        /**
         * Get avaialable locales
         * @return array
         */
        public static function getLocales () {
            return self::$_config['locales'];
        }
     
        /**
         * Get date format
         * @return string
         */
        public static function getDateFormat () {
            return self::$_config['date_format'];
        }
     
        /**
         * Get lang file
         * @return string
         */
        public static function getLangFile () {
            return self::$_config['lang_file'];
        }
     
        /**
         * Get translation table
         * @return array
         */
        public static function getTranslations () {
            return self::$_translations;
        }
     
        /**
         * Get accepeted languages using browser capabilities
         * @return array
         */
        public static function getAcceptedLanguages() {
            if (isset(self::$_accepted_languages_cache))
                return self::$_accepted_languages_cache;
     
            $httplanguages = $_SERVER['HTTP_ACCEPT_LANGUAGE'];
            $languages = array();
            if (empty($httplanguages)) {
                return $languages;
            }
     
            foreach (explode(',', $httplanguages) as $accept) {
                $result = preg_match('/^([a-z]{1,8}(?:[-_][a-z]{1,8})*)(?:;\s*q=(0(?:\.[0-9]{1,3})?|1(?:\.0{1,3})?))?$/i', $accept, $match);
     
                if (!$result) {
                    continue;
                }
                if (isset($match[2])) {
                    $quality = (float)$match[2];
                }
                else {
                    $quality = 1.0;
                }
     
                $countries = explode('-', $match[1]);
                $region = array_shift($countries);
                $country_sub = explode('_', $region);
                $region = array_shift($country_sub);
     
                foreach($countries as $country)
                    $languages[$region . '_' . strtoupper($country)] = $quality;
     
                foreach($country_sub as $country)
                    $languages[$region . '_' . strtoupper($country)] = $quality;
     
                $languages[$region] = $quality;
            }
     
            return self::$_accepted_languages_cache = $languages;
        }
     
        /**
         * Translate
         * @param string $key
         * @return string
         */
        public static function i18n ($key) {
            if (empty(self::$_translations))
                self::loadLanguage();
     
            if (!isset(self::$_translations[$key])) {
                return "<!-- UNDEFINED TRANSLATION: $key -->";
            }
     
            $args = func_get_args();
            $key = array_shift($args);
            $msg = (string)self::$_translations[$key];
            switch (count($args)) {
                case 0: return $msg; break;
                case 1: return sprintf($msg, $args[0]); break;
                case 2: return sprintf($msg, $args[0], $args[1]); break;
                case 3: return sprintf($msg, $args[0], $args[1], $args[2]); break;
                case 4: return sprintf($msg, $args[0], $args[1], $args[2], $args[3]); break;
                case 5: return sprintf($msg, $args[0], $args[1], $args[2], $args[3], $args[4]); break;
                default:
                    array_unshift($msg, $args);
                    return call_user_func_array('sprintf', $args);
                    break;
            }
        }
     
        public static function getBaseUrl () {
            return self::$_config['base_url'];
        }
     
        /**
         * Format URL
         * @param string $route
         * @param string $action = ""
         * @param string $lang = false
         * @return string
         */
        public static function url ($url, $lang = false) {
            if (!$lang)
                $lang = self::getLocale();
     
            return self::$_config['base_url'] . "$lang/$url";
        }
     
        /**
         * Format src
         * @param string $ressource
         * @return string
         */
        public static function src ($ressource) {
            if (strpos($ressource, '/') === 0)
                $ressource = substr($ressource, 1);
     
            return self::$_config['base_url'] . $ressource;
        }
    }
     
    /**
     * (Non PHP-doc)
     * @see Lang::i18n
     */
    function i18n ($key) {
        $args = func_get_args();
        switch (count($args)) {
            case 1: return Lang::i18n($args[0]); break;
            case 2: return Lang::i18n($args[0], $args[1]); break;
            case 3: return Lang::i18n($args[0], $args[1], $args[2]); break;
            case 4: return Lang::i18n($args[0], $args[1], $args[2], $args[3]); break;
            case 5: return Lang::i18n($args[0], $args[1], $args[2], $args[3], $args[4]); break;
            default: return call_user_func_array(array('Lang', 'i18n'), $args); break;
        }
    }
     
    /**
     * (Non PHP-doc)
     * @see Lang::url
     */
    function url ($route, $action = "", $lang = false) {
        return Lang::url($route, $action, $lang);
    }
     
    /**
     * (Non PHP-doc)
     * @see Lang::src
     */
    function src ($ressource) {
        return Lang::src($ressource);
    }
     
    /**
     * Parse a date to return an human readable date
     * @param mixed $date
     * @return string
     */
    function date2str ($date) {
        if (is_int($date))
            $date = date('ymdHi', $date);
     
        $minusdate = date('ymdHi') - $date;
     
        if($minusdate > 88697640 && $minusdate < 100000000) {
            $minusdate = $minusdate - 88697640;
        }
     
        switch ($minusdate) {
            case ($minusdate < 99):
                if($minusdate == 1) {
                    $date_string = i18n('date.minutes_ago', 1);
                }
                elseif($minusdate > 59) {
                    $date_string =  i18n('date.minutes_ago', $minusdate - 40);
                }
                elseif($minusdate > 1 && $minusdate < 59) {
                    $date_string = i18n('date.minutes_ago', $minusdate);
                }
                break;
     
            case ($minusdate > 99 && $minusdate < 2359):
                $flr = floor($minusdate * .01);
                if($flr == 1) {
                    $date_string = i18n('date.hours_ago', 1);;
                }
                else {
                    $date_string = i18n('date.hours_ago', $flr);
                }
                break;
     
            case ($minusdate > 2359 && $minusdate < 310000):
                $flr = floor($minusdate * .0001);
                if($flr == 1) {
                    $date_string = i18n('date.days_ago', 1);
                }
                else {
                    $date_string = i18n('date.days_ago', $flr);
                }
                break;
     
            case ($minusdate > 310001 && $minusdate < 12320000):
                $flr = floor($minusdate * .000001);
                if($flr == 1) {
                    $date_string = i18n('date.months_ago', 1);
                }
                else {
                    $date_string = i18n('date.months_ago', $flr);
                }
                break;
     
            case ($minusdate > 100000000):
            default:
                $flr = floor($minusdate * .00000001);
                if($flr == 1) {
                    $date_string = i18n('date.years_ago', 1);
                }
                else {
                    $date_string = i18n('date.years_ago', $flr);
                }
        }
     
        return $date_string;
    }
    Note: ce code est une partie du framework Axiom. Licencié sous LGPL.

    Usage (à titre d'exemple seulement):
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <?php
    // Lors du bootsrap
    Lang::setConfig(array('locale' => 'auto'));
     
    // Dans le fichier fr.ini par exemple
    [translations]
    ma.cle.de.traduction = "Bonjour %s, vous avez %d messages !"
     
    // Usage à proprement parler
    <span><?=i18n('ma.cle.de.traduction', $var1, $var2')?></span>
    Elle supporte:
    - Reconnaissance automatique de la langue
    - Chargement automatique des traductions depuis des fichiers ini
    - Gestion des dates, prise en compte des formats humains (par exemple "Il y a 3 jours")
    - Gestion de n locales disponibles
    - Offre des typo helpers pour faciliter son utilisation
    - Remplacement de placeholders (syntaxe printf) dans les traductions

    Prérequis:
    - PHP 5.1

    Aucune définition de variables / constante externe n'est requise, tout passe par Lang::setConfig. Je n'ai pas cherché à aller plus loin car cette classe est déjà assez performante et couvre 99% de mes besoins. De toute façon si on veut allez jusqu'au bout du problème de l’internationalisation, il faut utiliser gettext, y'a pas 36 solutions.

  7. #7
    Expert éminent sénior

    Profil pro
    Inscrit en
    Septembre 2010
    Messages
    7 920
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2010
    Messages : 7 920
    Points : 10 726
    Points
    10 726
    Par défaut
    je préfère de loin ICU que gettext, MessageFormatter est simple a utilisé, après faut créer les ressource a partir de XLIFF ou/et RB Manager, la classe est bouclé en quelque ligne

  8. #8
    Expert éminent sénior
    Avatar de rawsrc
    Homme Profil pro
    Dev indep
    Inscrit en
    Mars 2004
    Messages
    6 142
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Dev indep

    Informations forums :
    Inscription : Mars 2004
    Messages : 6 142
    Points : 16 545
    Points
    16 545
    Billets dans le blog
    12
    Par défaut Amélioration des perfs
    Voici le code source de la classe postée précédemment avec un raccourcissement du code et amélioration des perfs :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    <?php
    /**
     * Copyright (C) 2011+ Martin Lacroix
     * 
     * This program is free software: you can redistribute it and/or modify
     * it under the terms of the GNU General Public License as published by
     * the Free Software Foundation, either version 3 of the License, or
     *  (at your option) any later version.
     *
     * This program is distributed in the hope that it will be useful,
     * but WITHOUT ANY WARRANTY; without even the implied warranty of
     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
     * GNU General Public License for more details.
     *
     * @license GNU General Public License Version 3 (GPLv3)
     * @link http://www.gnu.org/licenses/
     */
     
    /**
     * PHP_VER   : 5.3+ 
     * LIBRARIES : 
     * KEYWORDS  : INTERNATIONALIZATION I18N TRANSLATION
     *             INTERNATIONALISATION I18N TRADUCTION
     * 
     * Class helping for internationalization (i18n) of texts
     * 
     * Classe fournissant un mécanisme d'internationalisation des textes
     * 
     * @package i18n
     * @version 1.0.1
     * @author Martin Lacroix
     */
    abstract class i18n {
     
       const __SELF__ = __CLASS__;
     
       /**
        * Renvoie la valeur de la constante traduite
        * Il est possible de forcer la traduction dans une langue en passant son identifiant un argument
        *    Ex: 'fr' 'en' 'de'
        * @param string $name
        * @param array $args
        * @return string|NULL
        */
       static function __callStatic($name, $args = array()) {
          # on récupère la classe propriétaire de la constante (pilotClass)
          $rc = new \ReflectionClass(static::__SELF__);
          while($rc->getParentClass()->hasConstant($name)) {
             $rc = $rc->getParentClass();
          }
          $owner = $rc->getName();
     
          if (NULL === $owner) {
             return GCT_LANG__TRANSLATION_MISSING;
          }
     
          # langue de traduction
          $lang = (empty($args)) ? GCT_LANG__USER : $args[0];
     
          if (NULL === $lang) {
             if (NULL === GCT_LANG__DEFAULT) {
                return GCT_LANG__TRANSLATION_MISSING;
             }
             $lang = GCT_LANG__DEFAULT;
          }
     
          $msg = @constant($owner . '_' . $lang . '::' . $name);
     
          # si pas de traduction trouvée, récupère la traduction avec le langage par défaut
          if ((NULL === $msg) && ($lang !== GCT_LANG__DEFAULT)) {
             $msg = @constant($owner . '_' . GCT_LANG__DEFAULT . '::' . $name);
          }
     
          return (NULL === $msg) ? GCT_LANG__TRANSLATION_MISSING : $msg;
       }
     
       /**
        * Détermine la classe qui doit exécuter la fonction en fonction de la langue
        * Langue définie par GCT_LANG__USER ou GCT_LANG__DEFAULT
        * @param string $funcName
        * @param array $params Liste ordonnée de paramètres (9 max) Array([] => param)
        * @param string $lang
        * @return string|NULL
        */
       static protected function translate($funcName, array $params = array(), $lang = GCT_LANG__USER) {      
          # on récupère la classe propriétaire de la fonction statique
          $rc = new \ReflectionClass(static::__SELF__);
          while($rc->getParentClass()->hasMethod($funcName)) {
             $rc = $rc->getParentClass();
          }
          $owner = $rc->getName();
     
          if (NULL === $owner) {
             return GCT_LANG__TRANSLATION_MISSING;
          }
     
          # langue de traduction      
          if (NULL === $lang) {
             if (NULL === GCT_LANG__DEFAULT) {
                return GCT_LANG__TRANSLATION_MISSING;
             }
             $lang = GCT_LANG__DEFAULT;
          }
     
          $msg = self::callFunc($owner . '_' . $lang . '::' . $funcName, $params);
     
          # si pas de message en retour avec la langue courante, on réessaye avec la langue par défaut
          if ((NULL === $msg) && ($lang !== GCT_LANG__DEFAULT)) {
             $msg  = self::callFunc($owner . '_' . GCT_LANG__DEFAULT . '::' . $funcName, $params);
          }
     
          return (NULL === $msg) ? GCT_LANG__TRANSLATION_MISSING : $msg;
       }
     
       /**
        * Lance l'éxecution d'une fonction de traduction paramétrable (9 paramètres maximum)
        * @param string $func
        * @param array $params
        * @return string|NULL
        */
       static private function callFunc($func, array $params = array()) {
          $nb = count($params);
     
          if ($nb === 0) { 
             return @call_user_func($func); 
          }
     
          if ($nb === 1) { 
             return @call_user_func(
                $func, 
                $params[0]
             );
          }
     
          if ($nb === 2) { 
             return @call_user_func(
                $func, 
                $params[0], $params[1]
             );
          }
     
          if ($nb === 3) { 
             return @call_user_func(
                $func, 
                $params[0], $params[1], $params[2]
             );
          }
     
          if ($nb === 4) { 
             return @call_user_func(
                $func, 
                $params[0], $params[1], $params[2], $params[3]
             );
          }
     
          if ($nb === 5) { 
             return @call_user_func(
                $func, $params[0], $params[1], $params[2], $params[3], $params[4]
             );
          }
     
          if ($nb === 6) { 
             return @call_user_func(
                $func, $params[0], $params[1], $params[2], $params[3], $params[4], $params[5]
             );
          }
     
          if ($nb === 7) { 
             return @call_user_func(
                $func, 
                $params[0], $params[1], $params[2], $params[3], $params[4], $params[5], $params[6]
             ); 
          }
     
          if ($nb === 8) { 
             return @call_user_func(
                $func, 
                $params[0], $params[1], $params[2], $params[3], $params[4], $params[5], $params[6], $params[7]
             );
          }
     
          if ($nb === 9) { 
             return @call_user_func(
                $func, 
                $params[0], $params[1], $params[2], $params[3], $params[4], $params[5], $params[6], $params[7], $params[8]
             );
          }
       }
    }
    ?>
    Merci pour vos contributions, j'en prends note

  9. #9
    Expert éminent sénior

    Profil pro
    Inscrit en
    Septembre 2010
    Messages
    7 920
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2010
    Messages : 7 920
    Points : 10 726
    Points
    10 726
    Par défaut
    qu'est ce que c'est que ça :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    static private function callFunc($func, array $params = array()) {
          $nb = count($params);
     
          if ($nb === 0) { 
             return @call_user_func($func); 
          }
     
          if ($nb === 1) { 
             return @call_user_func(
                $func, 
                $params[0]
             );
          }
     
          if ($nb === 2) { 
             return @call_user_func(
                $func, 
                $params[0], $params[1]
             );
          }
     
          if ($nb === 3) { 
             return @call_user_func(
                $func, 
                $params[0], $params[1], $params[2]
             );
          }
     
          if ($nb === 4) { 
             return @call_user_func(
                $func, 
                $params[0], $params[1], $params[2], $params[3]
             );
          }
     
          if ($nb === 5) { 
             return @call_user_func(
                $func, $params[0], $params[1], $params[2], $params[3], $params[4]
             );
          }
     
          if ($nb === 6) { 
             return @call_user_func(
                $func, $params[0], $params[1], $params[2], $params[3], $params[4], $params[5]
             );
          }
     
          if ($nb === 7) { 
             return @call_user_func(
                $func, 
                $params[0], $params[1], $params[2], $params[3], $params[4], $params[5], $params[6]
             ); 
          }
     
          if ($nb === 8) { 
             return @call_user_func(
                $func, 
                $params[0], $params[1], $params[2], $params[3], $params[4], $params[5], $params[6], $params[7]
             );
          }
     
          if ($nb === 9) { 
             return @call_user_func(
                $func, 
                $params[0], $params[1], $params[2], $params[3], $params[4], $params[5], $params[6], $params[7], $params[8]
             );
          }
    tu connais pas call_user_func_array ?

  10. #10
    Expert éminent sénior
    Avatar de rawsrc
    Homme Profil pro
    Dev indep
    Inscrit en
    Mars 2004
    Messages
    6 142
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Dev indep

    Informations forums :
    Inscription : Mars 2004
    Messages : 6 142
    Points : 16 545
    Points
    16 545
    Billets dans le blog
    12
    Par défaut
    Citation Envoyé par stealth35 Voir le message
    qu'est ce que c'est que ça
    Bah écoutes une horreur monstrueuse
    Faut avouer que je n'y ai pas pensé,

    Allez voici la version sacrément raccourcie dis donc :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    <?php
    /**
     * Copyright (C) 2011+ Martin Lacroix
     * 
     * This program is free software: you can redistribute it and/or modify
     * it under the terms of the GNU General Public License as published by
     * the Free Software Foundation, either version 3 of the License, or
     *  (at your option) any later version.
     *
     * This program is distributed in the hope that it will be useful,
     * but WITHOUT ANY WARRANTY; without even the implied warranty of
     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
     * GNU General Public License for more details.
     *
     * @license GNU General Public License Version 3 (GPLv3)
     * @link http://www.gnu.org/licenses/
     */
     
    /**
     * PHP_VER   : 5.3+ 
     * LIBRARIES : 
     * KEYWORDS  : INTERNATIONALIZATION I18N TRANSLATION
     *             INTERNATIONALISATION I18N TRADUCTION
     * 
     * Class helping for internationalization (i18n) of texts
     * 
     * Classe fournissant un mécanisme d'internationalisation des textes
     * 
     * @package i18n
     * @version 1.0.2
     * @author Martin Lacroix
     */
    abstract class i18n {
     
       const __SELF__ = __CLASS__;
     
       /**
        * Renvoie la valeur de la constante traduite
        * Il est possible de forcer la traduction dans une langue en passant son identifiant un argument
        *    Ex: 'fr' 'en' 'de'
        * @param string $name
        * @param array $args
        * @return string|NULL
        */
       static function __callStatic($name, $args = array()) {
          # on récupère la classe propriétaire de la constante (pilotClass)
          $rc = new \ReflectionClass(static::__SELF__);
          while($rc->getParentClass()->hasConstant($name)) {
             $rc = $rc->getParentClass();
          }
          $owner = $rc->getName();
     
          if (NULL === $owner) {
             return GCT_LANG__TRANSLATION_MISSING;
          }
     
          # langue de traduction
          $lang = (empty($args)) ? GCT_LANG__USER : $args[0];
     
          if (NULL === $lang) {
             if (NULL === GCT_LANG__DEFAULT) {
                return GCT_LANG__TRANSLATION_MISSING;
             }
             $lang = GCT_LANG__DEFAULT;
          }
     
          $msg = @constant($owner . '_' . $lang . '::' . $name);
     
          # si pas de traduction trouvée, récupère la traduction avec le langage par défaut
          if ((NULL === $msg) && ($lang !== GCT_LANG__DEFAULT)) {
             $msg = @constant($owner . '_' . GCT_LANG__DEFAULT . '::' . $name);
          }
     
          return (NULL === $msg) ? GCT_LANG__TRANSLATION_MISSING : $msg;
       }
     
       /**
        * Détermine la classe qui doit exécuter la fonction en fonction de la langue
        * Langue définie par GCT_LANG__USER ou GCT_LANG__DEFAULT
        * @param string $funcName
        * @param array $params Liste ordonnée de paramètres Array([] => param)
        * @param string $lang
        * @return string|NULL
        */
       static protected function translate($funcName, array $params = array(), $lang = GCT_LANG__USER) {      
          # on récupère la classe propriétaire de la fonction statique
          $rc = new \ReflectionClass(static::__SELF__);
          while($rc->getParentClass()->hasMethod($funcName)) {
             $rc = $rc->getParentClass();
          }
          $owner = $rc->getName();
     
          if (NULL === $owner) {
             return GCT_LANG__TRANSLATION_MISSING;
          }
     
          # langue de traduction      
          if (NULL === $lang) {
             if (NULL === GCT_LANG__DEFAULT) {
                return GCT_LANG__TRANSLATION_MISSING;
             }
             $lang = GCT_LANG__DEFAULT;
          }
     
          $msg = @call_user_func_array($owner . '_' . $lang . '::' . $funcName, $params);
     
          # si pas de message en retour avec la langue courante, on réessaye avec la langue par défaut
          if ((NULL === $msg) && ($lang !== GCT_LANG__DEFAULT)) {
             $msg  = @call_user_func_array($owner . '_' . GCT_LANG__DEFAULT . '::' . $funcName, $params);
          }
     
          return (NULL === $msg) ? GCT_LANG__TRANSLATION_MISSING : $msg;
       }
    }
    ?>
    Merci bien

Discussions similaires

  1. [3D] Poser des textes 3D sur une sequence Vidéo???
    Par pointer dans le forum Développement 2D, 3D et Jeux
    Réponses: 1
    Dernier message: 11/03/2006, 09h59
  2. Creer une requéte access pour choisir des textes word
    Par mariekero dans le forum Access
    Réponses: 1
    Dernier message: 16/01/2006, 10h25
  3. recherche tuto concernant exploitation des .text en vb
    Par tavekapaclike1er dans le forum VB 6 et antérieur
    Réponses: 6
    Dernier message: 19/11/2005, 12h55
  4. StatusBar : changer la couleur d'un des textes .
    Par qi130 dans le forum Composants VCL
    Réponses: 4
    Dernier message: 30/10/2005, 09h46
  5. couleur des textes des boutons
    Par mqsi dans le forum VB 6 et antérieur
    Réponses: 6
    Dernier message: 14/10/2005, 14h59

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo