strip_language_file.php 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345
  1. #!/usr/bin/env php
  2. <?php
  3. /* Copyright (C) 2014 by FromDual GmbH, licensed under GPL v2
  4. * Copyright (C) 2014 Laurent Destailleur <eldy@users.sourceforge.net>
  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. * (at your option) 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. * -----
  20. *
  21. * Compares a secondary language translation file with its primary
  22. * language file and strips redundant translations.
  23. *
  24. * Todo: Check if it works with multi byte (mb_*) character sets!
  25. *
  26. * Usage:
  27. * cd htdocs/langs
  28. * ./dev/translation/strip_language_file.php <primary_lang_dir> <secondary_lang_dir> [file.lang|all]
  29. *
  30. * To rename all .delta files, you can do
  31. * for fic in `ls *.delta`; do f=`echo $fic | sed -e 's/\.delta//'`; echo $f; mv $f.delta $f; done
  32. *
  33. * Rules:
  34. * secondary string == primary string -> strip
  35. * secondary string redundant -> strip and warning
  36. * secondary string not in primary -> strip and warning
  37. * secondary string has no value -> strip and warning
  38. * secondary string != primary string -> secondary.lang.delta
  39. */
  40. /**
  41. * \file dev/translation/strip_language_file.php
  42. * \ingroup dev
  43. * \brief This script clean sub-languages from duplicate keys-values
  44. */
  45. $sapi_type = php_sapi_name();
  46. $script_file = basename(__FILE__);
  47. $path=dirname(__FILE__).'/';
  48. // Test if batch mode
  49. if (substr($sapi_type, 0, 3) == 'cgi') {
  50. echo "Error: You are using PHP for CGI. To execute ".$script_file." from command line, you must use PHP for CLI mode.\n";
  51. exit;
  52. }
  53. $rc = 0;
  54. // Get and check arguments
  55. $lPrimary = isset($argv[1]) ? $argv[1] : '';
  56. $lSecondary = isset($argv[2]) ? $argv[2] : '';
  57. $lEnglish = 'en_US';
  58. $filesToProcess = isset($argv[3]) ? $argv[3] : '';
  59. if (empty($lPrimary) || empty($lSecondary) || empty($filesToProcess)) {
  60. $rc = 1;
  61. $msg = '***** Script to clean language files *****'."\n";
  62. $msg.= 'Usage: ./dev/translation/strip_language_file.php xx_XX xx_YY [file.lang|all]'."\n";
  63. print $msg . "(rc=$rc).\n";
  64. exit($rc);
  65. }
  66. $aPrimary = array();
  67. $aSecondary = array();
  68. $aEnglish = array();
  69. // Define array $filesToProcess
  70. if ($filesToProcess == 'all') {
  71. $dir = new DirectoryIterator('htdocs/langs/'.$lPrimary);
  72. while ($dir->valid()) {
  73. if (!$dir->isDot() && $dir->isFile() && ! preg_match('/^\./', $dir->getFilename())) {
  74. $files[] = $dir->getFilename();
  75. }
  76. $dir->next();
  77. }
  78. $filesToProcess=$files;
  79. } else {
  80. $filesToProcess=explode(',', $filesToProcess);
  81. }
  82. // Arguments should be OK here.
  83. // Loop on each file
  84. foreach ($filesToProcess as $fileToProcess) {
  85. $lPrimaryFile = 'htdocs/langs/'.$lPrimary.'/'.$fileToProcess;
  86. $lSecondaryFile = 'htdocs/langs/'.$lSecondary.'/'.$fileToProcess;
  87. $lEnglishFile = 'htdocs/langs/'.$lEnglish.'/'.$fileToProcess;
  88. $output = $lSecondaryFile . '.delta';
  89. print "---- Process language file ".$lSecondaryFile."\n";
  90. if (! is_readable($lPrimaryFile)) {
  91. $rc = 2;
  92. $msg = "Cannot read primary language file $lPrimaryFile.";
  93. print $msg . " (rc=$rc).\n";
  94. exit($rc);
  95. }
  96. if (! is_readable($lSecondaryFile)) {
  97. $rc = 3;
  98. $msg = "Cannot read secondary language file $lSecondaryFile. We discard this file.";
  99. print $msg . "\n";
  100. continue;
  101. }
  102. if (! is_readable($lEnglishFile)) {
  103. $rc = 3;
  104. $msg = "Cannot read english language file $lEnglishFile. We discard this file.";
  105. print $msg . "\n";
  106. continue;
  107. }
  108. // Start reading and parsing Secondary
  109. if ($handle = fopen($lSecondaryFile, 'r')) {
  110. print "Read Secondary File $lSecondaryFile:\n";
  111. $cnt = 0;
  112. while (($line = fgets($handle)) !== false) {
  113. $cnt++;
  114. // strip comments
  115. if (preg_match("/^\w*#/", $line)) {
  116. continue;
  117. }
  118. // strip empty lines
  119. if (preg_match("/^\w*$/", $line)) {
  120. continue;
  121. }
  122. $a = mb_split('=', trim($line), 2);
  123. if (count($a) != 2) {
  124. print "ERROR in file $lSecondaryFile, line $cnt: " . trim($line) . "\n";
  125. continue;
  126. }
  127. list($key, $value) = $a;
  128. // key is redundant
  129. if (array_key_exists($key, $aSecondary)) {
  130. print "Key $key is redundant in file $lSecondaryFile (line: $cnt).\n";
  131. continue;
  132. }
  133. // String has no value
  134. if ($value == '') {
  135. print "Key $key has no value in file $lSecondaryFile (line: $cnt).\n";
  136. continue;
  137. }
  138. $aSecondary[$key] = trim($value);
  139. }
  140. if (! feof($handle)) {
  141. $rc = 5;
  142. $msg = "Unexpected fgets() fail";
  143. print $msg . " (rc=$rc).\n";
  144. exit($rc);
  145. }
  146. fclose($handle);
  147. } else {
  148. $rc = 6;
  149. $msg = "Cannot open file $lSecondaryFile";
  150. print $msg . " (rc=$rc).\n";
  151. exit($rc);
  152. }
  153. // Start reading and parsing English
  154. $aEnglish = array();
  155. if ($handle = fopen($lEnglishFile, 'r')) {
  156. print "Read English File $lEnglishFile:\n";
  157. $cnt = 0;
  158. while (($line = fgets($handle)) !== false) {
  159. $cnt++;
  160. // strip comments
  161. if (preg_match("/^\w*#/", $line)) {
  162. continue;
  163. }
  164. // strip empty lines
  165. if (preg_match("/^\w*$/", $line)) {
  166. continue;
  167. }
  168. $a = mb_split('=', trim($line), 2);
  169. if (count($a) != 2) {
  170. print "ERROR in file $lEnglishFile, line $cnt: " . trim($line) . "\n";
  171. continue;
  172. }
  173. list($key, $value) = $a;
  174. // key is redundant
  175. if (array_key_exists($key, $aEnglish)) {
  176. print "Key $key is redundant in file $lEnglishFile (line: $cnt).\n";
  177. continue;
  178. }
  179. // String has no value
  180. if ($value == '') {
  181. print "Key $key has no value in file $lEnglishFile (line: $cnt).\n";
  182. continue;
  183. }
  184. $aEnglish[$key] = trim($value);
  185. }
  186. if (! feof($handle)) {
  187. $rc = 5;
  188. $msg = "Unexpected fgets() fail";
  189. print $msg . " (rc=$rc).\n";
  190. exit($rc);
  191. }
  192. fclose($handle);
  193. } else {
  194. $rc = 6;
  195. $msg = "Cannot open file $lEnglishFile";
  196. print $msg . " (rc=$rc).\n";
  197. exit($rc);
  198. }
  199. // Start reading and parsing Primary. See rules in header!
  200. $arrayofkeytoalwayskeep=array('DIRECTION','FONTFORPDF','FONTSIZEFORPDF','SeparatorDecimal','SeparatorThousand');
  201. if ($handle = fopen($lPrimaryFile, 'r')) {
  202. if (! $oh = fopen($output, 'w')) {
  203. print "ERROR in writing to file ".$output."\n";
  204. exit;
  205. }
  206. print "Read Primary File ".$lPrimaryFile." and write ".$output.":\n";
  207. fwrite($oh, "# Dolibarr language file - Source file is en_US - ".(preg_replace('/\.lang$/', '', $fileToProcess))."\n");
  208. $fileFirstFound = array();
  209. $lineFirstFound = array();
  210. $cnt = 0;
  211. while (($line = fgets($handle)) !== false) {
  212. $cnt++;
  213. // strip comments
  214. if (preg_match("/^\w*#/", $line)) {
  215. continue;
  216. }
  217. // strip empty lines
  218. if (preg_match("/^\w*$/", $line)) {
  219. continue;
  220. }
  221. $a = mb_split('=', trim($line), 2);
  222. if (count($a) != 2) {
  223. print "ERROR in file $lPrimaryFile, line $cnt: " . trim($line) . "\n";
  224. continue;
  225. }
  226. list($key, $value) = $a;
  227. // key is redundant
  228. if (array_key_exists($key, $aPrimary)) {
  229. print "Key $key is redundant in file $lPrimaryFile (line: $cnt)";
  230. if (!empty($fileFirstFound[$key])) {
  231. print " - Already found into ".$fileFirstFound[$key];
  232. print " (line: ".$lineFirstFound[$key].").\n";
  233. } else {
  234. $fileFirstFound[$key] = $fileToProcess;
  235. $lineFirstFound[$key] = $cnt;
  236. print " - Already found into main file.\n";
  237. }
  238. continue;
  239. } else {
  240. $fileFirstFound[$key] = $fileToProcess;
  241. $lineFirstFound[$key] = $cnt;
  242. }
  243. // String has no value
  244. if ($value == '') {
  245. print "Key $key has no value in file $lPrimaryFile (line: $cnt).\n";
  246. continue;
  247. }
  248. $aPrimary[$key] = trim($value);
  249. $fileFirstFound[$key] = $fileToProcess;
  250. $lineFirstFound[$key] = $cnt;
  251. // ----- Process output now -----
  252. //print "Found primary key = ".$key."\n";
  253. // Key not in other file
  254. if (in_array($key, $arrayofkeytoalwayskeep) || preg_match('/^FormatDate/', $key) || preg_match('/^FormatHour/', $key)) {
  255. //print "Key $key is a key we always want to see into secondary file (line: $cnt).\n";
  256. } elseif (! array_key_exists($key, $aSecondary)) {
  257. //print "Key $key does NOT exist in secondary language (line: $cnt).\n";
  258. continue;
  259. }
  260. // String exists in both files and value into alternative language differs from main language but also from english files
  261. // so we keep it.
  262. if ((!empty($aSecondary[$key]) && $aSecondary[$key] != $aPrimary[$key]
  263. && !empty($aEnglish[$key]) && $aSecondary[$key] != $aEnglish[$key])
  264. || in_array($key, $arrayofkeytoalwayskeep) || preg_match('/^FormatDate/', $key) || preg_match('/^FormatHour/', $key)
  265. ) {
  266. //print "Key $key differs (aSecondary=".$aSecondary[$key].", aPrimary=".$aPrimary[$key].", aEnglish=".$aEnglish[$key].") so we add it into new secondary language (line: $cnt).\n";
  267. fwrite($oh, $key."=".(empty($aSecondary[$key]) ? $aPrimary[$key] : $aSecondary[$key])."\n");
  268. }
  269. }
  270. if (! feof($handle)) {
  271. $rc = 7;
  272. $msg = "Unexpected fgets() fail";
  273. print $msg . " (rc=$rc).\n";
  274. exit($rc);
  275. }
  276. fclose($oh);
  277. fclose($handle);
  278. } else {
  279. $rc = 8;
  280. $msg = "Cannot open file $lPrimaryFile";
  281. print $msg . " (rc=$rc).\n";
  282. exit($rc);
  283. }
  284. print "Output can be found at $output.\n";
  285. print "To rename all .delta files, you can do:\n";
  286. print '> for fic in `ls htdocs/langs/'.$lSecondary.'/*.delta`; do f=`echo $fic | sed -e \'s/\.delta//\'`; echo $f; mv $f.delta $f; done'."\n";
  287. }
  288. return 0;