From b1168d223a09846865fe28d01c2b0f34b78b222b Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Thu, 17 Sep 2015 17:52:34 +0200 Subject: [PATCH] NEW Introduce table llx_overwrite_trans to be able to overwrite translations by simple database edition. --- htdocs/core/class/translate.class.php | 185 ++++++++++++++++-- .../install/mysql/migration/3.8.0-3.9.0.sql | 10 + .../mysql/tables/llx_overwrite_trans.sql | 27 +++ 3 files changed, 208 insertions(+), 14 deletions(-) create mode 100644 htdocs/install/mysql/tables/llx_overwrite_trans.sql diff --git a/htdocs/core/class/translate.class.php b/htdocs/core/class/translate.class.php index e477bdeb714..27bcb64da3c 100644 --- a/htdocs/core/class/translate.class.php +++ b/htdocs/core/class/translate.class.php @@ -29,17 +29,17 @@ */ class Translate { - var $dir; // Directories that contains /langs subdirectory + var $dir; // Directories that contains /langs subdirectory - var $defaultlang; // Current language for current user - var $direction = 'ltr'; // Left to right or Right to left - var $charset_output='UTF-8'; // Codage used by "trans" method outputs + var $defaultlang; // Current language for current user + var $direction = 'ltr'; // Left to right or Right to left + var $charset_output='UTF-8'; // Codage used by "trans" method outputs - var $tab_translate=array(); // Array of all translations key=>value - private $_tab_loaded=array(); // Array to store result after loading each language file + var $tab_translate=array(); // Array of all translations key=>value + private $_tab_loaded=array(); // Array to store result after loading each language file - var $cache_labels=array(); // Cache for labels return by getLabelFromKey method - var $cache_currencies=array(); // Cache to store currency symbols + var $cache_labels=array(); // Cache for labels return by getLabelFromKey method + var $cache_currencies=array(); // Cache to store currency symbols @@ -157,9 +157,11 @@ class Translate * @param int $forcelangdir To force a different lang directory * @return int <0 if KO, 0 if already loaded or loading not required, >0 if OK */ - function Load($domain,$alt=0,$stopafterdirection=0,$forcelangdir='') + function load($domain,$alt=0,$stopafterdirection=0,$forcelangdir='') { - global $conf; + global $conf,$db; + + if (count($this->tab_translate) == 0) $this->loadFromDatabase($db); // Nothing was loaded yet, so we load database. // Check parameters if (empty($domain)) @@ -239,7 +241,7 @@ class Translate $tmparray=dol_getcache($usecachekey); if (is_array($tmparray) && count($tmparray)) { - $this->tab_translate=array_merge($tmparray,$this->tab_translate); // Already found values tab_translate overwrites duplicates + $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. //print $newdomain."\n"; //var_dump($this->tab_translate); if ($alt == 2) $fileread=1; @@ -329,11 +331,12 @@ class Translate if (empty($this->_tab_loaded[$newdomain])) $this->_tab_loaded[$newdomain]=2; // Marque ce fichier comme non trouve } - - // Overwrite translation + // This part is deprecated and replaced with table llx_overwrite_trans + // Kept for backward compatibility. $overwritekey='MAIN_OVERWRITE_TRANS_'.$this->defaultlang; if (! empty($conf->global->$overwritekey)) // Overwrite translation with key1:newstring1,key2:newstring2 { + // Overwrite translation with param MAIN_OVERWRITE_TRANS_xx_XX $tmparray=explode(',', $conf->global->$overwritekey); foreach($tmparray as $tmp) { @@ -342,13 +345,167 @@ class Translate } } - // Check to be sure that SeparatorDecimal differs from SeparatorThousand + + + // Check to be sure that SeparatorDecimal differs from SeparatorThousand if (! empty($this->tab_translate["SeparatorDecimal"]) && ! empty($this->tab_translate["SeparatorThousand"]) && $this->tab_translate["SeparatorDecimal"] == $this->tab_translate["SeparatorThousand"]) $this->tab_translate["SeparatorThousand"]=''; return 1; } + /** + * Load translation key-value from database into a memory array. + * If data already loaded, do nothing. + * All data in translation array are stored in UTF-8 format. + * tab_loaded is completed with $domain key. + * rule "we keep first entry found with we keep last entry found" so it is probably not what you want to do. + * + * Value for hash are: 1:Loaded from disk, 2:Not found, 3:Loaded from cache + * + * @param Database $db Database handler + * @return int <0 if KO, 0 if already loaded or loading not required, >0 if OK + */ + function loadFromDatabase($db) + { + global $conf; + + $domain='database'; + + if ($this->defaultlang == 'none_NONE') return 0; // Special language code to not translate keys + + // Check parameters + if (empty($db)) return 0; // Database handler can't be used + + //dol_syslog("Translate::Load Start domain=".$domain." alt=".$alt." forcelangdir=".$forcelangdir." this->defaultlang=".$this->defaultlang); + + $newdomain = $domain; + $modulename = ''; + + // Check cache + if (! empty($this->_tab_loaded[$newdomain])) // File already loaded for this domain + { + //dol_syslog("Translate::Load already loaded for newdomain=".$newdomain); + return 0; + } + + $this->_tab_loaded[$newdomain] = 1; // We want to be sure this function is called once only. + + $fileread=0; + $langofdir=(empty($forcelangdir)?$this->defaultlang:$forcelangdir); + + // Redefine alt + $alt=2; + + if (empty($langofdir)) // This may occurs when load is called without setting the language and without providing a value for forcelangdir + { + dol_syslog("Error: ".get_class($this)."::Load was called but language was not set yet with langs->setDefaultLang(). Nothing will be loaded.", LOG_WARNING); + return -1; + } + + // TODO Move cache read out of loop on dirs or at least filelangexists + $found=false; + + // Enable caching of lang file in memory (not by default) + $usecachekey=''; + // Using a memcached server + if (! empty($conf->memcached->enabled) && ! empty($conf->global->MEMCACHED_SERVER)) + { + $usecachekey=$newdomain.'_'.$langofdir.'_'.md5($file_lang); // Should not contains special chars + } + // Using cache with shmop. Speed gain: 40ms - Memory overusage: 200ko (Size of session cache file) + else if (isset($conf->global->MAIN_OPTIMIZE_SPEED) && ($conf->global->MAIN_OPTIMIZE_SPEED & 0x02)) + { + $usecachekey=$newdomain; + } + + if ($usecachekey) + { + //dol_syslog('Translate::Load we will cache result into usecachekey '.$usecachekey); + //global $aaa; $aaa+=1; + //print $aaa." ".$usecachekey."\n"; + require_once DOL_DOCUMENT_ROOT .'/core/lib/memory.lib.php'; + $tmparray=dol_getcache($usecachekey); + if (is_array($tmparray) && count($tmparray)) + { + $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. + //print $newdomain."\n"; + //var_dump($this->tab_translate); + if ($alt == 2) $fileread=1; + $found=true; // Found in dolibarr PHP cache + } + } + + if (! $found) + { + // Overwrite translation with database read + $sql="SELECT transkey, transvalue FROM ".MAIN_DB_PREFIX."overwrite_trans where lang='".$this->defaultlang."'"; + $resql=$db->query($sql); + + if ($resql) + { + $num = $db->num_rows($resql); + if ($num) + { + if ($usecachekey) $tabtranslatedomain=array(); // To save lang content in cache + + $i = 0; + while ($i < $num) // Ex: Need 225ms for all fgets on all lang file for Third party page. Same speed than file_get_contents + { + $obj=$db->fetch_object($resql); + + $key=$obj->transkey; + $value=$obj->transvalue; + + //print "Domain=$domain, found a string for $tab[0] with value $tab[1]
"; + 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) + { + $value=trim(preg_replace('/\\n/',"\n",$value)); + + $this->tab_translate[$key]=$value; + if ($usecachekey) $tabtranslatedomain[$key]=$value; // To save lang content in cache + } + + $i++; + } + + $fileread=1; + + // TODO Move cache write out of loop on dirs + // To save lang content for usecachekey into cache + if ($usecachekey && count($tabtranslatedomain)) + { + $ressetcache=dol_setcache($usecachekey,$tabtranslatedomain); + if ($ressetcache < 0) + { + $error='Failed to set cache for usecachekey='.$usecachekey.' result='.$ressetcache; + dol_syslog($error, LOG_ERR); + } + } + } + } + else + { + dol_print_error($db); + } + } + + if ($alt == 2) + { + if ($fileread) $this->_tab_loaded[$newdomain]=1; // Set domain file as loaded + + if (empty($this->_tab_loaded[$newdomain])) $this->_tab_loaded[$newdomain]=2; // Marque ce cas comme non trouve (no lines found for language) + } + + // Check to be sure that SeparatorDecimal differs from SeparatorThousand + if (! empty($this->tab_translate["SeparatorDecimal"]) && ! empty($this->tab_translate["SeparatorThousand"]) + && $this->tab_translate["SeparatorDecimal"] == $this->tab_translate["SeparatorThousand"]) $this->tab_translate["SeparatorThousand"]=''; + + return 1; + } + + + /** * Return translated value of key for special keys ("Currency...", "Civility...", ...). * Search in lang file, then into database. Key must be any complete entry into lang file: CurrencyEUR, ... diff --git a/htdocs/install/mysql/migration/3.8.0-3.9.0.sql b/htdocs/install/mysql/migration/3.8.0-3.9.0.sql index dc778fd0eca..f9e649dfaf5 100755 --- a/htdocs/install/mysql/migration/3.8.0-3.9.0.sql +++ b/htdocs/install/mysql/migration/3.8.0-3.9.0.sql @@ -31,3 +31,13 @@ ALTER TABLE llx_accountingaccount RENAME TO llx_accounting_account; ALTER TABLE llx_societe ADD COLUMN model_pdf varchar(255); ALTER TABLE llx_societe_commerciaux ADD COLUMN import_key varchar(14) AFTER fk_user; + + +create table llx_overwrite_trans +( + rowid integer AUTO_INCREMENT PRIMARY KEY, + lang varchar(5), -- en_US, fr_FR ... + transkey varchar(128), + transvalue text +)ENGINE=innodb; + diff --git a/htdocs/install/mysql/tables/llx_overwrite_trans.sql b/htdocs/install/mysql/tables/llx_overwrite_trans.sql new file mode 100644 index 00000000000..152fe5e7a0c --- /dev/null +++ b/htdocs/install/mysql/tables/llx_overwrite_trans.sql @@ -0,0 +1,27 @@ +-- ============================================================================ +-- Copyright (C) 2013 Laurent Destailleur +-- +-- This program is free software; you can redistribute it and/or modify +-- it under the terms of the GNU General Public License as published by +-- the Free Software Foundation; either version 3 of the License, or +-- (at your option) any later version. +-- +-- This program is distributed in the hope that it will be useful, +-- but WITHOUT ANY WARRANTY; without even the implied warranty of +-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-- GNU General Public License for more details. +-- +-- You should have received a copy of the GNU General Public License +-- along with this program. If not, see . +-- ============================================================================ + + +create table llx_overwrite_trans +( + rowid integer AUTO_INCREMENT PRIMARY KEY, + lang varchar(5), -- en_US, fr_FR ... + transkey varchar(128), + transvalue text +)ENGINE=innodb; + +