dolibarr  9.0.0
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 <http://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 $charset_output='UTF-8'; // Codage used by "trans" method outputs
36 
37  public $tab_translate=array(); // Array of all translations key=>value
38  private $_tab_loaded=array(); // Array to store result after loading each language file
39 
40  public $cache_labels=array(); // Cache for labels return by getLabelFromKey method
41  public $cache_currencies=array(); // Cache to store currency symbols
42  private $cache_currencies_all_loaded=false;
43 
44 
51  function __construct($dir, $conf)
52  {
53  if (! empty($conf->file->character_set_client)) $this->charset_output=$conf->file->character_set_client; // If charset output is forced
54  if ($dir) $this->dir=array($dir);
55  else $this->dir=$conf->file->dol_document_root;
56  }
57 
58 
65  function setDefaultLang($srclang='en_US')
66  {
67  global $conf;
68 
69  //dol_syslog(get_class($this)."::setDefaultLang srclang=".$srclang,LOG_DEBUG);
70 
71  // If a module ask to force a priority on langs directories (to use its own lang files)
72  if (! empty($conf->global->MAIN_FORCELANGDIR))
73  {
74  $more=array();
75  $i=0;
76  foreach($conf->file->dol_document_root as $dir)
77  {
78  $newdir=$dir.$conf->global->MAIN_FORCELANGDIR; // For example $conf->global->MAIN_FORCELANGDIR is '/mymodule' meaning we search files into '/mymodule/langs/xx_XX'
79  if (! in_array($newdir,$this->dir))
80  {
81  $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.
82  }
83  }
84  $this->dir=array_merge($more,$this->dir); // Forced dir ($more) are before standard dirs ($this->dir)
85  }
86 
87  $this->origlang=$srclang;
88 
89  if (empty($srclang) || $srclang == 'auto')
90  {
91  $langpref=empty($_SERVER['HTTP_ACCEPT_LANGUAGE'])?'':$_SERVER['HTTP_ACCEPT_LANGUAGE'];
92  $langpref=preg_replace("/;([^,]*)/i","",$langpref);
93  $langpref=str_replace("-","_",$langpref);
94  $langlist=preg_split("/[;,]/",$langpref);
95  $codetouse=$langlist[0];
96  }
97  else $codetouse=$srclang;
98 
99  // We redefine $srclang
100  $langpart=explode("_",$codetouse);
101  //print "Short code before _ : ".$langpart[0].' / Short code after _ : '.$langpart[1].'<br>';
102  if (! empty($langpart[1])) // If it's for a codetouse that is a long code xx_YY
103  {
104  // Array force long code from first part, even if long code is defined
105  $longforshort=array('ar'=>'ar_SA');
106  $longforshortexcep=array('ar_EG');
107  if (isset($longforshort[strtolower($langpart[0])]) && ! in_array($codetouse, $longforshortexcep)) $srclang=$longforshort[strtolower($langpart[0])];
108  else if (! is_numeric($langpart[1])) { // Second part YY may be a numeric with some Chrome browser
109  $srclang=strtolower($langpart[0])."_".strtoupper($langpart[1]);
110  $longforlong=array('no_nb'=>'nb_NO');
111  if (isset($longforlong[strtolower($srclang)])) $srclang=$longforlong[strtolower($srclang)];
112  }
113  else $srclang=strtolower($langpart[0])."_".strtoupper($langpart[0]);
114  }
115  else { // If it's for a codetouse that is a short code xx
116  // Array to convert short lang code into long code.
117  $longforshort=array('ar'=>'ar_SA', 'el'=>'el_GR', 'ca'=>'ca_ES', 'en'=>'en_US', 'nb'=>'nb_NO', 'no'=>'nb_NO');
118  if (isset($longforshort[strtolower($langpart[0])])) $srclang=$longforshort[strtolower($langpart[0])];
119  else if (! empty($langpart[0])) $srclang=strtolower($langpart[0])."_".strtoupper($langpart[0]);
120  else $srclang='en_US';
121  }
122 
123  $this->defaultlang=$srclang;
124  //print 'this->defaultlang='.$this->defaultlang;
125  }
126 
127 
135  function getDefaultLang($mode=0)
136  {
137  if (empty($mode)) return $this->defaultlang;
138  else return substr($this->defaultlang,0,2);
139  }
140 
141 
148  function loadLangs($domains)
149  {
150  foreach($domains as $domain)
151  {
152  $this->load($domain);
153  }
154  }
155 
176  function load($domain,$alt=0,$stopafterdirection=0,$forcelangdir='',$loadfromfileonly=0)
177  {
178  global $conf,$db;
179 
180  //dol_syslog("Translate::Load Start domain=".$domain." alt=".$alt." forcelangdir=".$forcelangdir." this->defaultlang=".$this->defaultlang);
181 
182  // Check parameters
183  if (empty($domain))
184  {
185  dol_print_error('',get_class($this)."::Load ErrorWrongParameters");
186  return -1;
187  }
188  if ($this->defaultlang == 'none_NONE') return 0; // Special language code to not translate keys
189 
190 
191  // Load $this->tab_translate[] from database
192  if (empty($loadfromfileonly) && count($this->tab_translate) == 0) $this->loadFromDatabase($db); // No translation was never loaded yet, so we load database.
193 
194 
195  $newdomain = $domain;
196  $modulename = '';
197 
198  // Search if a module directory name is provided into lang file name
199  if (preg_match('/^([^@]+)@([^@]+)$/i',$domain,$regs))
200  {
201  $newdomain = $regs[1];
202  $modulename = $regs[2];
203  }
204 
205  // Check cache
206  if (! empty($this->_tab_loaded[$newdomain])) // File already loaded for this domain
207  {
208  //dol_syslog("Translate::Load already loaded for newdomain=".$newdomain);
209  return 0;
210  }
211 
212  $fileread=0;
213  $langofdir=(empty($forcelangdir)?$this->defaultlang:$forcelangdir);
214 
215  // Redefine alt
216  $langarray=explode('_',$langofdir);
217  if ($alt < 1 && isset($langarray[1]) && (strtolower($langarray[0]) == strtolower($langarray[1]) || in_array(strtolower($langofdir), array('el_gr')))) $alt=1;
218  if ($alt < 2 && strtolower($langofdir) == 'en_us') $alt=2;
219 
220  if (empty($langofdir)) // This may occurs when load is called without setting the language and without providing a value for forcelangdir
221  {
222  dol_syslog("Error: ".get_class($this)."::Load was called but language was not set yet with langs->setDefaultLang(). Nothing will be loaded.", LOG_WARNING);
223  return -1;
224  }
225 
226  foreach($this->dir as $keydir => $searchdir)
227  {
228  // Directory of translation files
229  $file_lang = $searchdir.($modulename?'/'.$modulename:'')."/langs/".$langofdir."/".$newdomain.".lang";
230  $file_lang_osencoded=dol_osencode($file_lang);
231 
232  $filelangexists=is_file($file_lang_osencoded);
233 
234  //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);
235  //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";
236 
237  if ($filelangexists)
238  {
239  // TODO Move cache read out of loop on dirs or at least filelangexists
240  $found=false;
241 
242  // Enable caching of lang file in memory (not by default)
243  $usecachekey='';
244  // Using a memcached server
245  if (! empty($conf->memcached->enabled) && ! empty($conf->global->MEMCACHED_SERVER))
246  {
247  $usecachekey=$newdomain.'_'.$langofdir.'_'.md5($file_lang); // Should not contains special chars
248  }
249  // Using cache with shmop. Speed gain: 40ms - Memory overusage: 200ko (Size of session cache file)
250  else if (isset($conf->global->MAIN_OPTIMIZE_SPEED) && ($conf->global->MAIN_OPTIMIZE_SPEED & 0x02))
251  {
252  $usecachekey=$newdomain;
253  }
254 
255  if ($usecachekey)
256  {
257  //dol_syslog('Translate::Load we will cache result into usecachekey '.$usecachekey);
258  //global $aaa; $aaa+=1;
259  //print $aaa." ".$usecachekey."\n";
260  require_once DOL_DOCUMENT_ROOT .'/core/lib/memory.lib.php';
261  $tmparray=dol_getcache($usecachekey);
262  if (is_array($tmparray) && count($tmparray))
263  {
264  $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.
265  //print $newdomain."\n";
266  //var_dump($this->tab_translate);
267  if ($alt == 2) $fileread=1;
268  $found=true; // Found in dolibarr PHP cache
269  }
270  }
271 
272  if (! $found)
273  {
274  if ($fp = @fopen($file_lang,"rt"))
275  {
276  if ($usecachekey) $tabtranslatedomain=array(); // To save lang content in cache
277 
283  while ($line = fscanf($fp, "%[^= ]%*[ =]%[^\n]"))
284  {
285  if (isset($line[1]))
286  {
287  list($key, $value) = $line;
288  //if ($domain == 'orders') print "Domain=$domain, found a string for $tab[0] with value $tab[1]. Currently in cache ".$this->tab_translate[$key]."<br>";
289  //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>";
290  if (empty($this->tab_translate[$key]))
291  { // 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)
292  $value = preg_replace('/\\n/', "\n", $value); // Parse and render carriage returns
293  if ($key == 'DIRECTION') { // This is to declare direction of language
294  if ($alt < 2 || empty($this->tab_translate[$key])) { // We load direction only for primary files or if not yet loaded
295  $this->tab_translate[$key] = $value;
296  if ($stopafterdirection) {
297  break; // We do not save tab if we stop after DIRECTION
298  } elseif ($usecachekey) {
299  $tabtranslatedomain[$key] = $value;
300  }
301  }
302  }
303  elseif ($key[0] == '#')
304  {
305  continue;
306  }
307  else {
308  $this->tab_translate[$key] = $value;
309  //if ($domain == 'orders') print "$tab[0] value $value<br>";
310  if ($usecachekey) {
311  $tabtranslatedomain[$key] = $value;
312  } // To save lang content in cache
313  }
314  }
315  }
316  }
317  fclose($fp);
318  $fileread=1;
319 
320  // TODO Move cache write out of loop on dirs
321  // To save lang content for usecachekey into cache
322  if ($usecachekey && count($tabtranslatedomain))
323  {
324  $ressetcache=dol_setcache($usecachekey,$tabtranslatedomain);
325  if ($ressetcache < 0)
326  {
327  $error='Failed to set cache for usecachekey='.$usecachekey.' result='.$ressetcache;
328  dol_syslog($error, LOG_ERR);
329  }
330  }
331 
332  if (empty($conf->global->MAIN_FORCELANGDIR)) break; // Break loop on each root dir. If a module has forced dir, we do not stop loop.
333  }
334  }
335  }
336  }
337 
338  // Now we complete with next file (fr_CA->fr_FR, es_MX->ex_ES, ...)
339  if ($alt == 0)
340  {
341  // This function MUST NOT contains call to syslog
342  //dol_syslog("Translate::Load loading alternate translation file (to complete ".$this->defaultlang."/".$newdomain.".lang file)", LOG_DEBUG);
343  $langofdir=strtolower($langarray[0]).'_'.strtoupper($langarray[0]);
344  if ($langofdir == 'el_EL') $langofdir = 'el_GR'; // main parent for el_CY is not 'el_EL' but 'el_GR'
345  if ($langofdir == 'ar_AR') $langofdir = 'ar_SA'; // main parent for ar_EG is not 'ar_AR' but 'ar_SA'
346  $this->load($domain,$alt+1,$stopafterdirection,$langofdir);
347  }
348 
349  // Now we complete with reference file (en_US)
350  if ($alt == 1)
351  {
352  // This function MUST NOT contains call to syslog
353  //dol_syslog("Translate::Load loading alternate translation file (to complete ".$this->defaultlang."/".$newdomain.".lang file)", LOG_DEBUG);
354  $langofdir='en_US';
355  $this->load($domain,$alt+1,$stopafterdirection,$langofdir);
356  }
357 
358  // We are in the pass of the reference file. No more files to scan to complete.
359  if ($alt == 2)
360  {
361  if ($fileread) $this->_tab_loaded[$newdomain]=1; // Set domain file as found so loaded
362 
363  if (empty($this->_tab_loaded[$newdomain])) $this->_tab_loaded[$newdomain]=2; // Set this file as not found
364  }
365 
366  // This part is deprecated and replaced with table llx_overwrite_trans
367  // Kept for backward compatibility.
368  if (empty($loadfromfileonly))
369  {
370  $overwritekey='MAIN_OVERWRITE_TRANS_'.$this->defaultlang;
371  if (! empty($conf->global->$overwritekey)) // Overwrite translation with key1:newstring1,key2:newstring2
372  {
373  // Overwrite translation with param MAIN_OVERWRITE_TRANS_xx_XX
374  $tmparray=explode(',', $conf->global->$overwritekey);
375  foreach($tmparray as $tmp)
376  {
377  $tmparray2=explode(':',$tmp);
378  if (! empty($tmparray2[1])) $this->tab_translate[$tmparray2[0]]=$tmparray2[1];
379  }
380  }
381  }
382 
383  // Check to be sure that SeparatorDecimal differs from SeparatorThousand
384  if (! empty($this->tab_translate["SeparatorDecimal"]) && ! empty($this->tab_translate["SeparatorThousand"])
385  && $this->tab_translate["SeparatorDecimal"] == $this->tab_translate["SeparatorThousand"]) $this->tab_translate["SeparatorThousand"]='';
386 
387  return 1;
388  }
389 
402  function loadFromDatabase($db)
403  {
404  global $conf;
405 
406  $domain='database';
407 
408  // Check parameters
409  if (empty($db)) return 0; // Database handler can't be used
410 
411  //dol_syslog("Translate::Load Start domain=".$domain." alt=".$alt." forcelangdir=".$forcelangdir." this->defaultlang=".$this->defaultlang);
412 
413  $newdomain = $domain;
414 
415  // Check cache
416  if (! empty($this->_tab_loaded[$newdomain])) // File already loaded for this domain 'database'
417  {
418  //dol_syslog("Translate::Load already loaded for newdomain=".$newdomain);
419  return 0;
420  }
421 
422  $this->_tab_loaded[$newdomain] = 1; // We want to be sure this function is called once only for domain 'database'
423 
424  $fileread=0;
425  $langofdir=$this->defaultlang;
426 
427  if (empty($langofdir)) // This may occurs when load is called without setting the language and without providing a value for forcelangdir
428  {
429  dol_syslog("Error: ".get_class($this)."::Load was called but language was not set yet with langs->setDefaultLang(). Nothing will be loaded.", LOG_WARNING);
430  return -1;
431  }
432 
433  // TODO Move cache read out of loop on dirs or at least filelangexists
434  $found=false;
435 
436  // Enable caching of lang file in memory (not by default)
437  $usecachekey='';
438  // Using a memcached server
439  if (! empty($conf->memcached->enabled) && ! empty($conf->global->MEMCACHED_SERVER))
440  {
441  $usecachekey=$newdomain.'_'.$langofdir; // Should not contains special chars
442  }
443  // Using cache with shmop. Speed gain: 40ms - Memory overusage: 200ko (Size of session cache file)
444  else if (isset($conf->global->MAIN_OPTIMIZE_SPEED) && ($conf->global->MAIN_OPTIMIZE_SPEED & 0x02))
445  {
446  $usecachekey=$newdomain;
447  }
448 
449  if ($usecachekey)
450  {
451  //dol_syslog('Translate::Load we will cache result into usecachekey '.$usecachekey);
452  //global $aaa; $aaa+=1;
453  //print $aaa." ".$usecachekey."\n";
454  require_once DOL_DOCUMENT_ROOT .'/core/lib/memory.lib.php';
455  $tmparray=dol_getcache($usecachekey);
456  if (is_array($tmparray) && count($tmparray))
457  {
458  $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.
459  //print $newdomain."\n";
460  //var_dump($this->tab_translate);
461  $fileread=1;
462  $found=true; // Found in dolibarr PHP cache
463  }
464  }
465 
466  if (! $found && ! empty($conf->global->MAIN_ENABLE_OVERWRITE_TRANSLATION))
467  {
468  // Overwrite translation with database read
469  $sql="SELECT transkey, transvalue FROM ".MAIN_DB_PREFIX."overwrite_trans where lang='".$db->escape($this->defaultlang)."'";
470  $resql=$db->query($sql);
471 
472  if ($resql)
473  {
474  $num = $db->num_rows($resql);
475  if ($num)
476  {
477  if ($usecachekey) $tabtranslatedomain=array(); // To save lang content in cache
478 
479  $i = 0;
480  while ($i < $num) // Ex: Need 225ms for all fgets on all lang file for Third party page. Same speed than file_get_contents
481  {
482  $obj=$db->fetch_object($resql);
483 
484  $key=$obj->transkey;
485  $value=$obj->transvalue;
486 
487  //print "Domain=$domain, found a string for $tab[0] with value $tab[1]<br>";
488  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)
489  {
490  $value=trim(preg_replace('/\\n/',"\n",$value));
491 
492  $this->tab_translate[$key]=$value;
493  if ($usecachekey) $tabtranslatedomain[$key]=$value; // To save lang content in cache
494  }
495 
496  $i++;
497  }
498 
499  $fileread=1;
500 
501  // TODO Move cache write out of loop on dirs
502  // To save lang content for usecachekey into cache
503  if ($usecachekey && count($tabtranslatedomain))
504  {
505  $ressetcache=dol_setcache($usecachekey,$tabtranslatedomain);
506  if ($ressetcache < 0)
507  {
508  $error='Failed to set cache for usecachekey='.$usecachekey.' result='.$ressetcache;
509  dol_syslog($error, LOG_ERR);
510  }
511  }
512  }
513  }
514  else
515  {
516  dol_print_error($db);
517  }
518  }
519 
520  if ($fileread) $this->_tab_loaded[$newdomain]=1; // Set domain file as loaded
521 
522  if (empty($this->_tab_loaded[$newdomain])) $this->_tab_loaded[$newdomain]=2; // Marque ce cas comme non trouve (no lines found for language)
523 
524  return 1;
525  }
526 
527 
528 
540  private function getTradFromKey($key)
541  {
542  global $conf, $db;
543 
544  if (! is_string($key)) return 'ErrorBadValueForParamNotAString'; // Avoid multiple errors with code not using function correctly.
545 
546  $newstr=$key;
547  if (preg_match('/^Civility([0-9A-Z]+)$/i',$key,$reg))
548  {
549  $newstr=$this->getLabelFromKey($db,$reg[1],'c_civility','code','label');
550  }
551  elseif (preg_match('/^Currency([A-Z][A-Z][A-Z])$/i',$key,$reg))
552  {
553  $newstr=$this->getLabelFromKey($db,$reg[1],'c_currencies','code_iso','label');
554  }
555  elseif (preg_match('/^SendingMethod([0-9A-Z]+)$/i',$key,$reg))
556  {
557  $newstr=$this->getLabelFromKey($db,$reg[1],'c_shipment_mode','code','libelle');
558  }
559  elseif (preg_match('/^PaymentTypeShort([0-9A-Z]+)$/i',$key,$reg))
560  {
561  $newstr=$this->getLabelFromKey($db,$reg[1],'c_paiement','code','libelle','',1);
562  }
563  elseif (preg_match('/^OppStatus([0-9A-Z]+)$/i',$key,$reg))
564  {
565  $newstr=$this->getLabelFromKey($db,$reg[1],'c_lead_status','code','label');
566  }
567  elseif (preg_match('/^OrderSource([0-9A-Z]+)$/i',$key,$reg))
568  {
569  // TODO OrderSourceX must be replaced with content of table llx_c_input_reason or llx_c_input_method
570  //$newstr=$this->getLabelFromKey($db,$reg[1],'c_ordersource','code','label');
571  }
572 
573  /* Disabled. There is too many cases where translation of $newstr is not defined is normal (like when output with setEventMessage an already translated string)
574  if (! empty($conf->global->MAIN_FEATURES_LEVEL) && $conf->global->MAIN_FEATURES_LEVEL >= 2)
575  {
576  dol_syslog(__METHOD__." MAIN_FEATURES_LEVEL=DEVELOP: missing translation for key '".$newstr."' in ".$_SERVER["PHP_SELF"], LOG_DEBUG);
577  }*/
578 
579  return $newstr;
580  }
581 
582 
597  function trans($key, $param1='', $param2='', $param3='', $param4='', $maxsize=0)
598  {
599  global $conf;
600 
601  if (! empty($this->tab_translate[$key])) // Translation is available
602  {
603  $str=$this->tab_translate[$key];
604 
605  // Make some string replacement after translation
606  $replacekey='MAIN_REPLACE_TRANS_'.$this->defaultlang;
607  if (! empty($conf->global->$replacekey)) // Replacement translation variable with string1:newstring1;string2:newstring2
608  {
609  $tmparray=explode(';', $conf->global->$replacekey);
610  foreach($tmparray as $tmp)
611  {
612  $tmparray2=explode(':',$tmp);
613  $str=preg_replace('/'.preg_quote($tmparray2[0]).'/',$tmparray2[1],$str);
614  }
615  }
616 
617  if (! preg_match('/^Format/',$key))
618  {
619  //print $str;
620  $str=sprintf($str,$param1,$param2,$param3,$param4); // Replace %s and %d except for FormatXXX strings.
621  }
622 
623  if ($maxsize) $str=dol_trunc($str,$maxsize);
624 
625  // We replace some HTML tags by __xx__ to avoid having them encoded by htmlentities
626  $str=str_replace(array('<','>','"',),array('__lt__','__gt__','__quot__'),$str);
627 
628  // Crypt string into HTML
629  $str=htmlentities($str, ENT_COMPAT, $this->charset_output); // Do not convert simple quotes in translation (strings in html are enmbraced by "). Use dol_escape_htmltag around text in HTML content
630 
631  // Restore HTML tags
632  $str=str_replace(array('__lt__','__gt__','__quot__'),array('<','>','"',),$str);
633 
634  return $str;
635  }
636  else // Translation is not available
637  {
638  //if ($key[0] == '$') { return dol_eval($key,1); }
639  return $this->getTradFromKey($key);
640  }
641  }
642 
643 
658  function transnoentities($key, $param1='', $param2='', $param3='', $param4='', $param5='')
659  {
660  return $this->convToOutputCharset($this->transnoentitiesnoconv($key, $param1, $param2, $param3, $param4, $param5));
661  }
662 
663 
679  function transnoentitiesnoconv($key, $param1='', $param2='', $param3='', $param4='', $param5='')
680  {
681  global $conf;
682 
683  if (! empty($this->tab_translate[$key])) // Translation is available
684  {
685  $str=$this->tab_translate[$key];
686 
687  // Make some string replacement after translation
688  $replacekey='MAIN_REPLACE_TRANS_'.$this->defaultlang;
689  if (! empty($conf->global->$replacekey)) // Replacement translation variable with string1:newstring1;string2:newstring2
690  {
691  $tmparray=explode(';', $conf->global->$replacekey);
692  foreach($tmparray as $tmp)
693  {
694  $tmparray2=explode(':',$tmp);
695  $str=preg_replace('/'.preg_quote($tmparray2[0]).'/',$tmparray2[1],$str);
696  }
697  }
698 
699  if (! preg_match('/^Format/',$key))
700  {
701  //print $str;
702  $str=sprintf($str, $param1, $param2, $param3, $param4, $param5); // Replace %s and %d except for FormatXXX strings.
703  }
704 
705  return $str;
706  }
707  else
708  {
709  if ($key[0] == '$') { return dol_eval($key,1); }
710  return $this->getTradFromKey($key);
711  }
712  }
713 
714 
722  function transcountry($str, $countrycode)
723  {
724  if ($this->tab_translate["$str$countrycode"]) return $this->trans("$str$countrycode");
725  else return $this->trans($str);
726  }
727 
728 
736  function transcountrynoentities($str, $countrycode)
737  {
738  if ($this->tab_translate["$str$countrycode"]) return $this->transnoentities("$str$countrycode");
739  else return $this->transnoentities($str);
740  }
741 
742 
750  function convToOutputCharset($str,$pagecodefrom='UTF-8')
751  {
752  if ($pagecodefrom == 'ISO-8859-1' && $this->charset_output == 'UTF-8') $str=utf8_encode($str);
753  if ($pagecodefrom == 'UTF-8' && $this->charset_output == 'ISO-8859-1') $str=utf8_decode(str_replace('€',chr(128),$str));
754  return $str;
755  }
756 
757 
758  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
767  function get_available_languages($langdir=DOL_DOCUMENT_ROOT,$maxlength=0,$usecode=0)
768  {
769  // phpcs:enable
770  global $conf;
771 
772  // We scan directory langs to detect available languages
773  $handle=opendir($langdir."/langs");
774  $langs_available=array();
775  while ($dir = trim(readdir($handle)))
776  {
777  if (preg_match('/^[a-z]+_[A-Z]+/i',$dir))
778  {
779  $this->load("languages");
780 
781  if ($usecode == 2)
782  {
783  $langs_available[$dir] = $dir;
784  }
785  if ($usecode == 1 || ! empty($conf->global->MAIN_SHOW_LANGUAGE_CODE))
786  {
787  $langs_available[$dir] = $dir.': '.dol_trunc($this->trans('Language_'.$dir),$maxlength);
788  }
789  else
790  {
791  $langs_available[$dir] = $this->trans('Language_'.$dir);
792  }
793  }
794  }
795  return $langs_available;
796  }
797 
798 
799  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
807  function file_exists($filename,$searchalt=0)
808  {
809  // phpcs:enable
810  // Test si fichier dans repertoire de la langue
811  foreach($this->dir as $searchdir)
812  {
813  if (is_readable(dol_osencode($searchdir."/langs/".$this->defaultlang."/".$filename))) return true;
814 
815  if ($searchalt)
816  {
817  // Test si fichier dans repertoire de la langue alternative
818  if ($this->defaultlang != "en_US") $filenamealt = $searchdir."/langs/en_US/".$filename;
819  //else $filenamealt = $searchdir."/langs/fr_FR/".$filename;
820  if (is_readable(dol_osencode($filenamealt))) return true;
821  }
822  }
823 
824  return false;
825  }
826 
827 
839  function getLabelFromNumber($number,$isamount=0)
840  {
841  global $conf;
842 
843  $newnumber=$number;
844 
845  $dirsubstitutions=array_merge(array(),$conf->modules_parts['substitutions']);
846  foreach($dirsubstitutions as $reldir)
847  {
848  $dir=dol_buildpath($reldir,0);
849  $newdir=dol_osencode($dir);
850 
851  // Check if directory exists
852  if (! is_dir($newdir)) continue; // We must not use dol_is_dir here, function may not be loaded
853 
854  $fonc='numberwords';
855  if (file_exists($newdir.'/functions_'.$fonc.'.lib.php'))
856  {
857  include_once $newdir.'/functions_'.$fonc.'.lib.php';
858  $newnumber=numberwords_getLabelFromNumber($this,$number,$isamount);
859  break;
860  }
861  }
862 
863  return $newnumber;
864  }
865 
866 
882  function getLabelFromKey($db,$key,$tablename,$fieldkey,$fieldlabel,$keyforselect='',$filteronentity=0)
883  {
884  // If key empty
885  if ($key == '') return '';
886 
887  //print 'param: '.$key.'-'.$keydatabase.'-'.$this->trans($key); exit;
888 
889  // Check if a translation is available (this can call getTradFromKey)
890  $tmp=$this->transnoentitiesnoconv($key);
891  if ($tmp != $key && $tmp != 'ErrorBadValueForParamNotAString')
892  {
893  return $tmp; // Found in language array
894  }
895 
896  // Check in cache
897  if (isset($this->cache_labels[$tablename][$key])) // Can be defined to 0 or ''
898  {
899  return $this->cache_labels[$tablename][$key]; // Found in cache
900  }
901 
902  $sql = "SELECT ".$fieldlabel." as label";
903  $sql.= " FROM ".MAIN_DB_PREFIX.$tablename;
904  $sql.= " WHERE ".$fieldkey." = '".$db->escape($keyforselect?$keyforselect:$key)."'";
905  if ($filteronentity) $sql.= " AND entity IN (" . getEntity($tablename). ')';
906  dol_syslog(get_class($this).'::getLabelFromKey', LOG_DEBUG);
907  $resql = $db->query($sql);
908  if ($resql)
909  {
910  $obj = $db->fetch_object($resql);
911  if ($obj) $this->cache_labels[$tablename][$key]=$obj->label;
912  else $this->cache_labels[$tablename][$key]=$key;
913 
914  $db->free($resql);
915  return $this->cache_labels[$tablename][$key];
916  }
917  else
918  {
919  $this->error=$db->lasterror();
920  return -1;
921  }
922  }
923 
924 
934  function getCurrencyAmount($currency_code, $amount)
935  {
936  $symbol=$this->getCurrencySymbol($currency_code);
937 
938  if (in_array($currency_code, array('USD'))) return $symbol.$amount;
939  else return $amount.$symbol;
940  }
941 
950  function getCurrencySymbol($currency_code, $forceloadall=0)
951  {
952  $currency_sign = ''; // By default return iso code
953 
954  if (function_exists("mb_convert_encoding"))
955  {
956  $this->loadCacheCurrencies($forceloadall?'':$currency_code);
957 
958  if (isset($this->cache_currencies[$currency_code]) && ! empty($this->cache_currencies[$currency_code]['unicode']) && is_array($this->cache_currencies[$currency_code]['unicode']))
959  {
960  foreach($this->cache_currencies[$currency_code]['unicode'] as $unicode)
961  {
962  $currency_sign .= mb_convert_encoding("&#{$unicode};", "UTF-8", 'HTML-ENTITIES');
963  }
964  }
965  }
966 
967  return ($currency_sign?$currency_sign:$currency_code);
968  }
969 
976  public function loadCacheCurrencies($currency_code)
977  {
978  global $db;
979 
980  if ($this->cache_currencies_all_loaded) return 0; // Cache already loaded for all
981  if (! empty($currency_code) && isset($this->cache_currencies[$currency_code])) return 0; // Cache already loaded for the currency
982 
983  $sql = "SELECT code_iso, label, unicode";
984  $sql.= " FROM ".MAIN_DB_PREFIX."c_currencies";
985  $sql.= " WHERE active = 1";
986  if (! empty($currency_code)) $sql.=" AND code_iso = '".$db->escape($currency_code)."'";
987  //$sql.= " ORDER BY code_iso ASC"; // Not required, a sort is done later
988 
989  dol_syslog(get_class($this).'::loadCacheCurrencies', LOG_DEBUG);
990  $resql = $db->query($sql);
991  if ($resql)
992  {
993  $this->load("dict");
994  $label=array();
995  if (! empty($currency_code)) foreach($this->cache_currencies as $key => $val) $label[$key]=$val['label']; // Label in already loaded cache
996 
997  $num = $db->num_rows($resql);
998  $i = 0;
999  while ($i < $num)
1000  {
1001  $obj = $db->fetch_object($resql);
1002 
1003  // Si traduction existe, on l'utilise, sinon on prend le libelle par defaut
1004  $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:''));
1005  $this->cache_currencies[$obj->code_iso]['unicode'] = (array) json_decode($obj->unicode, true);
1006  $label[$obj->code_iso] = $this->cache_currencies[$obj->code_iso]['label'];
1007  $i++;
1008  }
1009  if (empty($currency_code)) $this->cache_currencies_all_loaded=true;
1010  //print count($label).' '.count($this->cache_currencies);
1011 
1012  // Resort cache
1013  array_multisort($label, SORT_ASC, $this->cache_currencies);
1014  //var_dump($this->cache_currencies); $this->cache_currencies is now sorted onto label
1015  return $num;
1016  }
1017  else
1018  {
1019  dol_print_error($db);
1020  return -1;
1021  }
1022  }
1023 
1024  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
1032  {
1033  // phpcs:enable
1034  $substitutionarray = array();
1035 
1036  foreach($this->tab_translate as $code => $label) {
1037  $substitutionarray['lang_'.$code] = $label;
1038  $substitutionarray['__('.$code.')__'] = $label;
1039  }
1040 
1041  return $substitutionarray;
1042  }
1043 }
dol_osencode($str)
Return a string encoded into OS filesystem encoding.
transnoentitiesnoconv($key, $param1='', $param2='', $param3='', $param4='', $param5='')
Return translated value of a text string Si il n&#39;y a pas de correspondance pour ce texte...
setDefaultLang($srclang='en_US')
Set accessor for this->defaultlang.
transnoentities($key, $param1='', $param2='', $param3='', $param4='', $param5='')
Return translated value of a text string Si il n&#39;y a pas de correspondance pour ce texte...
dol_getcache($memoryid)
Read a memory area shared by all users, all sessions on server.
Definition: memory.lib.php:110
dol_eval($s, $returnvalue=0, $hideerrors=1)
Replace eval function to add more security.
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.
if(! empty($conf->facture->enabled) && $user->rights->facture->lire) if(! empty($conf->fournisseur->enabled) && $user->rights->fournisseur->facture->lire) if(! empty($conf->don->enabled) && $user->rights->societe->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) && $user->rights->fournisseur->facture->lire) $resql
Social contributions to pay.
Definition: index.php:1053
loadFromDatabase($db)
Load translation key-value from database into a memory array.
__construct($dir, $conf)
Constructor.
dol_print_error($db='', $error='', $errors=null)
Affiche message erreur system avec toutes les informations pour faciliter le diagnostic et la remonte...
get_available_languages($langdir=DOL_DOCUMENT_ROOT, $maxlength=0, $usecode=0)
Return list of all available languages.
loadCacheCurrencies($currency_code)
Load into the cache this->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.
load($domain, $alt=0, $stopafterdirection=0, $forcelangdir='', $loadfromfileonly=0)
Load translation key-value for a particular file, into a memory array.
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='')
Write log message into outputs.
getEntity($element, $shared=1, $currentobject=null)
Get list of entity id to use.
convToOutputCharset($str, $pagecodefrom='UTF-8')
Convert a string into output charset (this->charset_output that should be defined to conf->file->char...
getLabelFromNumber($number, $isamount=0)
Return full text translated to language label for a key.
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.
getLabelFromKey($db, $key, $tablename, $fieldkey, $fieldlabel, $keyforselect='', $filteronentity=0)
Return a label for a key.
getCurrencyAmount($currency_code, $amount)
Return a currency code into its symbol.
getDefaultLang($mode=0)
Return active language code for current user It&#39;s an accessor for this->defaultlang.
trans($key, $param1='', $param2='', $param3='', $param4='', $maxsize=0)
Return text translated of text received as parameter (and encode it into HTML) Si il n&#39;y a pas de cor...
get_translations_for_substitutions()
Return an array with content of all loaded translation keys (found into this->tab_translate) so we ge...
loadLangs($domains)
Load translation files.
getTradFromKey($key)
Return translated value of key for special keys ("Currency...", "Civility...", ...).
transcountry($str, $countrycode)
Return translation of a key depending on country.