sanity_check_en_langfiles.php 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391
  1. #!/usr/bin/env php
  2. <?php
  3. /* Copyright (c) 2015 Tommaso Basilici <t.basilici@19.coop>
  4. * Copyright (c) 2015 Laurent Destailleur <eldy@destailleur.fr>
  5. * Copyright (C) 2014-2016 Juanjo Menent <jmenent@2byte.es>
  6. *
  7. * This program is free software; you can redistribute it and/or modify
  8. * it under the terms of the GNU General Public License as published by
  9. * the Free Software Foundation; either version 2 of the License, or
  10. * (at your option) any later version.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public License
  18. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  19. */
  20. $sapi_type = php_sapi_name();
  21. $script_file = basename(__FILE__);
  22. $path=dirname(__FILE__).'/';
  23. $web=0;
  24. // Test if batch mode
  25. if (substr($sapi_type, 0, 3) == 'cgi')
  26. {
  27. $web=1;
  28. }
  29. if ($web)
  30. {
  31. echo "<html>";
  32. echo "<head>";
  33. echo "<STYLE type=\"text/css\">
  34. table {
  35. background: #f5f5f5;
  36. border-collapse: separate;
  37. box-shadow: inset 0 1px 0 #fff;
  38. font-size: 12px;
  39. line-height: 24px;
  40. margin: 30px auto;
  41. text-align: left;
  42. width: 800px;
  43. }
  44. th {
  45. background-color: #777;
  46. border-left: 1px solid #555;
  47. border-right: 1px solid #777;
  48. border-top: 1px solid #555;
  49. border-bottom: 1px solid #333;
  50. color: #fff;
  51. font-weight: bold;
  52. padding: 10px 15px;
  53. position: relative;
  54. text-shadow: 0 1px 0 #000;
  55. }
  56. td {
  57. border-right: 1px solid #fff;
  58. border-left: 1px solid #e8e8e8;
  59. border-top: 1px solid #fff;
  60. border-bottom: 1px solid #e8e8e8;
  61. padding: 10px 15px;
  62. position: relative;
  63. }
  64. tr {
  65. background-color: #f1f1f1;
  66. }
  67. tr:nth-child(odd) td {
  68. background-color: #f1f1f1;
  69. }
  70. </STYLE>";
  71. echo "<body>";
  72. }
  73. echo "If you call this with argument \"unused=true\" it searches for the translation strings that exist in en_US but are never used.\n";
  74. if ($web) print "<br>";
  75. echo "IMPORTANT: that can take quite a lot of time (up to 10 minutes), you need to tune the max_execution_time on your php.ini accordingly.\n";
  76. if ($web) print "<br>";
  77. // STEP 1 - Search duplicates keys
  78. // directory containing the php and lang files
  79. $htdocs = $path."../../htdocs/";
  80. $scripts = $path."../../scripts/";
  81. // directory containing the english lang files
  82. $workdir = $htdocs."langs/en_US/";
  83. $files = scandir($workdir);
  84. if (empty($files))
  85. {
  86. echo "Can't scan workdir = ".$workdir;
  87. exit;
  88. }
  89. $dups=array();
  90. $exludefiles = array('.','..','README');
  91. $files = array_diff($files,$exludefiles);
  92. // To force a file: $files=array('myfile.lang');
  93. if (isset($argv[2]))
  94. {
  95. $files = array($argv[2]);
  96. }
  97. $langstrings_3d = array();
  98. $langstrings_full = array();
  99. foreach ($files AS $file) {
  100. $path_file = pathinfo($file);
  101. // we're only interested in .lang files
  102. if ($path_file['extension']=='lang') {
  103. $content = file($workdir.$file);
  104. foreach ($content AS $line => $row) {
  105. // don't want comment lines
  106. if (substr($row,0,1) !== '#') {
  107. // don't want lines without the separator (why should those even be here, anyway...)
  108. if (strpos($row,'=')!==false) {
  109. $row_array = explode('=',$row); // $row_array[0] = key
  110. $langstrings_3d[$path_file['basename']][$line+1]=$row_array[0];
  111. $langstrings_3dtrans[$path_file['basename']][$line+1]=$row_array[1];
  112. $langstrings_full[]=$row_array[0];
  113. $langstrings_dist[$row_array[0]]=$row;
  114. }
  115. }
  116. }
  117. }
  118. }
  119. foreach ($langstrings_3d AS $filename => $file)
  120. {
  121. foreach ($file AS $linenum => $value)
  122. {
  123. $keys = array_keys($langstrings_full, $value);
  124. if (count($keys)>1)
  125. {
  126. foreach ($keys AS $key) {
  127. $dups[$value][$filename][$linenum] = trim($langstrings_3dtrans[$filename][$linenum]);
  128. }
  129. }
  130. }
  131. }
  132. if ($web) print "<h2>";
  133. print "Duplicate strings in lang files in $workdir - ".count($dups)." found\n";
  134. if ($web) print "</h2>";
  135. if ($web)
  136. {
  137. echo '<table border_bottom="1">'."\n";
  138. echo "<thead><tr><th align=\"center\">#</th><th>String</th><th>File and lines</th></thead>\n";
  139. echo "<tbody>\n";
  140. }
  141. $sduplicateinsamefile='';
  142. $sinmainandother='';
  143. $sininstallandadmin='';
  144. $sother='';
  145. $count = 0;
  146. foreach ($dups as $string => $pages)
  147. {
  148. $count++;
  149. $s='';
  150. // Keyword $string
  151. if ($web) $s.="<tr>";
  152. if ($web) $s.="<td align=\"center\">";
  153. if ($web) $s.=$count;
  154. if ($web) $s.="</td>";
  155. if ($web) $s.="<td>";
  156. $s.=$string;
  157. if ($web) $s.="</td>";
  158. if ($web) $s.="<td>";
  159. if (! $web) $s.= ' : ';
  160. // Loop on each files keyword was found
  161. $duplicateinsamefile=0;
  162. $inmain=0;
  163. $inadmin=0;
  164. foreach ($pages AS $file => $lines)
  165. {
  166. if ($file == 'main.lang') { $inmain=1; $inadmin=0; }
  167. if ($file == 'admin.lang' && ! $inmain) { $inadmin=1; }
  168. $s.=$file." ";
  169. // Loop on each line keword was found into file.
  170. $listoffilesforthisentry=array();
  171. foreach ($lines as $line => $translatedvalue)
  172. {
  173. if (! empty($listoffilesforthisentry[$file])) $duplicateinsamefile=1;
  174. $listoffilesforthisentry[$file]=1;
  175. $s.= "(".$line." - ".htmlentities($translatedvalue).") ";
  176. }
  177. if ($web) $s.="<br>";
  178. }
  179. if ($web) $s.="</td></tr>";
  180. $s.="\n";
  181. if ($duplicateinsamefile) $sduplicateinsamefile .= $s;
  182. else if ($inmain) $sinmainandother .= $s;
  183. else if ($inadmin) $sininstallandadmin .= $s;
  184. else $sother .= $s;
  185. }
  186. if (! $web) print "\n***** Entries duplicated in same file\n";
  187. print $sduplicateinsamefile;
  188. if (! $web && empty($sduplicateinsamefile)) print "None\n";
  189. if (! $web) print "\n";
  190. if (! $web) print "***** Entries in main and another (keep only entry in main)\n";
  191. print $sinmainandother;
  192. if (! $web && empty($sinmainandother)) print "None\n";
  193. if (! $web) print "\n";
  194. if (! $web) print "***** Entries in admin and another\n";
  195. print $sininstallandadmin;
  196. if (! $web && empty($sininstallandadmin)) print "None\n";
  197. if (! $web) print "\n";
  198. if (! $web) print "***** Other\n";
  199. print $sother;
  200. if (! $web && empty($sother)) print "None\n";
  201. if (! $web) print "\n";
  202. if ($web)
  203. {
  204. echo "</tbody>\n";
  205. echo "</table>\n";
  206. }
  207. // STEP 2 - Search key not used
  208. if ((! empty($_REQUEST['unused']) && $_REQUEST['unused'] == 'true') || (isset($argv[1]) && $argv[1]=='unused=true'))
  209. {
  210. print "***** Strings in en_US that are never used:\n";
  211. $unused=array();
  212. foreach ($langstrings_dist AS $value => $line)
  213. {
  214. $qualifiedforclean=1;
  215. // Check if we must keep this key to be into file for removal
  216. if (preg_match('/^Module\d+/', $value)) $qualifiedforclean=0;
  217. if (preg_match('/^Permission\d+/', $value)) $qualifiedforclean=0;
  218. if (preg_match('/^PermissionAdvanced\d+/', $value)) $qualifiedforclean=0;
  219. if (preg_match('/^ProfId\d+/', $value)) $qualifiedforclean=0;
  220. if (preg_match('/^Delays_/', $value)) $qualifiedforclean=0;
  221. if (preg_match('/^BarcodeDesc/', $value)) $qualifiedforclean=0;
  222. if (preg_match('/^Extrafield/', $value)) $qualifiedforclean=0;
  223. if (preg_match('/^LocalTax/', $value)) $qualifiedforclean=0;
  224. if (preg_match('/^Country/', $value)) $qualifiedforclean=0;
  225. if (preg_match('/^Civility/', $value)) $qualifiedforclean=0;
  226. if (preg_match('/^Currency/', $value)) $qualifiedforclean=0;
  227. if (preg_match('/^DemandReasonTypeSRC/', $value)) $qualifiedforclean=0;
  228. if (preg_match('/^PaperFormat/', $value)) $qualifiedforclean=0;
  229. if (preg_match('/^Duration/', $value)) $qualifiedforclean=0;
  230. if (preg_match('/^AmountLT/', $value)) $qualifiedforclean=0;
  231. if (preg_match('/^TotalLT/', $value)) $qualifiedforclean=0;
  232. if (preg_match('/^Month/', $value)) $qualifiedforclean=0;
  233. if (preg_match('/^MonthShort/', $value)) $qualifiedforclean=0;
  234. if (preg_match('/^Day\d/', $value)) $qualifiedforclean=0;
  235. if (preg_match('/^Short/', $value)) $qualifiedforclean=0;
  236. if (preg_match('/^ExportDataset_/', $value)) $qualifiedforclean=0;
  237. if (preg_match('/^ImportDataset_/', $value)) $qualifiedforclean=0;
  238. if (preg_match('/^ActionAC_/', $value)) $qualifiedforclean=0;
  239. if (preg_match('/^TypeLocaltax/', $value)) $qualifiedforclean=0;
  240. if (preg_match('/^StatusProspect/', $value)) $qualifiedforclean=0;
  241. if (preg_match('/^PL_/', $value)) $qualifiedforclean=0;
  242. if (preg_match('/^TE_/', $value)) $qualifiedforclean=0;
  243. if (preg_match('/^JuridicalStatus/', $value)) $qualifiedforclean=0;
  244. if (preg_match('/^CalcMode/', $value)) $qualifiedforclean=0;
  245. if (preg_match('/^newLT/', $value)) $qualifiedforclean=0;
  246. if (preg_match('/^LT[0-9]/', $value)) $qualifiedforclean=0;
  247. if (preg_match('/^TypeContact_contrat_/', $value)) $qualifiedforclean=0;
  248. if (preg_match('/^ErrorPriceExpression/', $value)) $qualifiedforclean=0;
  249. if (preg_match('/^Language_/', $value)) $qualifiedforclean=0;
  250. if (preg_match('/^DescADHERENT_/', $value)) $qualifiedforclean=0;
  251. if (preg_match('/^SubmitTranslation/', $value)) $qualifiedforclean=0;
  252. if (preg_match('/^ModuleCompanyCode/', $value)) $qualifiedforclean=0;
  253. if (preg_match('/InDolibarr$/', $value)) $qualifiedforclean=0;
  254. // admin.lang
  255. if (preg_match('/^DAV_ALLOW_PUBLIC_DIR/i', $value)) $qualifiedforclean=0;
  256. if (preg_match('/^DAV_ALLOW_ECM_DIR/i', $value)) $qualifiedforclean=0;
  257. // boxes.lang
  258. if (preg_match('/^BoxTitleLast/', $value)) $qualifiedforclean=0;
  259. if (preg_match('/^BoxTitleLatest/', $value)) $qualifiedforclean=0;
  260. // install.lang
  261. if (preg_match('/^KeepDefaultValues/', $value)) $qualifiedforclean=0;
  262. // mail.lang
  263. if (preg_match('/MailingModuleDesc/i', $value)) $qualifiedforclean=0;
  264. // main.lang
  265. if (preg_match('/^Duration/', $value)) $qualifiedforclean=0;
  266. if (preg_match('/^FormatDate/', $value)) $qualifiedforclean=0;
  267. if (preg_match('/^DateFormat/', $value)) $qualifiedforclean=0;
  268. if (preg_match('/^.b$/', $value)) $qualifiedforclean=0;
  269. if (preg_match('/^.*Bytes$/', $value)) $qualifiedforclean=0;
  270. if (preg_match('/^NoteSomeFeaturesAreDisabled/', $value)) $qualifiedforclean=0;
  271. if (preg_match('/^(DoTest|Under|Limits|Cards|CurrentValue|DateLimit|DateAndHour|NbOfLines|NbOfObjects|NbOfReferes|TotalTTCShort|VATs)/', $value)) $qualifiedforclean=0;
  272. // modulebuilder
  273. if (preg_match('/^ModuleBuilderDesc/', $value)) $qualifiedforclean=0;
  274. // orders
  275. if (preg_match('/^OrderSource/', $value)) $qualifiedforclean=0;
  276. if (preg_match('/^TypeContact_/', $value)) $qualifiedforclean=0;
  277. // other.lang
  278. if (preg_match('/^Notify_/', $value)) $qualifiedforclean=0;
  279. if (preg_match('/^PredefinedMail/', $value)) $qualifiedforclean=0;
  280. if (preg_match('/^DemoCompany/', $value)) $qualifiedforclean=0;
  281. if (preg_match('/^WeightUnit/', $value)) $qualifiedforclean=0;
  282. if (preg_match('/^LengthUnit/', $value)) $qualifiedforclean=0;
  283. if (preg_match('/^SurfaceUnit/', $value)) $qualifiedforclean=0;
  284. if (preg_match('/^VolumeUnit/', $value)) $qualifiedforclean=0;
  285. if (preg_match('/^SizeUnit/', $value)) $qualifiedforclean=0;
  286. if (preg_match('/^EMailText/', $value)) $qualifiedforclean=0;
  287. if (preg_match('/ById$/', $value)) $qualifiedforclean=0;
  288. if (preg_match('/ByLogin$/', $value)) $qualifiedforclean=0;
  289. // printing
  290. if (preg_match('/PrintingDriverDesc$/', $value)) $qualifiedforclean=0;
  291. if (preg_match('/PrintTestDesc$/', $value)) $qualifiedforclean=0;
  292. // products
  293. if (preg_match('/GlobalVariableUpdaterType$/', $value)) $qualifiedforclean=0;
  294. if (preg_match('/GlobalVariableUpdaterHelp$/', $value)) $qualifiedforclean=0;
  295. if (preg_match('/OppStatus/', $value)) $qualifiedforclean=0;
  296. if (preg_match('/AvailabilityType/', $value)) $qualifiedforclean=0;
  297. if (preg_match('/CardProduct/', $value)) $qualifiedforclean=0;
  298. if (preg_match('/sms/i', $value)) $qualifiedforclean=0;
  299. if (preg_match('/TF_/i', $value)) $qualifiedforclean=0;
  300. if (preg_match('/WithBankUsing/i', $value)) $qualifiedforclean=0;
  301. if (preg_match('/descWORKFLOW_/i', $value)) $qualifiedforclean=0;
  302. if (! $qualifiedforclean)
  303. {
  304. continue;
  305. }
  306. //$search = '\'trans("'.$value.'")\'';
  307. $search = '-e "\''.$value.'\'" -e \'"'.$value.'"\' -e "('.$value.')" -e "('.$value.',"';
  308. $string = 'grep -R -m 1 -F --exclude=includes/* --include=*.php '.$search.' '.$htdocs.'* '.$scripts.'*';
  309. //print $string."<br>\n";
  310. exec($string,$output);
  311. if (empty($output)) {
  312. $unused[$value] = $line;
  313. echo $line; // $trad contains the \n
  314. }
  315. else
  316. {
  317. unset($output);
  318. //print 'X'.$output.'Y';
  319. }
  320. }
  321. if (empty($unused)) print "No string not used found.\n";
  322. else
  323. {
  324. $filetosave='/tmp/'.($argv[2]?$argv[2]:"").'notused.lang';
  325. print "Strings in en_US that are never used are saved into file ".$filetosave.":\n";
  326. file_put_contents($filetosave, implode("",$unused));
  327. print "To remove from original file, run command :\n";
  328. if (($argv[2]?$argv[2]:"")) print 'cd htdocs/langs/en_US; mv '.($argv[2]?$argv[2]:"")." ".($argv[2]?$argv[2]:"").".tmp; ";
  329. print "diff ".($argv[2]?$argv[2]:"").".tmp ".$filetosave." | grep \< | cut -b 3- > ".($argv[2]?$argv[2]:"");
  330. if (($argv[2]?$argv[2]:"")) print "; rm ".($argv[2]?$argv[2]:"").".tmp;\n";
  331. }
  332. }
  333. echo "\n";
  334. if ($web)
  335. {
  336. echo "</body>\n";
  337. echo "</html>\n";
  338. }
  339. exit;