[SPIP][PLUGINS] v3.0-->v3.2
[lhc/web/www.git] / www / ecrire / inc / traduire.php
index 861bce1..2e4c06f 100644 (file)
 /***************************************************************************\
  *  SPIP, Systeme de publication pour l'internet                           *
  *                                                                         *
- *  Copyright (c) 2001-2016                                                *
+ *  Copyright (c) 2001-2017                                                *
  *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
  *                                                                         *
  *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
  *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
 \***************************************************************************/
 
-if (!defined('_ECRIRE_INC_VERSION')) return;
+/**
+ * Outils pour la traduction et recherche de traductions
+ *
+ * @package SPIP\Core\Traductions
+ **/
+
+if (!defined('_ECRIRE_INC_VERSION')) {
+       return;
+}
 
 /**
  * Rechercher tous les lang/file dans le path
- * qui seront ensuite charges dans l'ordre du path
- * version dediee et optimisee pour cet usage de find_in_path
+ * qui seront ensuite chargés dans l'ordre du path
  *
- * @staticvar <type> $dirs
- * @param <type> $file
- * @param <type> $dirname
- * @return <type>
+ * Version dédiée et optimisée pour cet usage de find_in_path
+ *
+ * @see find_in_path()
+ *
+ * @staticvar array $dirs
+ *
+ * @param string $file
+ *     Nom du fichier cherché, tel que `mots_fr.php`
+ * @param string $dirname
+ *     Nom du répertoire de recherche
+ * @return array
+ *     Liste des fichiers de langue trouvés, dans l'ordre des chemins
  */
-function find_langs_in_path ($file, $dirname='lang') {
-       static $dirs=array();
+function find_langs_in_path($file, $dirname = 'lang') {
+       static $dirs = array();
        $liste = array();
-       foreach(creer_chemin() as $dir) {
-               if (!isset($dirs[$a = $dir . $dirname]))
-                       $dirs[$a] = (is_dir($a) || !$a) ;
+       foreach (creer_chemin() as $dir) {
+               if (!isset($dirs[$a = $dir . $dirname])) {
+                       $dirs[$a] = (is_dir($a) || !$a);
+               }
                if ($dirs[$a]) {
                        if (is_readable($a .= $file)) {
                                $liste[] = $a;
                        }
                }
        }
+
        return array_reverse($liste);
 }
-//
-// Charger un fichier langue
-//
-// http://doc.spip.org/@chercher_module_lang
+
+/**
+ * Recherche le ou les fichiers de langue d'un module de langue
+ *
+ * @param string $module
+ *     Nom du module de langue, tel que `mots` ou `ecrire`
+ * @param string $lang
+ *     Langue dont on veut obtenir les traductions.
+ *     Paramètre optionnel uniquement si le module est `local`
+ * @return array
+ *     Liste des fichiers touvés pour ce module et cette langue.
+ **/
 function chercher_module_lang($module, $lang = '') {
-       if ($lang)
-               $lang = '_'.$lang;
+       if ($lang) {
+               $lang = '_' . $lang;
+       }
 
        // 1) dans un repertoire nomme lang/ se trouvant sur le chemin
        if ($f = ($module == 'local'
-               ? find_in_path($module.$lang.'.php', 'lang/')
-               : find_langs_in_path($module.$lang.'.php', 'lang/')))
-               return is_array($f)?$f:array($f);
+               ? find_in_path($module . $lang . '.php', 'lang/')
+               : find_langs_in_path($module . $lang . '.php', 'lang/'))
+       ) {
+               return is_array($f) ? $f : array($f);
+       }
 
        // 2) directement dans le chemin (old style, uniquement pour local)
-       return (($module == 'local') OR strpos($module, '/'))
-               ? (($f = find_in_path($module.$lang. '.php')) ? array($f):false)
+       return (($module == 'local') or strpos($module, '/'))
+               ? (($f = find_in_path($module . $lang . '.php')) ? array($f) : false)
                : false;
 }
 
