autotranslator.class.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366
  1. <?php
  2. /* Copyright (C) 2009-2012 Laurent Destailleur <eldy@users.sourceforge.net>
  3. *
  4. * This program is free software; you can redistribute it and/or modify
  5. * it under the terms of the GNU General Public License as published by
  6. * the Free Software Foundation; either version 3 of the License, or
  7. * (at your option) any later version.
  8. *
  9. * This program is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. * GNU General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU General Public License
  15. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  16. */
  17. /**
  18. * \file dev/translation/langAutoParser.class.php
  19. * \ingroup dev
  20. * \brief This file is an example for a command line script
  21. */
  22. /**
  23. * Class to parse language files and translate them
  24. * This is a language automatic translator parser for Dolibarr
  25. */
  26. class autoTranslator
  27. {
  28. private $_translatedFiles = array();
  29. private $_destlang = '';
  30. private $_refLang = '';
  31. private $_langDir = '';
  32. private $_limittofile = '';
  33. private $_time;
  34. private $_time_end;
  35. private $_outputpagecode = 'UTF-8';
  36. private $_apikey;
  37. //private $_outputpagecode = 'ISO-8859-1';
  38. const DIR_SEPARATOR = '/';
  39. /**
  40. * Constructor
  41. *
  42. * @param string $_destlang Destination lang
  43. * @param string $_refLang Ref lang
  44. * @param string $_langDir Dir lang
  45. * @param string $_limittofile Limit to file
  46. * @param string $_apikey Api key
  47. * @return void
  48. */
  49. public function __construct($_destlang, $_refLang, $_langDir, $_limittofile, $_apikey)
  50. {
  51. // Set enviorment variables
  52. $this->_destlang = $_destlang;
  53. $this->_refLang = $_refLang;
  54. $this->_langDir = $_langDir.self::DIR_SEPARATOR;
  55. $this->_time = date('Y-m-d H:i:s');
  56. $this->_limittofile = $_limittofile;
  57. $this->_apikey = $_apikey;
  58. // Translate
  59. //ini_set('default_charset','UTF-8');
  60. ini_set('default_charset', $this->_outputpagecode);
  61. $this->parseRefLangTranslationFiles();
  62. }
  63. /**
  64. * Parse file
  65. *
  66. * @return void
  67. */
  68. private function parseRefLangTranslationFiles()
  69. {
  70. $files = $this->getTranslationFilesArray($this->_refLang);
  71. $counter = 1;
  72. foreach ($files as $file) {
  73. if ($this->_limittofile && $this->_limittofile != $file) {
  74. continue;
  75. }
  76. $counter++;
  77. $fileContent = null;
  78. $refPath = $this->_langDir.$this->_refLang.self::DIR_SEPARATOR.$file;
  79. $fileContent = file($refPath, FILE_IGNORE_NEW_LINES|FILE_SKIP_EMPTY_LINES);
  80. print "Processing file " . $file . ", with ".count($fileContent)." lines<br>\n";
  81. // Define target dirs
  82. $targetlangs=array($this->_destlang);
  83. if ($this->_destlang == 'all') {
  84. $targetlangs=array();
  85. // If we must process all languages
  86. $arraytmp=dol_dir_list($this->_langDir, 'directories', 0);
  87. foreach ($arraytmp as $dirtmp) {
  88. if ($dirtmp['name'] === $this->_refLang) {
  89. continue; // We discard source language
  90. }
  91. $tmppart=explode('_', $dirtmp['name']);
  92. if (preg_match('/^en/i', $dirtmp['name'])) {
  93. continue; // We discard en_* languages
  94. }
  95. if (preg_match('/^fr/i', $dirtmp['name'])) {
  96. continue; // We discard fr_* languages
  97. }
  98. if (preg_match('/^es/i', $dirtmp['name'])) {
  99. continue; // We discard es_* languages
  100. }
  101. if (preg_match('/ca_ES/i', $dirtmp['name'])) {
  102. continue; // We discard es_CA language
  103. }
  104. if (preg_match('/pt_BR/i', $dirtmp['name'])) {
  105. continue; // We discard pt_BR language
  106. }
  107. if (preg_match('/nl_BE/i', $dirtmp['name'])) {
  108. continue; // We discard nl_BE language
  109. }
  110. if (preg_match('/^\./i', $dirtmp['name'])) {
  111. continue; // We discard files .*
  112. }
  113. if (preg_match('/^CVS/i', $dirtmp['name'])) {
  114. continue; // We discard CVS
  115. }
  116. $targetlangs[]=$dirtmp['name'];
  117. }
  118. //var_dump($targetlangs);
  119. }
  120. // Process translation of source file for each target languages
  121. foreach ($targetlangs as $my_destlang) {
  122. $this->_translatedFiles = array();
  123. $destPath = $this->_langDir.$my_destlang.self::DIR_SEPARATOR.$file;
  124. // Check destination file presence
  125. if (! file_exists($destPath)) {
  126. // No file present, we generate file
  127. echo "File not found: " . $destPath . ". We generate it.<br>\n";
  128. $this->createTranslationFile($destPath, $my_destlang);
  129. } else {
  130. echo "Updating file: " . $destPath . "<br>\n";
  131. }
  132. // Translate lines
  133. $fileContentDest = file($destPath, FILE_IGNORE_NEW_LINES|FILE_SKIP_EMPTY_LINES);
  134. $newlines=0;
  135. foreach ($fileContent as $line) {
  136. $key = $this->getLineKey($line);
  137. $value = $this->getLineValue($line);
  138. if ($key && $value) {
  139. $newlines+=$this->translateFileLine($fileContentDest, $file, $key, $value, $my_destlang);
  140. }
  141. }
  142. $this->updateTranslationFile($destPath, $file, $my_destlang);
  143. echo "New translated lines: " . $newlines . "<br>\n";
  144. //if ($counter ==3) die('fim');
  145. }
  146. }
  147. }
  148. /**
  149. * Update file with new translations
  150. *
  151. * @param string $destPath Target path
  152. * @param string $file File
  153. * @param string $my_destlang Target language code
  154. * @return void
  155. */
  156. private function updateTranslationFile($destPath, $file, $my_destlang)
  157. {
  158. $this->_time_end = date('Y-m-d H:i:s');
  159. if (isset($this->_translatedFiles[$file]) && count($this->_translatedFiles[$file])>0) {
  160. $fp = fopen($destPath, 'a');
  161. fwrite($fp, "\n");
  162. fwrite($fp, "\n");
  163. fwrite($fp, "// START - Lines generated via autotranslator.php tool (".$this->_time.").\n");
  164. fwrite($fp, "// Reference language: ".$this->_refLang." -> ".$my_destlang."\n");
  165. foreach ($this->_translatedFiles[$file] as $line) {
  166. fwrite($fp, $line . "\n");
  167. }
  168. fwrite($fp, "// STOP - Lines generated via autotranslator.php tool (".$this->_time_end.").\n");
  169. fclose($fp);
  170. }
  171. return;
  172. }
  173. /**
  174. * Create a new translation file
  175. *
  176. * @param string $path Path
  177. * @param string $my_destlang Target language code
  178. * @return void
  179. */
  180. private function createTranslationFile($path, $my_destlang)
  181. {
  182. $fp = fopen($path, 'w+');
  183. fwrite($fp, "/*\n");
  184. fwrite($fp, " * Language code: {$my_destlang}\n");
  185. fwrite($fp, " * Automatic generated via autotranslator.php tool\n");
  186. fwrite($fp, " * Generation date " . $this->_time. "\n");
  187. fwrite($fp, " */\n");
  188. fclose($fp);
  189. return;
  190. }
  191. /**
  192. * Put in array _translatedFiles[$file], line of a new tranlated pair
  193. *
  194. * @param string $content Existing content of dest file
  195. * @param string $file Target file name translated (xxxx.lang)
  196. * @param string $key Key to translate
  197. * @param string $value Existing value in source file
  198. * @param string $my_destlang Language code (ie: fr_FR)
  199. * @return int 0=Nothing translated, 1=Record translated
  200. */
  201. private function translateFileLine($content, $file, $key, $value, $my_destlang)
  202. {
  203. //print "key =".$key."\n";
  204. foreach ($content as $line) {
  205. $destKey = $this->getLineKey($line);
  206. $destValue = $this->getLineValue($line);
  207. // If translated return
  208. //print "destKey=".$destKey."\n";
  209. if (trim($destKey) == trim($key)) { // Found already existing translation (key already exits in dest file)
  210. return 0;
  211. }
  212. }
  213. if ($key == 'CHARSET') {
  214. $val=$this->_outputpagecode;
  215. } elseif (preg_match('/^Format/', $key)) {
  216. $val=$value;
  217. } elseif ($value=='-') {
  218. $val=$value;
  219. } else {
  220. // If not translated then translate
  221. if ($this->_outputpagecode == 'UTF-8') {
  222. $val=$this->translateTexts(array($value), substr($this->_refLang, 0, 2), substr($my_destlang, 0, 2));
  223. } else {
  224. $val=mb_convert_encoding($this->translateTexts(array($value), substr($this->_refLang, 0, 2), substr($my_destlang, 0, 2)), 'ISO-8859-1');
  225. }
  226. }
  227. $val=trim($val);
  228. if (empty($val)) {
  229. return 0;
  230. }
  231. $this->_translatedFiles[$file][] = $key . '=' . $val ;
  232. return 1;
  233. }
  234. /**
  235. * getLineKey
  236. *
  237. * @param string $line Line found into file
  238. * @return string Key
  239. */
  240. private function getLineKey($line)
  241. {
  242. $arraykey = explode('=', $line, 2);
  243. return trim($arraykey[0]);
  244. }
  245. /**
  246. * getLineValue
  247. *
  248. * @param string $line Line found into file
  249. * @return string Value
  250. */
  251. private function getLineValue($line)
  252. {
  253. $arraykey = explode('=', $line, 2);
  254. return trim(isset($arraykey[1]) ? $arraykey[1] : '');
  255. }
  256. /**
  257. * getTranslationFilesArray
  258. *
  259. * @param string $lang Language code
  260. * @return array Array
  261. */
  262. private function getTranslationFilesArray($lang)
  263. {
  264. $dir = new DirectoryIterator($this->_langDir.$lang);
  265. while ($dir->valid()) {
  266. if (!$dir->isDot() && $dir->isFile() && ! preg_match('/^\./', $dir->getFilename())) {
  267. $files[] = $dir->getFilename();
  268. }
  269. $dir->next();
  270. }
  271. return $files;
  272. }
  273. /**
  274. * Return translation of a value
  275. *
  276. * @param array $src_texts Array with one value
  277. * @param string $src_lang Language code source (us, fr, it, ...)
  278. * @param string $dest_lang Language code target (es, de, ...)
  279. * @return string Value translated
  280. */
  281. private function translateTexts($src_texts, $src_lang, $dest_lang)
  282. {
  283. // We want to be sure that src_lang and dest_lang are using 2 chars only
  284. $tmp=explode('_', $src_lang);
  285. if (!empty($tmp[1]) && $tmp[0] == $tmp[1]) {
  286. $src_lang=$tmp[0];
  287. }
  288. $tmp=explode('_', $dest_lang);
  289. if (!empty($tmp[1]) && $tmp[0] == $tmp[1]) {
  290. $dest_lang=$tmp[0];
  291. }
  292. //setting language pair
  293. $lang_pair = $src_lang.'|'.$dest_lang;
  294. $src_text_to_translate=preg_replace('/%s/', 'SSSSS', implode('', $src_texts));
  295. $src_text_to_translate=preg_replace('/'.preg_quote('\n\n').'/', ' NNNNN ', $src_text_to_translate);
  296. // Define GET URL v1
  297. //$url = "http://ajax.googleapis.com/ajax/services/language/translate?v=1.0&q=".urlencode($src_text_to_translate)."&langpair=".urlencode($lang_pair);
  298. // Example: http://ajax.googleapis.com/ajax/services/language/translate?v=1.0&q=Setup%20area&langpair=en_US|fr_FR
  299. // Define GET URL v2
  300. $url = "https://www.googleapis.com/language/translate/v2?key=".$this->_apikey."&q=".urlencode($src_text_to_translate)."&source=".urlencode($src_lang)."&target=".urlencode($dest_lang);
  301. // Example: https://www.googleapis.com/language/translate/v2?key=_apikey&q=Setup%20area&source=en_US&target=fr_FR
  302. // Send request
  303. //print "Url to translate: ".$url."\n";
  304. if (! function_exists("curl_init")) {
  305. print "Error, your PHP does not support curl functions.\n";
  306. die();
  307. }
  308. $ch = curl_init();
  309. curl_setopt($ch, CURLOPT_URL, $url);
  310. curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
  311. curl_setopt($ch, CURLOPT_REFERER, "Mozilla");
  312. $body = curl_exec($ch);
  313. curl_close($ch);
  314. //sleep(1); // This is to avoid to overload server.
  315. // now, process the JSON string
  316. $json = json_decode($body, true);
  317. if ((!empty($json['responseStatus']) && $json['responseStatus'] != 200)
  318. || count($json['data']['translations']) == 0) {
  319. print "Error: ".$json['responseStatus']." ".$url."\n";
  320. return false;
  321. }
  322. $rep=$json['data']['translations'][0]['translatedText'];
  323. $rep=preg_replace('/SSSSS/i', '%s', $rep);
  324. $rep=preg_replace('/NNNNN/i', '\n\n', $rep);
  325. $rep=preg_replace('/&#39;/i', '\'', $rep);
  326. //print "OK ".join('',$src_texts).' => '.$rep."\n";
  327. return $rep;
  328. }
  329. }