dolibarr  13.0.2
translate.class.php
Go to the documentation of this file.
1 <?php
2 /* Copyright (C) 2001 Eric Seigne <erics@rycks.com>
3  * Copyright (C) 2004-2015 Destailleur Laurent <eldy@users.sourceforge.net>
4  * Copyright (C) 2005-2010 Regis Houssin <regis.houssin@inodbox.com>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 3 of the License, or
9  * any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program. If not, see <https://www.gnu.org/licenses/>.
18  */
19 
30 class Translate
31 {
32  public $dir; // Directories that contains /langs subdirectory
33 
34  public $defaultlang; // Current language for current user
35  public $shortlang; // Short language for current user
36  public $charset_output = 'UTF-8'; // Codage used by "trans" method outputs
37 
38  public $tab_translate = array(); // Array of all translations key=>value
39  private $_tab_loaded = array(); // Array to store result after loading each language file
40 
41  public $cache_labels = array(); // Cache for labels return by getLabelFromKey method
42  public $cache_currencies = array(); // Cache to store currency symbols
43  private $cache_currencies_all_loaded = false;
44 
45 
52  public function __construct($dir, $conf)
53  {
54  if (!empty($conf->file->character_set_client)) $this->charset_output = $conf->file->character_set_client; // If charset output is forced
55  if ($dir) $this->dir = array($dir);
56  else $this->dir = $conf->file->dol_document_root;
57  }
58 
59 
66  public function setDefaultLang($srclang = 'en_US')
67  {
68  global $conf;
69 
70  //dol_syslog(get_class($this)."::setDefaultLang srclang=".$srclang,LOG_DEBUG);
71 
72  // If a module ask to force a priority on langs directories (to use its own lang files)
73  if (!empty($conf->global->MAIN_FORCELANGDIR))
74  {
75  $more = array();
76  $i = 0;
77  foreach ($conf->file->dol_document_root as $dir)
78  {
79  $newdir = $dir.$conf->global->MAIN_FORCELANGDIR; // For example $conf->global->MAIN_FORCELANGDIR is '/mymodule' meaning we search files into '/mymodule/langs/xx_XX'
80  if (!in_array($newdir, $this->dir))
81  {
82  $more['module_'.$i] = $newdir; $i++; // We add the forced dir into the array $more. Just after, we add entries into $more to list of lang dir $this->dir.
83  }
84  }
85  $this->dir = array_merge($more, $this->dir); // Forced dir ($more) are before standard dirs ($this->dir)
86  }
87 
88  $this->origlang = $srclang;
89 
90  if (empty($srclang) || $srclang == 'auto')
91  {
92  // $_SERVER['HTTP_ACCEPT_LANGUAGE'] can be 'fr-FR,fr;q=0.9,en-US;q=0.8,en;q=0.7,it;q=0.6' but can contains also malicious content
93  $langpref = empty($_SERVER['HTTP_ACCEPT_LANGUAGE']) ? '' : $_SERVER['HTTP_ACCEPT_LANGUAGE'];
94  $langpref = preg_replace("/;([^,]*)/i", "", $langpref); // Remove the 'q=x.y,' part
95  $langpref = str_replace("-", "_", $langpref);
96  $langlist = preg_split("/[;,]/", $langpref);
97  $codetouse = preg_replace('/[^_a-zA-Z]/', '', $langlist[0]);
98  } else $codetouse = $srclang;
99 
100  // We redefine $srclang
101  $langpart = explode("_", $codetouse);
102  //print "Short code before _ : ".$langpart[0].' / Short code after _ : '.$langpart[1].'<br>';
103  if (!empty($langpart[1])) // If it's for a codetouse that is a long code xx_YY
104  {
105  // Array force long code from first part, even if long code is defined
106  $longforshort = array('ar'=>'ar_SA');
107  $longforshortexcep = array('ar_EG');
108  if (isset($longforshort[strtolower($langpart[0])]) && !in_array($codetouse, $longforshortexcep)) $srclang = $longforshort[strtolower($langpart[0])];
109  elseif (!is_numeric($langpart[1])) { // Second part YY may be a numeric with some Chrome browser
110  $srclang = strtolower($langpart[0])."_".strtoupper($langpart[1]);
111  $longforlong = array('no_nb'=>'nb_NO');
112  if (isset($longforlong[strtolower($srclang)])) $srclang = $longforlong[strtolower($srclang)];
113  } else $srclang = strtolower($langpart[0])."_".strtoupper($langpart[0]);
114  } else { // If it's for a codetouse that is a short code xx
115  // Array to convert short lang code into long code.
116  $longforshort = array(
117  'am'=>'am_ET', 'ar'=>'ar_SA', 'bn'=>'bn_DB', 'el'=>'el_GR', 'ca'=>'ca_ES', 'cs'=>'cs_CZ', 'en'=>'en_US', 'fa'=>'fa_IR',
118  'gl'=>'gl_ES', 'he'=>'he_IL', 'hi'=>'hi_IN', 'ja'=>'ja_JP',
119  'ka'=>'ka_GE', 'km'=>'km_KH', 'kn'=>'kn_IN', 'ko'=>'ko_KR', 'lo'=>'lo_LA', 'nb'=>'nb_NO', 'no'=>'nb_NO', 'ne'=>'ne_NP',
120  'sl'=>'sl_SI', 'sq'=>'sq_AL', 'sr'=>'sr_RS', 'sv'=>'sv_SE', 'uk'=>'uk_UA', 'vi'=>'vi_VN', 'zh'=>'zh_CN'
121  );
122  if (isset($longforshort[strtolower($langpart[0])])) $srclang = $longforshort[strtolower($langpart[0])];
123  elseif (!empty($langpart[0])) $srclang = strtolower($langpart[0])."_".strtoupper($langpart[0]);
124  else $srclang = 'en_US';
125  }
126 
127  $this->defaultlang = $srclang;
128  $this->shortlang = substr($srclang, 0, 2);
129  //print 'this->defaultlang='.$this->defaultlang;
130  }
131 
132 
140  public function getDefaultLang($mode = 0)
141  {
142  if (empty($mode)) return $this->defaultlang;
143  else return substr($this->defaultlang, 0, 2);
144  }
145 
146 
153  public function loadLangs($domains)
154  {
155  foreach ($domains as $domain)
156  {
157  $this->load($domain);
158  }
159  }
160 
182  public function load($domain, $alt = 0, $stopafterdirection = 0, $forcelangdir = '', $loadfromfileonly = 0, $forceloadifalreadynotfound = 0)
183  {
184  global $conf, $db;
185 
186  //dol_syslog("Translate::Load Start domain=".$domain." alt=".$alt." forcelangdir=".$forcelangdir." this->defaultlang=".$this->defaultlang);
187 
188  // Check parameters
189  if (empty($domain))
190  {
191  dol_print_error('', get_class($this)."::Load ErrorWrongParameters");
192  return -1;
193  }
194  if ($this->defaultlang == 'none_NONE') return 0; // Special language code to not translate keys
195 
196 
197  // Load $this->tab_translate[] from database
198  if (empty($loadfromfileonly) && count($this->tab_translate) == 0) $this->loadFromDatabase($db); // No translation was never loaded yet, so we load database.
199 
200 
201  $newdomain = $domain;
202  $modulename = '';
203 
204  // Search if a module directory name is provided into lang file name
205  $regs = array();
206  if (preg_match('/^([^@]+)@([^@]+)$/i', $domain, $regs))
207  {
208  $newdomain = $regs[1];
209  $modulename = $regs[2];
210  }
211 
212  // Check cache
213  if (!empty($this->_tab_loaded[$newdomain])
214  && ($this->_tab_loaded[$newdomain] != 2 || empty($forceloadifalreadynotfound))) // File already loaded and found and not forced for this domain
215  {
216  //dol_syslog("Translate::Load already loaded for newdomain=".$newdomain);
217  return 0;
218  }
219 
220  $fileread = 0;
221  $langofdir = (empty($forcelangdir) ? $this->defaultlang : $forcelangdir);
222 
223  // Redefine alt
224  $langarray = explode('_', $langofdir);
225  if ($alt < 1 && isset($langarray[1]) && (strtolower($langarray[0]) == strtolower($langarray[1]) || in_array(strtolower($langofdir), array('el_gr')))) $alt = 1;
226  if ($alt < 2 && strtolower($langofdir) == 'en_us') $alt = 2;
227 
228  if (empty($langofdir)) // This may occurs when load is called without setting the language and without providing a value for forcelangdir
229  {
230  dol_syslog("Error: ".get_class($this)."::load was called for domain=".$domain." but language was not set yet with langs->setDefaultLang(). Nothing will be loaded.", LOG_WARNING);
231  return -1;
232  }
233 
234  foreach ($this->dir as $searchdir)
235  {
236  // Directory of translation files
237  $file_lang = $searchdir.($modulename ? '/'.$modulename : '')."/langs/".$langofdir."/".$newdomain.".lang";
238  $file_lang_osencoded = dol_osencode($file_lang);
239 
240  $filelangexists = is_file($file_lang_osencoded);
241 
242  //dol_syslog(get_class($this).'::Load Try to read for alt='.$alt.' langofdir='.$langofdir.' domain='.$domain.' newdomain='.$newdomain.' modulename='.$modulename.' file_lang='.$file_lang." => filelangexists=".$filelangexists);
243  //print 'Try to read for alt='.$alt.' langofdir='.$langofdir.' domain='.$domain.' newdomain='.$newdomain.' modulename='.$modulename.' this->_tab_loaded[newdomain]='.$this->_tab_loaded[$newdomain].' file_lang='.$file_lang." => filelangexists=".$filelangexists."\n";
244 
245  if ($filelangexists)
246  {
247  // TODO Move cache read out of loop on dirs or at least filelangexists
248  $found = false;
249 
250  // Enable caching of lang file in memory (not by default)
251  $usecachekey = '';
252  // Using a memcached server
253  if (!empty($conf->memcached->enabled) && !empty($conf->global->MEMCACHED_SERVER))
254  {
255  $usecachekey = $newdomain.'_'.$langofdir.'_'.md5($file_lang); // Should not contains special chars
256  }
257  // Using cache with shmop. Speed gain: 40ms - Memory overusage: 200ko (Size of session cache file)
258  elseif (isset($conf->global->MAIN_OPTIMIZE_SPEED) && ($conf->global->MAIN_OPTIMIZE_SPEED & 0x02))
259  {
260  $usecachekey = $newdomain;
261  }
262 
263  if ($usecachekey)
264  {
265  //dol_syslog('Translate::Load we will cache result into usecachekey '.$usecachekey);
266  //global $aaa; $aaa+=1;
267  //print $aaa." ".$usecachekey."\n";
268  require_once DOL_DOCUMENT_ROOT.'/core/lib/memory.lib.php';
269  $tmparray = dol_getcache($usecachekey);
270  if (is_array($tmparray) && count($tmparray))
271  {
272  $this->tab_translate += $tmparray; // Faster than array_merge($tmparray,$this->tab_translate). Note: If a value already exists into tab_translate, value into tmparaay is not added.
273  //print $newdomain."\n";
274  //var_dump($this->tab_translate);
275  if ($alt == 2) $fileread = 1;
276  $found = true; // Found in dolibarr PHP cache
277  }
278  }
279 
280  if (!$found)
281  {
282  if ($fp = @fopen($file_lang, "rt"))
283  {
284  if ($usecachekey) $tabtranslatedomain = array(); // To save lang content in cache
285 
291  while ($line = fscanf($fp, "%[^= ]%*[ =]%[^\n\r]"))
292  {
293  if (isset($line[1]))
294  {
295  list($key, $value) = $line;
296  //if ($domain == 'orders') print "Domain=$domain, found a string for $tab[0] with value $tab[1]. Currently in cache ".$this->tab_translate[$key]."<br>";
297  //if ($key == 'Order') print "Domain=$domain, found a string for key=$key=$tab[0] with value $tab[1]. Currently in cache ".$this->tab_translate[$key]."<br>";
298  if (empty($this->tab_translate[$key]))
299  { // If translation was already found, we must not continue, even if MAIN_FORCELANGDIR is set (MAIN_FORCELANGDIR is to replace lang dir, not to overwrite entries)
300  if ($key == 'DIRECTION') { // This is to declare direction of language
301  if ($alt < 2 || empty($this->tab_translate[$key])) { // We load direction only for primary files or if not yet loaded
302  $this->tab_translate[$key] = $value;
303  if ($stopafterdirection) {
304  break; // We do not save tab if we stop after DIRECTION
305  } elseif ($usecachekey) {
306  $tabtranslatedomain[$key] = $value;
307  }
308  }
309  } elseif ($key[0] == '#')
310  {
311  continue;
312  } else {
313  // Convert some strings: Parse and render carriage returns. Also, change '\\s' into '\s' because transifex sync pull the string '\s' into string '\\s'
314  $this->tab_translate[$key] = str_replace(array('\\n', '\\\\s'), array("\n", '\s'), $value);
315  if ($usecachekey) {
316  $tabtranslatedomain[$key] = $value;
317  } // To save lang content in cache
318  }
319  }
320  }
321  }
322  fclose($fp);
323  $fileread = 1;
324 
325  // TODO Move cache write out of loop on dirs
326  // To save lang content for usecachekey into cache
327  if ($usecachekey && count($tabtranslatedomain))
328  {
329  $ressetcache = dol_setcache($usecachekey, $tabtranslatedomain);
330  if ($ressetcache < 0)
331  {
332  $error = 'Failed to set cache for usecachekey='.$usecachekey.' result='.$ressetcache;
333  dol_syslog($error, LOG_ERR);
334  }
335  }
336 
337  if (empty($conf->global->MAIN_FORCELANGDIR)) break; // Break loop on each root dir. If a module has forced dir, we do not stop loop.
338  }
339  }
340  }
341  }
342 
343  // Now we complete with next file (fr_CA->fr_FR, es_MX->ex_ES, ...)
344  if ($alt == 0)
345  {
346  // This function MUST NOT contains call to syslog
347  //dol_syslog("Translate::Load loading alternate translation file (to complete ".$this->defaultlang."/".$newdomain.".lang file)", LOG_DEBUG);
348  $langofdir = strtolower($langarray[0]).'_'.strtoupper($langarray[0]);
349  if ($langofdir == 'el_EL') $langofdir = 'el_GR'; // main parent for el_CY is not 'el_EL' but 'el_GR'
350  if ($langofdir == 'ar_AR') $langofdir = 'ar_SA'; // main parent for ar_EG is not 'ar_AR' but 'ar_SA'
351  $this->load($domain, $alt + 1, $stopafterdirection, $langofdir);
352  }
353 
354  // Now we complete with reference file (en_US)
355  if ($alt == 1)
356  {
357  // This function MUST NOT contains call to syslog
358  //dol_syslog("Translate::Load loading alternate translation file (to complete ".$this->defaultlang."/".$newdomain.".lang file)", LOG_DEBUG);
359  $langofdir = 'en_US';
360  $this->load($domain, $alt + 1, $stopafterdirection, $langofdir);
361  }
362 
363  // We are in the pass of the reference file. No more files to scan to complete.
364  if ($alt == 2)
365  {
366  if ($fileread) $this->_tab_loaded[$newdomain] = 1; // Set domain file as found so loaded
367 
368  if (empty($this->_tab_loaded[$newdomain])) $this->_tab_loaded[$newdomain] = 2; // Set this file as not found
369  }
370 
371  // This part is deprecated and replaced with table llx_overwrite_trans
372  // Kept for backward compatibility.
373  if (empty($loadfromfileonly))
374  {
375  $overwritekey = 'MAIN_OVERWRITE_TRANS_'.$this->defaultlang;
376  if (!empty($conf->global->$overwritekey)) // Overwrite translation with key1:newstring1,key2:newstring2
377  {
378  // Overwrite translation with param MAIN_OVERWRITE_TRANS_xx_XX
379  $tmparray = explode(',', $conf->global->$overwritekey);
380  foreach ($tmparray as $tmp)
381  {
382  $tmparray2 = explode(':', $tmp);
383  if (!empty($tmparray2[1])) $this->tab_translate[$tmparray2[0]] = $tmparray2[1];
384  }
385  }
386  }
387 
388  // Check to be sure that SeparatorDecimal differs from SeparatorThousand
389  if (!empty($this->tab_translate["SeparatorDecimal"]) && !empty($this->tab_translate["SeparatorThousand"])
390  && $this->tab_translate["SeparatorDecimal"] == $this->tab_translate["SeparatorThousand"]) $this->tab_translate["SeparatorThousand"] = '';
391 
392  return 1;
393  }
394 
407  public function loadFromDatabase($db)
408  {
409  global $conf;
410 
411  $domain = 'database';
412 
413  // Check parameters
414  if (empty($db)) return 0; // Database handler can't be used
415 
416  //dol_syslog("Translate::Load Start domain=".$domain." alt=".$alt." forcelangdir=".$forcelangdir." this->defaultlang=".$this->defaultlang);
417 
418  $newdomain = $domain;
419 
420  // Check cache
421  if (!empty($this->_tab_loaded[$newdomain])) // File already loaded for this domain 'database'
422  {
423  //dol_syslog("Translate::Load already loaded for newdomain=".$newdomain);
424  return 0;
425  }
426 
427  $this->_tab_loaded[$newdomain] = 1; // We want to be sure this function is called once only for domain 'database'
428 
429  $fileread = 0;
430  $langofdir = $this->defaultlang;
431 
432  if (empty($langofdir)) // This may occurs when load is called without setting the language and without providing a value for forcelangdir
433  {
434  dol_syslog("Error: ".get_class($this)."::loadFromDatabase was called but language was not set yet with langs->setDefaultLang(). Nothing will be loaded.", LOG_WARNING);
435  return -1;
436  }
437 
438  // TODO Move cache read out of loop on dirs or at least filelangexists
439  $found = false;
440 
441  // Enable caching of lang file in memory (not by default)
442  $usecachekey = '';
443  // Using a memcached server
444  if (!empty($conf->memcached->enabled) && !empty($conf->global->MEMCACHED_SERVER))
445  {
446  $usecachekey = $newdomain.'_'.$langofdir; // Should not contains special chars
447  }
448  // Using cache with shmop. Speed gain: 40ms - Memory overusage: 200ko (Size of session cache file)
449  elseif (isset($conf->global->MAIN_OPTIMIZE_SPEED) && ($conf->global->MAIN_OPTIMIZE_SPEED & 0x02))
450  {
451  $usecachekey = $newdomain;
452  }
453 
454  if ($usecachekey)
455  {
456  //dol_syslog('Translate::Load we will cache result into usecachekey '.$usecachekey);
457  //global $aaa; $aaa+=1;
458  //print $aaa." ".$usecachekey."\n";
459  require_once DOL_DOCUMENT_ROOT.'/core/lib/memory.lib.php';
460  $tmparray = dol_getcache($usecachekey);
461  if (is_array($tmparray) && count($tmparray))
462  {
463  $this->tab_translate += $tmparray; // Faster than array_merge($tmparray,$this->tab_translate). Note: If a valuer already exists into tab_translate, value into tmparaay is not added.
464  //print $newdomain."\n";
465  //var_dump($this->tab_translate);
466  $fileread = 1;
467  $found = true; // Found in dolibarr PHP cache
468  }
469  }
470 
471  if (!$found && !empty($conf->global->MAIN_ENABLE_OVERWRITE_TRANSLATION))
472  {
473  // Overwrite translation with database read
474  $sql = "SELECT transkey, transvalue FROM ".MAIN_DB_PREFIX."overwrite_trans where lang='".$db->escape($this->defaultlang)."' OR lang IS NULL";
475  $sql .= " AND entity IN (0, ".getEntity('overwrite_trans').")";
476  $sql .= $db->order("lang", "DESC");
477  $resql = $db->query($sql);
478 
479  if ($resql)
480  {
481  $num = $db->num_rows($resql);
482  if ($num)
483  {
484  if ($usecachekey) $tabtranslatedomain = array(); // To save lang content in cache
485 
486  $i = 0;
487  while ($i < $num) // Ex: Need 225ms for all fgets on all lang file for Third party page. Same speed than file_get_contents
488  {
489  $obj = $db->fetch_object($resql);
490 
491  $key = $obj->transkey;
492  $value = $obj->transvalue;
493 
494  //print "Domain=$domain, found a string for $tab[0] with value $tab[1]<br>";
495  if (empty($this->tab_translate[$key])) // If translation was already found, we must not continue, even if MAIN_FORCELANGDIR is set (MAIN_FORCELANGDIR is to replace lang dir, not to overwrite entries)
496  {
497  // Convert some strings: Parse and render carriage returns. Also, change '\\s' int '\s' because transifex sync pull the string '\s' into string '\\s'
498  $this->tab_translate[$key] = str_replace(array('\\n', '\\\\s'), array("\n", '\s'), $value);
499 
500  if ($usecachekey) $tabtranslatedomain[$key] = $value; // To save lang content in cache
501  }
502 
503  $i++;
504  }
505 
506  $fileread = 1;
507 
508  // TODO Move cache write out of loop on dirs
509  // To save lang content for usecachekey into cache
510  if ($usecachekey && count($tabtranslatedomain))
511  {
512  $ressetcache = dol_setcache($usecachekey, $tabtranslatedomain);
513  if ($ressetcache < 0)
514  {
515  $error = 'Failed to set cache for usecachekey='.$usecachekey.' result='.$ressetcache;
516  dol_syslog($error, LOG_ERR);
517  }
518  }
519  }
520  } else {
521  dol_print_error($db);
522  }
523  }
524 
525  if ($fileread) $this->_tab_loaded[$newdomain] = 1; // Set domain file as loaded
526 
527  if (empty($this->_tab_loaded[$newdomain])) $this->_tab_loaded[$newdomain] = 2; // Mark this case as not found (no lines found for language)
528 
529  return 1;
530  }
531 
538  public function isLoaded($domain)
539  {
540  return $this->_tab_loaded[$domain];
541  }
542 
554  private function getTradFromKey($key)
555  {
556  global $conf, $db;
557 
558  if (!is_string($key)) {
559  //xdebug_print_function_stack('ErrorBadValueForParamNotAString');
560  return 'ErrorBadValueForParamNotAString'; // Avoid multiple errors with code not using function correctly.
561  }
562 
563  $newstr = $key;
564  if (preg_match('/^Civility([0-9A-Z]+)$/i', $key, $reg))
565  {
566  $newstr = $this->getLabelFromKey($db, $reg[1], 'c_civility', 'code', 'label');
567  } elseif (preg_match('/^Currency([A-Z][A-Z][A-Z])$/i', $key, $reg))
568  {
569  $newstr = $this->getLabelFromKey($db, $reg[1], 'c_currencies', 'code_iso', 'label');
570  } elseif (preg_match('/^SendingMethod([0-9A-Z]+)$/i', $key, $reg))
571  {
572  $newstr = $this->getLabelFromKey($db, $reg[1], 'c_shipment_mode', 'code', 'libelle');
573  } elseif (preg_match('/^PaymentTypeShort([0-9A-Z]+)$/i', $key, $reg))
574  {
575  $newstr = $this->getLabelFromKey($db, $reg[1], 'c_paiement', 'code', 'libelle', '', 1);
576  } elseif (preg_match('/^OppStatus([0-9A-Z]+)$/i', $key, $reg))
577  {
578  $newstr = $this->getLabelFromKey($db, $reg[1], 'c_lead_status', 'code', 'label');
579  } elseif (preg_match('/^OrderSource([0-9A-Z]+)$/i', $key, $reg))
580  {
581  // TODO OrderSourceX must be replaced with content of table llx_c_input_reason or llx_c_input_method
582  //$newstr=$this->getLabelFromKey($db,$reg[1],'c_ordersource','code','label');
583  }
584 
585  /* Disabled. There is too many cases where translation of $newstr is not defined is normal (like when output with setEventMessage an already translated string)
586  if (! empty($conf->global->MAIN_FEATURES_LEVEL) && $conf->global->MAIN_FEATURES_LEVEL >= 2)
587  {
588  dol_syslog(__METHOD__." MAIN_FEATURES_LEVEL=DEVELOP: missing translation for key '".$newstr."' in ".$_SERVER["PHP_SELF"], LOG_DEBUG);
589  }*/
590 
591  return $newstr;
592  }
593 
594 
608  public function trans($key, $param1 = '', $param2 = '', $param3 = '', $param4 = '', $maxsize = 0)
609  {
610  global $conf;
611 
612  if (!empty($this->tab_translate[$key])) // Translation is available
613  {
614  $str = $this->tab_translate[$key];
615 
616  // Make some string replacement after translation
617  $replacekey = 'MAIN_REPLACE_TRANS_'.$this->defaultlang;
618  if (!empty($conf->global->$replacekey)) // Replacement translation variable with string1:newstring1;string2:newstring2
619  {
620  $tmparray = explode(';', $conf->global->$replacekey);
621  foreach ($tmparray as $tmp)
622  {
623  $tmparray2 = explode(':', $tmp);
624  $str = preg_replace('/'.preg_quote($tmparray2[0]).'/', $tmparray2[1], $str);
625  }
626  }
627 
628  // We replace some HTML tags by __xx__ to avoid having them encoded by htmlentities because
629  // we want to keep '"' '<b>' '</b>' '<strong' '</strong>' '<a ' '</a>' '<br>' '< ' '<span' '</span>' that are reliable HTML tags inside translation strings.
630  $str = str_replace(
631  array('"', '<b>', '</b>', '<u>', '</u>', '<i>', '</i>', '<center>', '</center>', '<strong>', '</strong>', '<a ', '</a>', '<br>', '<span', '</span>', '< ', '>'), // We accept '< ' but not '<'. We can accept however '>'
632  array('__quot__', '__tagb__', '__tagbend__', '__tagu__', '__taguend__', '__tagi__', '__tagiend__', '__tagcenter__', '__tagcenterend__', '__tagb__', '__tagbend__', '__taga__', '__tagaend__', '__tagbr__', '__tagspan__', '__tagspanend__', '__ltspace__', '__gt__'),
633  $str
634  );
635 
636  if (strpos($key, 'Format') !== 0)
637  {
638  $str = sprintf($str, $param1, $param2, $param3, $param4); // Replace %s and %d except for FormatXXX strings.
639  }
640 
641  // Crypt string into HTML
642  $str = htmlentities($str, ENT_COMPAT, $this->charset_output); // Do not convert simple quotes in translation (strings in html are embraced by "). Use dol_escape_htmltag around text in HTML content
643 
644  // Restore reliable HTML tags into original translation string
645  $str = str_replace(
646  array('__quot__', '__tagb__', '__tagbend__', '__tagu__', '__taguend__', '__tagi__', '__tagiend__', '__tagcenter__', '__tagcenterend__', '__taga__', '__tagaend__', '__tagbr__', '__tagspan__', '__tagspanend__', '__ltspace__', '__gt__'),
647  array('"', '<b>', '</b>', '<u>', '</u>', '<i>', '</i>', '<center>', '</center>', '<a ', '</a>', '<br>', '<span', '</span>', '< ', '>'),
648  $str
649  );
650 
651  if ($maxsize) $str = dol_trunc($str, $maxsize);
652 
653  return $str;
654  } else { // Translation is not available
655  //if ($key[0] == '$') { return dol_eval($key,1); }
656  return $this->getTradFromKey($key);
657  }
658  }
659 
660 
675  public function transnoentities($key, $param1 = '', $param2 = '', $param3 = '', $param4 = '', $param5 = '')
676  {
677  return $this->convToOutputCharset($this->transnoentitiesnoconv($key, $param1, $param2, $param3, $param4, $param5));
678  }
679 
680 
696  public function transnoentitiesnoconv($key, $param1 = '', $param2 = '', $param3 = '', $param4 = '', $param5 = '')
697  {
698  global $conf;
699 
700  if (!empty($this->tab_translate[$key])) // Translation is available
701  {
702  $str = $this->tab_translate[$key];
703 
704  // Make some string replacement after translation
705  $replacekey = 'MAIN_REPLACE_TRANS_'.$this->defaultlang;
706  if (!empty($conf->global->$replacekey)) // Replacement translation variable with string1:newstring1;string2:newstring2
707  {
708  $tmparray = explode(';', $conf->global->$replacekey);
709  foreach ($tmparray as $tmp)
710  {
711  $tmparray2 = explode(':', $tmp);
712  $str = preg_replace('/'.preg_quote($tmparray2[0]).'/', $tmparray2[1], $str);
713  }
714  }
715 
716  if (!preg_match('/^Format/', $key))
717  {
718  //print $str;
719  $str = sprintf($str, $param1, $param2, $param3, $param4, $param5); // Replace %s and %d except for FormatXXX strings.
720  }
721 
722  return $str;
723  } else {
724  if ($key[0] == '$') { return dol_eval($key, 1); }
725  return $this->getTradFromKey($key);
726  }
727  }
728 
729 
737  public function transcountry($str, $countrycode)
738  {
739  if ($this->tab_translate["$str$countrycode"]) return $this->trans("$str$countrycode");
740  else return $this->trans($str);
741  }
742 
743 
751  public function transcountrynoentities($str, $countrycode)
752  {
753  if (!empty($this->tab_translate["$str$countrycode"])) return $this->transnoentities("$str$countrycode");
754  else return $this->transnoentities($str);
755  }
756 
757 
765  public function convToOutputCharset($str, $pagecodefrom = 'UTF-8')
766  {
767  if ($pagecodefrom == 'ISO-8859-1' && $this->charset_output == 'UTF-8') $str = utf8_encode($str);
768  if ($pagecodefrom == 'UTF-8' && $this->charset_output == 'ISO-8859-1') $str = utf8_decode(str_replace('€', chr(128), $str));
769  return $str;
770  }
771 
772 
773  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
783  public function get_available_languages($langdir = DOL_DOCUMENT_ROOT, $maxlength = 0, $usecode = 0, $mainlangonly = 0)
784  {
785  // phpcs:enable
786  global $conf;
787 
788  $this->load("languages");
789 
790  // We scan directory langs to detect available languages
791  $handle = opendir($langdir."/langs");
792  $langs_available = array();
793  while ($dir = trim(readdir($handle)))
794  {
795  $regs = array();
796  if (preg_match('/^([a-z]+)_([A-Z]+)/i', $dir, $regs))
797  {
798  // We must keep only main languages
799  if ($mainlangonly) {
800  $arrayofspecialmainlanguages = array(
801  'en'=>'en_US',
802  'am'=>'am_ET',
803  'ar'=>'ar_SA',
804  'bn'=>'bn_DB',
805  'bs'=>'bs_BA',
806  'ca'=>'ca_ES',
807  'cs'=>'cs_CZ',
808  'da'=>'da_DK',
809  'et'=>'et_EE',
810  'el'=>'el_GR',
811  'eu'=>'eu_ES',
812  'fa'=>'fa_IR',
813  'he'=>'he_IL',
814  'ka'=>'ka_GE',
815  'km'=>'km_KH',
816  'kn'=>'kn_IN',
817  'ko'=>'ko_KR',
818  'ja'=>'ja_JP',
819  'lo'=>'lo_LA',
820  'nb'=>'nb_NO',
821  'fa'=>'fa_IR',
822  'sq'=>'sq_AL',
823  'sr'=>'sr_RS',
824  'sv'=>'sv_SE',
825  'sl'=>'sl_SI',
826  'uk'=>'uk_UA',
827  'vi'=>'vi_VN',
828  'zh'=>'zh_CN'
829  );
830  if (strtolower($regs[1]) != strtolower($regs[2]) && !in_array($dir, $arrayofspecialmainlanguages)) continue;
831  }
832  // We must keep only languages into MAIN_LANGUAGES_ALLOWED
833  if (!empty($conf->global->MAIN_LANGUAGES_ALLOWED) && !in_array($dir, explode(',', $conf->global->MAIN_LANGUAGES_ALLOWED))) continue;
834 
835  if ($usecode == 2)
836  {
837  $langs_available[$dir] = $dir;
838  }
839 
840  if ($usecode == 1 || !empty($conf->global->MAIN_SHOW_LANGUAGE_CODE))
841  {
842  $langs_available[$dir] = $dir.': '.dol_trunc($this->trans('Language_'.$dir), $maxlength);
843  } else {
844  $langs_available[$dir] = $this->trans('Language_'.$dir);
845  }
846  if ($mainlangonly) {
847  $langs_available[$dir] = str_replace(' (United States)', '', $langs_available[$dir]);
848  }
849  }
850  }
851  return $langs_available;
852  }
853 
854 
855  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
863  public function file_exists($filename, $searchalt = 0)
864  {
865  // phpcs:enable
866  // Test si fichier dans repertoire de la langue
867  foreach ($this->dir as $searchdir)
868  {
869  if (is_readable(dol_osencode($searchdir."/langs/".$this->defaultlang."/".$filename))) return true;
870 
871  if ($searchalt)
872  {
873  // Test si fichier dans repertoire de la langue alternative
874  if ($this->defaultlang != "en_US") $filenamealt = $searchdir."/langs/en_US/".$filename;
875  //else $filenamealt = $searchdir."/langs/fr_FR/".$filename;
876  if (is_readable(dol_osencode($filenamealt))) return true;
877  }
878  }
879 
880  return false;
881  }
882 
883 
895  public function getLabelFromNumber($number, $isamount = '')
896  {
897  global $conf;
898 
899  $newnumber = $number;
900 
901  $dirsubstitutions = array_merge(array(), $conf->modules_parts['substitutions']);
902  foreach ($dirsubstitutions as $reldir)
903  {
904  $dir = dol_buildpath($reldir, 0);
905  $newdir = dol_osencode($dir);
906 
907  // Check if directory exists
908  if (!is_dir($newdir)) continue; // We must not use dol_is_dir here, function may not be loaded
909 
910  $fonc = 'numberwords';
911  if (file_exists($newdir.'/functions_'.$fonc.'.lib.php'))
912  {
913  include_once $newdir.'/functions_'.$fonc.'.lib.php';
914  $newnumber = numberwords_getLabelFromNumber($this, $number, $isamount);
915  break;
916  }
917  }
918 
919  return $newnumber;
920  }
921 
922 
938  public function getLabelFromKey($db, $key, $tablename, $fieldkey, $fieldlabel, $keyforselect = '', $filteronentity = 0)
939  {
940  // If key empty
941  if ($key == '') return '';
942 
943  //print 'param: '.$key.'-'.$keydatabase.'-'.$this->trans($key); exit;
944 
945  // Check if a translation is available (this can call getTradFromKey)
946  $tmp = $this->transnoentitiesnoconv($key);
947  if ($tmp != $key && $tmp != 'ErrorBadValueForParamNotAString')
948  {
949  return $tmp; // Found in language array
950  }
951 
952  // Check in cache
953  if (isset($this->cache_labels[$tablename][$key])) // Can be defined to 0 or ''
954  {
955  return $this->cache_labels[$tablename][$key]; // Found in cache
956  }
957 
958  $sql = "SELECT ".$fieldlabel." as label";
959  $sql .= " FROM ".MAIN_DB_PREFIX.$tablename;
960  $sql .= " WHERE ".$fieldkey." = '".$db->escape($keyforselect ? $keyforselect : $key)."'";
961  if ($filteronentity) $sql .= " AND entity IN (".getEntity($tablename).')';
962  dol_syslog(get_class($this).'::getLabelFromKey', LOG_DEBUG);
963  $resql = $db->query($sql);
964  if ($resql)
965  {
966  $obj = $db->fetch_object($resql);
967  if ($obj) $this->cache_labels[$tablename][$key] = $obj->label;
968  else $this->cache_labels[$tablename][$key] = $key;
969 
970  $db->free($resql);
971  return $this->cache_labels[$tablename][$key];
972  } else {
973  $this->error = $db->lasterror();
974  return -1;
975  }
976  }
977 
978 
988  public function getCurrencyAmount($currency_code, $amount)
989  {
990  $symbol = $this->getCurrencySymbol($currency_code);
991 
992  if (in_array($currency_code, array('USD'))) return $symbol.$amount;
993  else return $amount.$symbol;
994  }
995 
1004  public function getCurrencySymbol($currency_code, $forceloadall = 0)
1005  {
1006  $currency_sign = ''; // By default return iso code
1007 
1008  if (function_exists("mb_convert_encoding"))
1009  {
1010  $this->loadCacheCurrencies($forceloadall ? '' : $currency_code);
1011 
1012  if (isset($this->cache_currencies[$currency_code]) && !empty($this->cache_currencies[$currency_code]['unicode']) && is_array($this->cache_currencies[$currency_code]['unicode']))
1013  {
1014  foreach ($this->cache_currencies[$currency_code]['unicode'] as $unicode)
1015  {
1016  $currency_sign .= mb_convert_encoding("&#{$unicode};", "UTF-8", 'HTML-ENTITIES');
1017  }
1018  }
1019  }
1020 
1021  return ($currency_sign ? $currency_sign : $currency_code);
1022  }
1023 
1030  public function loadCacheCurrencies($currency_code)
1031  {
1032  global $db;
1033 
1034  if ($this->cache_currencies_all_loaded) return 0; // Cache already loaded for all
1035  if (!empty($currency_code) && isset($this->cache_currencies[$currency_code])) return 0; // Cache already loaded for the currency
1036 
1037  $sql = "SELECT code_iso, label, unicode";
1038  $sql .= " FROM ".MAIN_DB_PREFIX."c_currencies";
1039  $sql .= " WHERE active = 1";
1040  if (!empty($currency_code)) $sql .= " AND code_iso = '".$db->escape($currency_code)."'";
1041  //$sql.= " ORDER BY code_iso ASC"; // Not required, a sort is done later
1042 
1043  dol_syslog(get_class($this).'::loadCacheCurrencies', LOG_DEBUG);
1044  $resql = $db->query($sql);
1045  if ($resql)
1046  {
1047  $this->load("dict");
1048  $label = array();
1049  if (!empty($currency_code)) foreach ($this->cache_currencies as $key => $val) $label[$key] = $val['label']; // Label in already loaded cache
1050 
1051  $num = $db->num_rows($resql);
1052  $i = 0;
1053  while ($i < $num)
1054  {
1055  $obj = $db->fetch_object($resql);
1056 
1057  // Si traduction existe, on l'utilise, sinon on prend le libelle par defaut
1058  $this->cache_currencies[$obj->code_iso]['label'] = ($obj->code_iso && $this->trans("Currency".$obj->code_iso) != "Currency".$obj->code_iso ? $this->trans("Currency".$obj->code_iso) : ($obj->label != '-' ? $obj->label : ''));
1059  $this->cache_currencies[$obj->code_iso]['unicode'] = (array) json_decode($obj->unicode, true);
1060  $label[$obj->code_iso] = $this->cache_currencies[$obj->code_iso]['label'];
1061  $i++;
1062  }
1063  if (empty($currency_code)) $this->cache_currencies_all_loaded = true;
1064  //print count($label).' '.count($this->cache_currencies);
1065 
1066  // Resort cache
1067  array_multisort($label, SORT_ASC, $this->cache_currencies);
1068  //var_dump($this->cache_currencies); $this->cache_currencies is now sorted onto label
1069  return $num;
1070  } else {
1071  dol_print_error($db);
1072  return -1;
1073  }
1074  }
1075 
1076  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1084  {
1085  // phpcs:enable
1086  $substitutionarray = array();
1087 
1088  foreach ($this->tab_translate as $code => $label) {
1089  $substitutionarray['lang_'.$code] = $label;
1090  $substitutionarray['__('.$code.')__'] = $label;
1091  }
1092 
1093  return $substitutionarray;
1094  }
1095 }
convToOutputCharset($str, $pagecodefrom= 'UTF-8')
Convert a string into output charset (this-&gt;charset_output that should be defined to conf-&gt;file-&gt;char...
dol_osencode($str)
Return a string encoded into OS filesystem encoding.
dol_getcache($memoryid)
Read a memory area shared by all users, all sessions on server.
Definition: memory.lib.php:104
dol_eval($s, $returnvalue=0, $hideerrors=1)
Replace eval function to add more security.
transnoentities($key, $param1= '', $param2= '', $param3= '', $param4= '', $param5= '')
Return translated value of a text string If there is no match for this text, we look in alternative f...
loadFromDatabase($db)
Load translation key-value from database into a memory array.
__construct($dir, $conf)
Constructor.
getLabelFromNumber($number, $isamount= '')
Return full text translated to language label for a key.
loadCacheCurrencies($currency_code)
Load into the cache this-&gt;cache_currencies, all currencies.
dol_buildpath($path, $type=0, $returnemptyifnotfound=0)
Return path of url or filesystem.
transcountrynoentities($str, $countrycode)
Retourne la version traduite du texte passe en parametre complete du code pays.
file_exists($filename, $searchalt=0)
Return if a filename $filename exists for current language (or alternate language) ...
getCurrencySymbol($currency_code, $forceloadall=0)
Return a currency code into its symbol.
getLabelFromKey($db, $key, $tablename, $fieldkey, $fieldlabel, $keyforselect= '', $filteronentity=0)
Return a label for a key.
isLoaded($domain)
Get information with result of loading data for domain.
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename= '', $restricttologhandler= '', $logcontext=null)
Write log message into outputs.
dol_setcache($memoryid, $data)
Save data into a memory area shared by all users, all sessions on server.
Definition: memory.lib.php:42
Class to manage translations.
setDefaultLang($srclang= 'en_US')
Set accessor for this-&gt;defaultlang.
getCurrencyAmount($currency_code, $amount)
Return a currency code into its symbol.
get_available_languages($langdir=DOL_DOCUMENT_ROOT, $maxlength=0, $usecode=0, $mainlangonly=0)
Return list of all available languages.
print $_SERVER["PHP_SELF"]
Edit parameters.
getDefaultLang($mode=0)
Return active language code for current user It&#39;s an accessor for this-&gt;defaultlang.
get_translations_for_substitutions()
Return an array with content of all loaded translation keys (found into this-&gt;tab_translate) so we ge...
if(!empty($conf->facture->enabled)&&$user->rights->facture->lire) if((!empty($conf->fournisseur->enabled)&&empty($conf->global->MAIN_USE_NEW_SUPPLIERMOD)||!empty($conf->supplier_invoice->enabled))&&$user->rights->fournisseur->facture->lire) if(!empty($conf->don->enabled)&&$user->rights->don->lire) if(!empty($conf->tax->enabled)&&$user->rights->tax->charges->lire) if(!empty($conf->facture->enabled)&&!empty($conf->commande->enabled)&&$user->rights->commande->lire &&empty($conf->global->WORKFLOW_DISABLE_CREATE_INVOICE_FROM_ORDER)) if(!empty($conf->facture->enabled)&&$user->rights->facture->lire) if((!empty($conf->fournisseur->enabled)&&empty($conf->global->MAIN_USE_NEW_SUPPLIERMOD)||!empty($conf->supplier_invoice->enabled))&&$user->rights->fournisseur->facture->lire) $resql
Social contributions to pay.
Definition: index.php:1232
dol_print_error($db= '', $error= '', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
loadLangs($domains)
Load translation files.
load($domain, $alt=0, $stopafterdirection=0, $forcelangdir= '', $loadfromfileonly=0, $forceloadifalreadynotfound=0)
Load translation key-value for a particular file, into a memory array.
dol_trunc($string, $size=40, $trunc= 'right', $stringencoding= 'UTF-8', $nodot=0, $display=0)
Truncate a string to a particular length adding &#39;...&#39; if string larger than length.
getTradFromKey($key)
Return translated value of key for special keys (&quot;Currency...&quot;, &quot;Civility...&quot;, ...).
trans($key, $param1= '', $param2= '', $param3= '', $param4= '', $maxsize=0)
Return text translated of text received as parameter (and encode it into HTML) If there is no match f...
transcountry($str, $countrycode)
Return translation of a key depending on country.
transnoentitiesnoconv($key, $param1= '', $param2= '', $param3= '', $param4= '', $param5= '')
Return translated value of a text string If there is no match for this text, we look in alternative f...