Précédent   Forum du club des développeurs et IT Pro > PHP > Langage > Contribuez
Contribuez Proposez vos articles, cours, tutoriels, FAQ, sources, etc. pour PHP
Partagez cette discussion sur d'autres réseaux sociaux : Viadeo Twitter Google Facebook Digg Delicious MySpace Yahoo
Réponse
 
Outils de la discussion
Publicité
'
Vieux 12/08/2011, 18h42   #1
rawsrc
Modérateur
 
Avatar de rawsrc
 
Homme Martin
Dev indep
Inscription : mars 2004
Messages : 2 588
Détails du profil
Informations personnelles :
Nom : Homme Martin
Âge : 36
Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

Informations professionnelles :
Activité : Dev indep

Informations forums :
Inscription : mars 2004
Messages : 2 588
Points : 6 053
Points : 6 053
Envoyer un message via Skype™ à rawsrc
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 :
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 :
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 :
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 :
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 :
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 :
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 :
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 :
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 :
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 :
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 :
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 :
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 :
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 :
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 :
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.
__________________
# Dans la Création, tout est permis mais tout n'est pas utile...
rawsrc est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 16/08/2011, 12h37   #2
Thes32
Rédacteur/Modérateur
 
Avatar de Thes32
 
Homme
Développeur PHP, .Net, T-SQL
Inscription : décembre 2006
Messages : 2 354
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 354
Points : 4 628
Points : 4 628
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
Thes32 est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 16/08/2011, 23h13   #3
kaymak
Membre Expert
 
Inscription : janvier 2007
Messages : 1 452
Détails du profil
Informations personnelles :
Âge : 28
Localisation : France, Paris (Île de France)

Informations forums :
Inscription : janvier 2007
Messages : 1 452
Points : 1 914
Points : 1 914
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+
kaymak est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 17/08/2011, 08h46   #4
grunk
Modérateur
 
Avatar de grunk
 
Homme Olivier
Développeur Web
Inscription : août 2003
Messages : 2 499
Détails du profil
Informations personnelles :
Nom : Homme Olivier
Âge : 28
Localisation : France, Côte d'Or (Bourgogne)

Informations professionnelles :
Activité : Développeur Web
Secteur : Industrie

Informations forums :
Inscription : août 2003
Messages : 2 499
Points : 5 214
Points : 5 214
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.
grunk est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 17/08/2011, 11h40   #5
rawsrc
Modérateur
 
Avatar de rawsrc
 
Homme Martin
Dev indep
Inscription : mars 2004
Messages : 2 588
Détails du profil
Informations personnelles :
Nom : Homme Martin
Âge : 36
Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

Informations professionnelles :
Activité : Dev indep

Informations forums :
Inscription : mars 2004
Messages : 2 588
Points : 6 053
Points : 6 053
Envoyer un message via Skype™ à rawsrc
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
__________________
# Dans la Création, tout est permis mais tout n'est pas utile...
rawsrc est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 19/08/2011, 18h19   #6
Benjamin Delespierre
Modérateur
 
Avatar de Benjamin Delespierre
 
Benjamin Delespierre
Développeur Web
Inscription : février 2010
Messages : 3 891
Détails du profil
Informations personnelles :
Nom : Benjamin Delespierre
Âge : 25
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 891
Points : 8 583
Points : 8 583
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 :
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 :
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.
__________________
On vous a menti
PHP, Injection de dépendances et composants
La POO en PHP en 10 minutes pour moins
Suivez-moi sur GitHub et Twitter

N'oubliez pas de vous servir des bouttons , et
Benjamin Delespierre est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 19/08/2011, 18h48   #7
stealth35
Modérateur
 
Inscription : septembre 2010
Messages : 7 958
Détails du profil
Informations forums :
Inscription : septembre 2010
Messages : 7 958
Points : 9 508
Points : 9 508
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
__________________
http://blog.stealth35.com/
stealth35 est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 20/08/2011, 16h32   #8
rawsrc
Modérateur
 
Avatar de rawsrc
 
Homme Martin
Dev indep
Inscription : mars 2004
Messages : 2 588
Détails du profil
Informations personnelles :
Nom : Homme Martin
Âge : 36
Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

Informations professionnelles :
Activité : Dev indep

Informations forums :
Inscription : mars 2004
Messages : 2 588
Points : 6 053
Points : 6 053
Envoyer un message via Skype™ à rawsrc
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 :
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
__________________
# Dans la Création, tout est permis mais tout n'est pas utile...
rawsrc est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 20/08/2011, 17h33   #9
stealth35
Modérateur
 
Inscription : septembre 2010
Messages : 7 958
Détails du profil
Informations forums :
Inscription : septembre 2010
Messages : 7 958
Points : 9 508
Points : 9 508
qu'est ce que c'est que ça :

Code :
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 ?
__________________
http://blog.stealth35.com/
stealth35 est déconnecté   Envoyer un message privé Réponse avec citation 10
Vieux 20/08/2011, 17h54   #10
rawsrc
Modérateur
 
Avatar de rawsrc
 
Homme Martin
Dev indep
Inscription : mars 2004
Messages : 2 588
Détails du profil
Informations personnelles :
Nom : Homme Martin
Âge : 36
Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

Informations professionnelles :
Activité : Dev indep

Informations forums :
Inscription : mars 2004
Messages : 2 588
Points : 6 053
Points : 6 053
Envoyer un message via Skype™ à rawsrc
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 :
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
__________________
# Dans la Création, tout est permis mais tout n'est pas utile...
rawsrc est déconnecté   Envoyer un message privé Réponse avec citation 00
Réponse
Outils de la discussion

Navigation rapide


Fuseau horaire GMT +2. Il est actuellement 07h51.


 
 
 
 
Partenaires

Hébergement Web