-// http://doc.spip.org/@charger_langue
+/**
+ * Charge en mémoire les couples cle/traduction d'un module de langue
+ * et une langue donnée
+ *
+ * Interprête un fichier de langue pour le module et la langue désignée
+ * s'il existe, et sinon se rabat soit sur la langue principale du site
+ * (définie par la meta `langue_site`), soit sur le français.
+ *
+ * Définit la globale `idx_lang` qui sert à la lecture du fichier de langue
+ * (include) et aux surcharges via `surcharger_langue()`
+ *
+ * @uses chercher_module_lang()
+ * @uses surcharger_langue()
+ *
+ * @param string $lang Code de langue
+ * @param string $module Nom du module de langue
+ * @return string Langue du module chargé, sinon chaîne vide.
+ **/
 function charger_langue($lang, $module = 'spip') {
-       if ($lang AND $fichiers_lang = chercher_module_lang($module, $lang)) {
-               $GLOBALS['idx_lang']='i18n_'.$module.'_'.$lang;
+       $var = 'i18n_' . $module . '_' . $lang;
+       if ($lang and $fichiers_lang = chercher_module_lang($module, $lang)) {
+               $GLOBALS['idx_lang'] = $var;
                include(array_shift($fichiers_lang));
                surcharger_langue($fichiers_lang);
+               $GLOBALS['lang_' . $var] = $lang;
        } else {
                // si le fichier de langue du module n'existe pas, on se rabat sur
                // la langue par defaut du site -- et au pire sur le francais, qui
                // *par definition* doit exister, et on copie le tableau dans la
                // var liee a la langue
                $l = $GLOBALS['meta']['langue_site'];
-               if (!$fichiers_lang = chercher_module_lang($module, $l))
-                       $fichiers_lang = chercher_module_lang($module, _LANGUE_PAR_DEFAUT);
+               if (!$fichiers_lang = chercher_module_lang($module, $l)) {
+                       $l = _LANGUE_PAR_DEFAUT;
+                       $fichiers_lang = chercher_module_lang($module, $l);
+               }
 
                if ($fichiers_lang) {
-                       $GLOBALS['idx_lang']='i18n_'.$module.'_' .$l;
+                       $GLOBALS['idx_lang'] = 'i18n_' . $module . '_' . $l;
                        include(array_shift($fichiers_lang));
                        surcharger_langue($fichiers_lang);
-                       $GLOBALS['i18n_'.$module.'_'.$lang]
-                               = &$GLOBALS['i18n_'.$module.'_'.$l];
+                       $GLOBALS[$var] = &$GLOBALS['i18n_' . $module . '_' . $l];
+                       $GLOBALS['lang_' . $var] = $l;
                        #spip_log("module de langue : ${module}_$l.php");
                }
        }
 }
 
-//
-// Surcharger le fichier de langue courant avec un ou plusieurs autre (tordu, hein...)
-//
-// http://doc.spip.org/@surcharger_langue
+/**
+ * Surcharger le fichier de langue courant avec un ou plusieurs autres
+ *
+ * Charge chaque fichier de langue dont les chemins sont transmis et
+ * surcharge les infos de cette langue/module déjà connues par les nouvelles
+ * données chargées. Seule les clés nouvelles ou modifiées par la
+ * surcharge sont impactées (les clés non présentes dans la surcharge
+ * ne sont pas supprimées !).
+ *
+ * La fonction suppose la présence de la globale `idx_lang` indiquant
+ * la destination des couples de traduction, de la forme
+ * `i18n_${module}_${lang}`
+ *
+ * @param array $fichiers
+ *    Liste des chemins de fichiers de langue à surcharger.
+ **/
 function surcharger_langue($fichiers) {
        static $surcharges = array();
-       if (!isset($GLOBALS['idx_lang'])) return;
+       if (!isset($GLOBALS['idx_lang'])) {
+               return;
+       }
 
-       if (!is_array($fichiers)) $fichiers = array($fichiers);
-       if (!count($fichiers)) return;
-       foreach($fichiers as $fichier){
+       if (!is_array($fichiers)) {
+               $fichiers = array($fichiers);
+       }
+       if (!count($fichiers)) {
+               return;
+       }
+       foreach ($fichiers as $fichier) {
                if (!isset($surcharges[$fichier])) {
                        $idx_lang_normal = $GLOBALS['idx_lang'];
-                       $GLOBALS['idx_lang'] = $GLOBALS['idx_lang'].'@temporaire';
+                       $GLOBALS['idx_lang'] = $GLOBALS['idx_lang'] . '@temporaire';
                        include($fichier);
                        $surcharges[$fichier] = $GLOBALS[$GLOBALS['idx_lang']];
-                       unset ($GLOBALS[$GLOBALS['idx_lang']]);
+                       unset($GLOBALS[$GLOBALS['idx_lang']]);
                        $GLOBALS['idx_lang'] = $idx_lang_normal;
                }
                if (is_array($surcharges[$fichier])) {
                        $GLOBALS[$GLOBALS['idx_lang']] = array_merge(
-                               (array)$GLOBALS[$GLOBALS['idx_lang']],
+                               (isset($GLOBALS[$GLOBALS['idx_lang']]) ? (array)$GLOBALS[$GLOBALS['idx_lang']] : array()),
                                $surcharges[$fichier]
                        );
                }
        }
 }
 
-//
-// Traduire une chaine internationalisee
-//
-// http://doc.spip.org/@inc_traduire_dist
-function inc_traduire_dist($ori, $lang) {
+
+
+class SPIP_Traductions_Description {
+       /** @var string code de langue (hors module) */
+       public $code;
+       /** @var string nom du module de langue */
+       public $module;
+       /** @var string langue de la traduction */
+       public $langue;
+       /** @var string traduction */
+       public $texte;
+       /** @var string var mode particulier appliqué ? */
+       public $mode;
+       /** @var bool Corrections des textes appliqué ? */
+       public $corrections = false;
+}
+
+
+/**
+ * Traduire une chaine internationalisée
+ *
+ * Lorsque la langue demandée n'a pas de traduction pour la clé de langue
+ * transmise, la fonction cherche alors la traduction dans la langue
+ * principale du site (défini par la meta `langue_site`), puis, à défaut
+ * dans la langue française.
+ *
+ * Les traductions sont cherchées dans les modules de langue indiqués.
+ * Par exemple le module `mots` dans la clé `mots:titre_mot`, pour une
+ * traduction `es` (espagnol) provoquera une recherche dans tous les fichiers
+ * `lang\mots_es.php`.
+ *
+ * Des surcharges locales peuvent être présentes également
+ * dans les fichiers `lang/local_es.php` ou `lang/local.php`
+ *
+ * @note
+ *   Les couples clé/traductions déjà connus sont sauvés en interne
+ *   dans les globales `i18n_${module}_${lang}` tel que `i18n_mots_es`
+ *   et sont également sauvés dans la variable statique `deja_vu`
+ *   de cette fonction.
+ *
+ * @uses charger_langue()
+ * @uses chercher_module_lang()
+ * @uses surcharger_langue()
+ *
+ * @param string $ori
+ *     Clé de traduction, tel que `bouton_enregistrer` ou `mots:titre_mot`
+ * @param string $lang
+ *     Code de langue, la traduction doit se faire si possible dans cette langue
+ * @param bool $raw
+ *     - false : retourne le texte (par défaut)
+ *     - true  : retourne une description de la chaine de langue (module, texte, langue)
+ * @return string|array
+ *     - string : Traduction demandée. Chaîne vide si aucune traduction trouvée.
+ *     - SPIP_Traductions_Description : traduction et description (texte, module, langue)
+ **/
+function inc_traduire_dist($ori, $lang, $raw = false) {
        static $deja_vu = array();
        static $local = array();
-  
-       if (isset($deja_vu[$lang][$ori]) AND (_request('var_mode') != 'traduction'))
-               return $deja_vu[$lang][$ori];
+
+       if (isset($deja_vu[$lang][$ori]) and (_request('var_mode') != 'traduction')) {
+               return $raw ? $deja_vu[$lang][$ori] : $deja_vu[$lang][$ori]->texte;
+       }
 
        // modules demandes explicitement <xxx|yyy|zzz:code> cf MODULES_IDIOMES
-       if (strpos($ori,':')) {
-               list($modules,$code) = explode(':',$ori,2);
+       if (strpos($ori, ':')) {
+               list($modules, $code) = explode(':', $ori, 2);
                $modules = explode('|', $modules);
                $ori_complet = $ori;
        } else {
@@ -132,78 +251,103 @@ function inc_traduire_dist($ori, $lang) {
                $ori_complet = implode('|', $modules) . ':' . $ori;
        }
 
-       $text = '';
+       $desc = new SPIP_Traductions_Description();
+
        // parcourir tous les modules jusqu'a ce qu'on trouve
        foreach ($modules as $module) {
-               $var = "i18n_".$module."_".$lang;
+               $var = "i18n_" . $module . "_" . $lang;
 
                if (empty($GLOBALS[$var])) {
                        charger_langue($lang, $module);
-
-                       // surcharge perso
-                       // -- on cherche (lang/)local_xx.php ...
-                       if (!isset($local['local_'.$lang])) {
+                       // surcharges persos -- on cherche
+                       // (lang/)local_xx.php et/ou (lang/)local.php ...
+                       if (!isset($local['local_' . $lang])) {
                                // redéfinir la langue en cours pour les surcharges (chercher_langue a pu le changer)
-                               $GLOBALS['idx_lang']= $var;
-                               $local['local_'.$lang] = chercher_module_lang('local', $lang);
+                               $GLOBALS['idx_lang'] = $var;
+                               // ... (lang/)local_xx.php
+                               $local['local_' . $lang] = chercher_module_lang('local', $lang);
+                       }
+                       if ($local['local_' . $lang]) {
+                               surcharger_langue($local['local_' . $lang]);
                        }
-                       if ($local['local_'.$lang])
-                               surcharger_langue($local['local_'.$lang]);
                        // ... puis (lang/)local.php
-                       if (!isset($local['local']))
+                       if (!isset($local['local'])) {
                                $local['local'] = chercher_module_lang('local');
-                       if ($local['local'])
+                       }
+                       if ($local['local']) {
                                surcharger_langue($local['local']);
+                       }
                }
 
                if (isset($GLOBALS[$var][$code])) {
-                       $module_retenu = $module;
-                       $text = $GLOBALS[$var][$code];
+                       $desc->code = $code;
+                       $desc->module = $module;
+                       $desc->langue = $GLOBALS['lang_' . $var];
+                       $desc->texte = $GLOBALS[$var][$code];
                        break;
                }
        }
 
-       // Retour aux sources si la chaine est absente dans la langue cible ;
-       // on essaie d'abord la langue du site, puis a defaut la langue fr
-       $langue_retenue = $lang;
-       if (!strlen($text)
-       AND $lang !== _LANGUE_PAR_DEFAUT) {
-               if ($lang !== $GLOBALS['meta']['langue_site']) {
-                       $text = inc_traduire_dist($ori, $GLOBALS['meta']['langue_site']);
-                       $langue_retenue = (!strlen($text) ? $GLOBALS['meta']['langue_site'] : '');
-               }
-               else {
-                       $text = inc_traduire_dist($ori, _LANGUE_PAR_DEFAUT);
-                       $langue_retenue = (!strlen($text) ? _LANGUE_PAR_DEFAUT : '');
+       if (!$desc->corrections) {
+               $desc->corrections = true;
+               // Retour aux sources si la chaine est absente dans la langue cible ;
+               // on essaie d'abord la langue du site, puis a defaut la langue fr
+               if (!strlen($desc->texte) and $lang !== _LANGUE_PAR_DEFAUT) {
+                       if ($lang !== $GLOBALS['meta']['langue_site']) {
+                               $desc = inc_traduire_dist($ori, $GLOBALS['meta']['langue_site'], true);
+                       } else {
+                               $desc = inc_traduire_dist($ori, _LANGUE_PAR_DEFAUT, true);
+                       }
                }
-       }
 
-       // Supprimer la mention <NEW> ou <MODIF>
-       if (substr($text,0,1) === '<')
-               $text = str_replace(array('<NEW>', '<MODIF>'), array(), $text);
+               // Supprimer la mention <NEW> ou <MODIF>
+               if (substr($desc->texte, 0, 1) === '<') {
+                       $desc->texte = str_replace(array('<NEW>', '<MODIF>'), array(), $desc->texte);
+               }
 
-       // Si on n'est pas en utf-8, la chaine peut l'etre...
-       // le cas echeant on la convertit en entites html &#xxx;
-       if ($GLOBALS['meta']['charset'] !== 'utf-8'
-       AND preg_match(',[\x7f-\xff],S', $text)) {
-               include_spip('inc/charsets');
-               $text = charset2unicode($text,'utf-8');
+               // Si on n'est pas en utf-8, la chaine peut l'etre...
+               // le cas echeant on la convertit en entites html &#xxx;
+               if ((!isset($GLOBALS['meta']['charset']) or $GLOBALS['meta']['charset'] !== 'utf-8')
+                       and preg_match(',[\x7f-\xff],S', $desc->texte)
+               ) {
+                       include_spip('inc/charsets');
+                       $desc->texte = charset2unicode($desc->texte, 'utf-8');
+               }
        }
 
        if (_request('var_mode') == 'traduction') {
-               if ($text)  {
-                       $classe = 'debug-traduction' . ($module_retenu == 'ecrire' ? '-prive' : '');
-                       $text = '<span lang=' . $langue_retenue . ' class=' . $classe . ' title=' . $ori_complet . '(' . $langue_retenue . ')>' . $text . '</span>';
-                       $text = str_replace(
-                                               array("$module_retenu:", "$module_retenu|"),
-                                               array("*$module_retenu*:", "*$module_retenu*|"),
-                                               $text);
-               }
-       }
-       else {
-               $deja_vu[$lang][$ori] = $text;
+               $desc = definir_details_traduction($desc, $ori_complet);
+       } else {
+               $deja_vu[$lang][$ori] = $desc;
        }
 
-       return $text;
+       return $raw ? $desc : $desc->texte;
 }
-?>
\ No newline at end of file
+
+/**
+ * Modifie le texte de traduction pour indiquer des éléments
+ * servant au debug de celles-ci. (pour var_mode=traduction)
+ *
+ * @param SPIP_Traductions_Description $desc
+ * @param string $modules Les modules qui étaient demandés
+ * @return SPIP_Traductions_Description
+ */
+function definir_details_traduction($desc, $modules) {
+       if (!$desc->mode and $desc->texte) {
+               // ne pas modifier 2 fois l'affichage
+               $desc->mode = 'traduction';
+               $classe = 'debug-traduction' . ($desc->module == 'ecrire' ? '-prive' : '');
+               $desc->texte = '<span '
+                       . 'lang=' . $desc->langue
+                       . ' class=' . $classe
+                       . ' title=' . $modules . '(' . $desc->langue . ')>'
+                       . $desc->texte
+                       . '</span>';
+               $desc->texte = str_replace(
+                       array("$desc->module:", "$desc->module|"),
+                       array("*$desc->module*:", "*$desc->module*|"),
+                       $desc->texte
+               );
+       }
+       return $desc;
+}
\ No newline at end of file