41382cb798a9d6e610d39db806ff683a5854c3ba
[lhc/web/www.git] / www / ecrire / inc / lang.php
1 <?php
2
3 /***************************************************************************\
4 * SPIP, Systeme de publication pour l'internet *
5 * *
6 * Copyright (c) 2001-2017 *
7 * Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James *
8 * *
9 * Ce programme est un logiciel libre distribue sous licence GNU/GPL. *
10 * Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne. *
11 \***************************************************************************/
12
13 /**
14 * Gestion des langues et choix de langue
15 *
16 * @package SPIP\Core\Langue
17 **/
18 if (!defined('_ECRIRE_INC_VERSION')) {
19 return;
20 }
21
22
23 /**
24 * Changer la langue courante
25 *
26 * Définit la langue utilisée par la langue désignée
27 * si elle fait partie des langues utilisables dans le site.
28 *
29 * Cette fonction définit les globales :
30 * spip_lang, spip_lang_rtl, spip_lang_right, spip_lang_left
31 *
32 * @param string $lang
33 * La langue à utiliser
34 * @return string|bool
35 * string : La langue qui a été utilisée si trouvée
36 * false : aucune langue ne correspondait à la demande
37 **/
38 function changer_langue($lang) {
39
40 $liste_langues = ',' . @$GLOBALS['meta']['langues_proposees']
41 . ',' . @$GLOBALS['meta']['langues_multilingue'] . ',';
42
43 // Si la langue demandee n'existe pas, on essaie d'autres variantes
44 // Exemple : 'pt-br' => 'pt_br' => 'pt'
45 $lang = str_replace('-', '_', trim($lang));
46 if (!$lang) {
47 return false;
48 }
49
50 if (strpos($liste_langues, ",$lang,") !== false
51 or ($lang = preg_replace(',_.*,', '', $lang)
52 and strpos($liste_langues, ",$lang,") !== false)
53 ) {
54
55 $GLOBALS['spip_lang_rtl'] = lang_dir($lang, '', '_rtl');
56 $GLOBALS['spip_lang_right'] = $GLOBALS['spip_lang_rtl'] ? 'left' : 'right';
57 $GLOBALS['spip_lang_left'] = $GLOBALS['spip_lang_rtl'] ? 'right' : 'left';
58
59 return $GLOBALS['spip_lang'] = $lang;
60 } else {
61 return false;
62 }
63 }
64
65 //
66 // Gestion des blocs multilingues
67 // Selection dans un tableau dont les index sont des noms de langues
68 // de la valeur associee a la langue en cours
69 // si absente, retourne le premier
70 // remarque : on pourrait aussi appeler un service de traduction externe
71 // ou permettre de choisir une langue "plus proche",
72 // par exemple le francais pour l'espagnol, l'anglais pour l'allemand, etc.
73
74 function choisir_traduction($trads, $lang = '') {
75 $k = approcher_langue($trads, $lang);
76
77 return $k ? $trads[$k] : array_shift($trads);
78 }
79
80 // retourne son 2e argument si c'est un index du premier
81 // ou un index approchant sinon et si possible,
82 // la langue X etant consideree comme une approche de X_Y
83 function approcher_langue($trads, $lang = '') {
84
85 if (!$lang) {
86 $lang = $GLOBALS['spip_lang'];
87 }
88
89 if (isset($trads[$lang])) {
90 return $lang;
91 } // cas des langues xx_yy
92 else {
93 $r = explode('_', $lang);
94 if (isset($trads[$r[0]])) {
95 return $r[0];
96 }
97 }
98
99 return '';
100 }
101
102 /**
103 * Traduit un code de langue (fr, en, etc...) vers le nom de la langue
104 * en toute lettres dans cette langue (français, English, etc....).
105 *
106 * Si le spip ne connait pas le nom de la langue, il retourne le code
107 *
108 * @param string $lang
109 * Code de langue
110 * @return string
111 * Nom de la langue, sinon son code.
112 **/
113 function traduire_nom_langue($lang) {
114 include_spip('inc/lang_liste');
115 include_spip('inc/charsets');
116
117 return html2unicode(isset($GLOBALS['codes_langues'][$lang]) ? $GLOBALS['codes_langues'][$lang] : $lang);
118 }
119
120 //
121 // Filtres de langue
122 //
123
124 // Donne la direction d'ecriture a partir de la langue. Retourne 'gaucher' si
125 // la langue est arabe, persan, kurde, dari, pachto, ourdou (langues ecrites en
126 // alphabet arabe a priori), hebreu, yiddish (langues ecrites en alphabet
127 // hebreu a priori), 'droitier' sinon.
128 // C'est utilise par #LANG_DIR, #LANG_LEFT, #LANG_RIGHT.
129 // http://code.spip.net/@lang_dir
130 function lang_dir($lang = '', $droitier = 'ltr', $gaucher = 'rtl') {
131 static $lang_rtl = array('ar', 'fa', 'ku', 'prs', 'ps', 'ur', 'he', 'heb', 'hbo', 'yi');
132
133 return in_array(($lang ? $lang : $GLOBALS['spip_lang']), $lang_rtl) ?
134 $gaucher : $droitier;
135 }
136
137 // typo francaise ou anglaise ?
138 // $lang_objet est fixee dans l'interface privee pour editer
139 // un texte anglais en interface francaise (ou l'inverse) ;
140 // sinon determiner la typo en fonction de la langue courante
141
142 // http://code.spip.net/@lang_typo
143 function lang_typo($lang = '') {
144 if (!$lang) {
145 $lang = isset($GLOBALS['lang_objet'])
146 ? $GLOBALS['lang_objet']
147 : $GLOBALS['spip_lang'];
148 }
149 if ($lang == 'eo'
150 or $lang == 'fr'
151 or strncmp($lang, 'fr_', 3) == 0
152 or $lang == 'cpf'
153 ) {
154 return 'fr';
155 } else {
156 return 'en';
157 }
158 }
159
160 // gestion de la globale $lang_objet pour que les textes soient affiches
161 // avec les memes typo et direction dans l'espace prive que dans le public
162 // http://code.spip.net/@changer_typo
163 function changer_typo($lang = '') {
164 if ($lang) {
165 $GLOBALS['lang_objet'] = $lang;
166 } else {
167 unset($GLOBALS['lang_objet']);
168 }
169 }
170
171 //
172 // Afficher un menu de selection de langue
173 // - 'var_lang_ecrire' = langue interface privee,
174 // pour var_lang' = langue de l'article, espace public, voir les squelettes
175 // pour 'changer_lang' (langue de l'article, espace prive), c'est en Ajax
176 //
177 // http://code.spip.net/@menu_langues
178 function menu_langues($nom_select, $default = '') {
179 include_spip('inc/actions');
180
181 $langues = liste_options_langues($nom_select);
182 $ret = "";
183 if (!count($langues)) {
184 return '';
185 }
186
187 if (!$default) {
188 $default = $GLOBALS['spip_lang'];
189 }
190 foreach ($langues as $l) {
191 $selected = ($l == $default) ? ' selected=\'selected\'' : '';
192 $ret .= "<option value='$l'$selected>[" . $l . "] " . traduire_nom_langue($l) . "</option>\n";
193 }
194
195 if (!test_espace_prive()) {
196 $cible = self();
197 $base = '';
198 } else {
199 $cible = self();
200 $base = spip_connect() ? 'base' : '';
201 }
202
203 $change = ' onchange="this.parentNode.parentNode.submit()"';
204
205 return generer_action_auteur('converser', $base, $cible,
206 (select_langues($nom_select, $change, $ret)
207 . "<noscript><div style='display:inline'><input type='submit' class='fondo' value='" . _T('bouton_changer') . "' /></div></noscript>"),
208 " method='post'");
209 }
210
211 // http://code.spip.net/@select_langues
212 function select_langues($nom_select, $change, $options, $label = "") {
213 static $cpt = 0;
214 $id = "menu_langues" . $cpt++;
215
216 return
217 "<label for='$id'>" . ($label ? $label : _T('info_langues')) . "</label> " .
218 "<select name='$nom_select' id='$id' "
219 . ((!test_espace_prive()) ?
220 ("class='forml menu_langues'") :
221 (($nom_select == 'var_lang_ecrire') ?
222 ("class='lang_ecrire'") :
223 "class='fondl'"))
224 . $change
225 . ">\n"
226 . $options
227 . "</select>";
228 }
229
230 /**
231 * Lister les langues disponibles
232 *
233 * Retourne un tableau de langue utilisables, triées par code de langue,
234 * mais pas le même tableau en fonction du paramètre $nom_select.
235 *
236 * @param string $nom_select
237 * Attribut name du select
238 * Selon son nom, retourne une liste différente :
239 *
240 * - var_lang ou changer_lang :
241 * liste des langues sélectionnées dans la config multilinguisme
242 * - var_lang_ecrire :
243 * toutes les langues présentes en fichier de langue
244 * @return array
245 * Liste des langues
246 */
247 function liste_options_langues($nom_select) {
248
249 switch ($nom_select) {
250 # #MENU_LANG
251 case 'var_lang':
252 # menu de changement de la langue d'un article
253 # les langues selectionnees dans la configuration "multilinguisme"
254 case 'changer_lang':
255 $langues = explode(',', $GLOBALS['meta']['langues_multilingue']);
256 break;
257 # menu de l'interface (privee, installation et panneau de login)
258 # les langues presentes sous forme de fichiers de langue
259 # on force la relecture du repertoire des langues pour etre synchrone.
260 case 'var_lang_ecrire':
261 default:
262 $GLOBALS['meta']['langues_proposees'] = '';
263 init_langues();
264 $langues = explode(',', $GLOBALS['meta']['langues_proposees']);
265 break;
266
267 # dernier choix possible : toutes les langues = langues_proposees
268 # + langues_multilingues ; mais, ne sert pas
269 # $langues = explode(',', $GLOBALS['all_langs']);
270 }
271 if (count($langues) <= 1) {
272 return array();
273 }
274 sort($langues);
275
276 return $langues;
277 }
278
279
280 /**
281 * Redirige sur la bonne langue lorsque l'option forcer_lang est active
282 *
283 * Cette fonction est appelee depuis ecrire/public.php si on a installé
284 * la variable de personnalisation $forcer_lang ; elle renvoie le brouteur
285 * si necessaire vers l'URL xxxx?lang=ll
286 *
287 * @return void
288 **/
289 function verifier_lang_url() {
290
291 // quelle langue est demandee ?
292 $lang_demandee = (test_espace_prive() ? $GLOBALS['spip_lang'] : $GLOBALS['meta']['langue_site']);
293 if (isset($_COOKIE['spip_lang_ecrire'])) {
294 $lang_demandee = $_COOKIE['spip_lang_ecrire'];
295 }
296 if (!test_espace_prive() and isset($_COOKIE['spip_lang'])) {
297 $lang_demandee = $_COOKIE['spip_lang'];
298 }
299 if (isset($_GET['lang'])) {
300 $lang_demandee = $_GET['lang'];
301 }
302
303 // Renvoyer si besoin (et si la langue demandee existe)
304 if ($GLOBALS['spip_lang'] != $lang_demandee
305 and changer_langue($lang_demandee)
306 and $lang_demandee != @$_GET['lang']
307 ) {
308 $destination = parametre_url(self(), 'lang', $lang_demandee, '&');
309 // ici on a besoin des var_truc
310 foreach ($_GET as $var => $val) {
311 if (!strncmp('var_', $var, 4)) {
312 $destination = parametre_url($destination, $var, $val, '&');
313 }
314 }
315 include_spip('inc/headers');
316 redirige_par_entete($destination);
317 }
318
319 // Subtilite : si la langue demandee par cookie est la bonne
320 // alors on fait comme si $lang etait passee dans l'URL
321 // (pour criteres {lang}).
322 $GLOBALS['lang'] = $_GET['lang'] = $GLOBALS['spip_lang'];
323 }
324
325
326 /**
327 * Utilise la langue du site
328 *
329 * Change la langue en cours d'utilisation par la langue du site
330 * si ce n'est pas déjà le cas.
331 *
332 * Note : Cette fonction initialise la globale spip_lang au chargement de inc/lang
333 *
334 * @return string
335 * La langue sélectionnée
336 **/
337 function utiliser_langue_site() {
338 // s'il existe une langue du site (en gros tout le temps en théorie)
339 if (isset($GLOBALS['meta']['langue_site'])
340 // et si spip_langue est pas encore définie (ce que va faire changer_langue())
341 // ou qu'elle n'est pas identique à la langue du site
342 and (!isset($GLOBALS['spip_lang'])
343 or $GLOBALS['spip_lang'] != $GLOBALS['meta']['langue_site'])
344 ) {
345 return changer_langue($GLOBALS['meta']['langue_site']);//@:install
346 }
347 // en theorie là, la globale est définie, sinon c'est un problème.
348 if (!isset($GLOBALS['spip_lang'])) {
349 spip_log("La globale spip_lang est indéfinie dans utiliser_langue_site() !", _LOG_ERREUR);
350 }
351
352 return $GLOBALS['spip_lang'];
353 }
354
355 /**
356 * Initialise la langue pour un visiteur du site
357 *
358 * La langue est choisie dans cet ordre :
359 * - Dans le cookie 'spip_lang' ou 'spip_lang_ecrire' s'il existe (selon l'espace public ou privé).
360 * - Sinon dans la session du visiteur.
361 * - Sinon dans une des langues définie en préférence du navigateur
362 * - Sinon la langue du site
363 *
364 * @return string
365 * La langue utilisée
366 **/
367 function utiliser_langue_visiteur() {
368
369 $l = (!test_espace_prive() ? 'spip_lang' : 'spip_lang_ecrire');
370 if (isset($_COOKIE[$l])) {
371 if (changer_langue($l = $_COOKIE[$l])) {
372 return $l;
373 }
374 }
375
376 if (isset($GLOBALS['visiteur_session']['lang'])) {
377 if (changer_langue($l = $GLOBALS['visiteur_session']['lang'])) {
378 return $l;
379 }
380 }
381
382 if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
383 foreach (explode(',', $_SERVER['HTTP_ACCEPT_LANGUAGE']) as $s) {
384 if (preg_match('#^([a-z]{2,3})(-[a-z]{2,3})?(;q=[0-9.]+)?$#i', trim($s), $r)) {
385 if (changer_langue($l = strtolower($r[1]))) {
386 return $l;
387 }
388 }
389 }
390 }
391
392 return utiliser_langue_site();
393 }
394
395
396 /**
397 * Verifier qu'une chaine suceptible d'etre un nom de langue a le bon format
398 * @param string $chaine
399 * @return int
400 */
401 function match_langue($chaine) {
402 return preg_match('/^[a-z]{2,3}(_[a-z]{2,3}){0,2}$/', $chaine);
403 }
404
405 /**
406 * Initialisation des listes de langues
407 *
408 * Initialise les métas :
409 * - langues_proposees : liste des traductions disponibles
410 * - langue_site : langue par défaut du site
411 *
412 * Lorsque ces métas n'existent pas encore (c'est à dire à l'installation),
413 * elles sont calculées en obtenant la liste des langues
414 * dans les fichiers de lang
415 *
416 * @return void
417 **/
418 function init_langues() {
419
420 // liste des langues dans les meta, sauf a l'install
421 $all_langs = @$GLOBALS['meta']['langues_proposees'];
422
423 $tout = array();
424 if (!$all_langs) {
425 // trouver tous les modules lang/spip_xx.php
426 $modules = find_all_in_path("lang/", "/spip_([a-z_]+)\.php$");
427 foreach ($modules as $name => $path) {
428 if (preg_match(',^spip_([a-z_]+)\.php$,', $name, $regs)) {
429 if (match_langue($regs[1])) {
430 $tout[] = $regs[1];
431 }
432 }
433 }
434 sort($tout);
435 $tout = join(',', $tout);
436 // Si les langues n'ont pas change, ne rien faire
437 if ($tout != $all_langs) {
438 $GLOBALS['meta']['langues_proposees'] = $tout;
439 include_spip('inc/meta');
440 ecrire_meta('langues_proposees', $tout);
441 }
442 }
443 if (!isset($GLOBALS['meta']['langue_site'])) {
444 // Initialisation : le francais si dispo, sinon la premiere langue trouvee
445 $GLOBALS['meta']['langue_site'] = $tout =
446 (!$all_langs or (strpos(',' . _LANGUE_PAR_DEFAUT . ',', ",$all_langs,") !== false))
447 ? _LANGUE_PAR_DEFAUT : substr($all_langs, 0, strpos($all_langs, ','));
448 ecrire_meta('langue_site', $tout);
449 }
450 }
451
452 /**
453 * Retourne une balise <html>
454 *
455 * Retourne une balise HTML contenant les attributs 'lang' et 'dir'
456 * définis sur la langue en cours d'utilisation,
457 * ainsi que des classes CSS de ces du nom de la langue et direction choisie.
458 *
459 * @return string
460 * Code html de la balise <html>
461 **/
462 function html_lang_attributes() {
463 $lang = $GLOBALS['spip_lang'];
464 $dir = ($GLOBALS['spip_lang_rtl'] ? 'rtl' : 'ltr');
465
466 return "<html class='$dir $lang no-js' xmlns='http://www.w3.org/1999/xhtml' lang='$lang' dir='$dir'>\n";
467 }
468
469
470 /**
471 * Calcul de la direction du texte et la mise en page selon la langue
472 *
473 * En hébreu le ? ne doit pas être inversé.
474 *
475 * @param string $spip_lang
476 * @param string $spip_lang_rtl
477 * @return string
478 */
479 function aide_lang_dir($spip_lang, $spip_lang_rtl) {
480 return ($spip_lang <> 'he') ? $spip_lang_rtl : '';
481 }
482
483
484 // initialise les globales (liste des langue, langue du site, spip_lang...)
485 init_langues();
486 utiliser_langue_site();