sanity_check_en_langfiles.php 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591
  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 3 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 <https://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. $web=1;
  27. }
  28. if ($web) {
  29. echo "<html>";
  30. echo "<head>";
  31. echo "<STYLE type=\"text/css\">
  32. table {
  33. background: #f5f5f5;
  34. border-collapse: separate;
  35. box-shadow: inset 0 1px 0 #fff;
  36. font-size: 12px;
  37. line-height: 24px;
  38. margin: 30px auto;
  39. text-align: left;
  40. width: 800px;
  41. }
  42. th {
  43. background-color: #777;
  44. border-left: 1px solid #555;
  45. border-right: 1px solid #777;
  46. border-top: 1px solid #555;
  47. border-bottom: 1px solid #333;
  48. color: #fff;
  49. font-weight: bold;
  50. padding: 10px 15px;
  51. position: relative;
  52. text-shadow: 0 1px 0 #000;
  53. }
  54. td {
  55. border-right: 1px solid #fff;
  56. border-left: 1px solid #e8e8e8;
  57. border-top: 1px solid #fff;
  58. border-bottom: 1px solid #e8e8e8;
  59. padding: 10px 15px;
  60. position: relative;
  61. }
  62. tr {
  63. background-color: #f1f1f1;
  64. }
  65. tr:nth-child(odd) td {
  66. background-color: #f1f1f1;
  67. }
  68. </STYLE>";
  69. echo "<body>";
  70. }
  71. 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";
  72. if ($web) {
  73. print "<br>";
  74. }
  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) {
  77. print "<br>";
  78. }
  79. // STEP 1 - Search duplicates keys
  80. // directory containing the php and lang files
  81. $htdocs = $path."../../htdocs/";
  82. $scripts = $path."../../scripts/";
  83. // directory containing the english lang files
  84. $workdir = $htdocs."langs/en_US/";
  85. $files = scandir($workdir);
  86. if (empty($files)) {
  87. echo "Can't scan workdir = ".$workdir;
  88. exit;
  89. }
  90. $dups=array();
  91. $exludefiles = array('.','..','README');
  92. $files = array_diff($files, $exludefiles);
  93. // To force a file: $files=array('myfile.lang');
  94. if (isset($argv[2])) {
  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. foreach ($file as $linenum => $value) {
  121. $keys = array_keys($langstrings_full, $value);
  122. if (count($keys)>1) {
  123. foreach ($keys as $key) {
  124. $dups[$value][$filename][$linenum] = trim($langstrings_3dtrans[$filename][$linenum]);
  125. }
  126. }
  127. }
  128. }
  129. if ($web) {
  130. print "<h2>";
  131. }
  132. print "Duplicate strings in lang files in $workdir - ".count($dups)." found\n";
  133. if ($web) {
  134. print "</h2>";
  135. }
  136. if ($web) {
  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. $count++;
  148. $s='';
  149. // Keyword $string
  150. if ($web) {
  151. $s.="<tr>";
  152. }
  153. if ($web) {
  154. $s.="<td align=\"center\">";
  155. }
  156. if ($web) {
  157. $s.=$count;
  158. }
  159. if ($web) {
  160. $s.="</td>";
  161. }
  162. if ($web) {
  163. $s.="<td>";
  164. }
  165. $s.=$string;
  166. if ($web) {
  167. $s.="</td>";
  168. }
  169. if ($web) {
  170. $s.="<td>";
  171. }
  172. if (! $web) {
  173. $s.= ' : ';
  174. }
  175. // Loop on each files keyword was found
  176. $duplicateinsamefile=0;
  177. $inmain=0;
  178. $inadmin=0;
  179. foreach ($pages as $file => $lines) {
  180. if ($file == 'main.lang') {
  181. $inmain=1; $inadmin=0;
  182. }
  183. if ($file == 'admin.lang' && ! $inmain) {
  184. $inadmin=1;
  185. }
  186. $s.=$file." ";
  187. // Loop on each line keword was found into file.
  188. $listoffilesforthisentry=array();
  189. foreach ($lines as $line => $translatedvalue) {
  190. if (! empty($listoffilesforthisentry[$file])) {
  191. $duplicateinsamefile=1;
  192. }
  193. $listoffilesforthisentry[$file]=1;
  194. $s.= "(".$line." - ".htmlentities($translatedvalue).") ";
  195. }
  196. if ($web) {
  197. $s.="<br>";
  198. }
  199. }
  200. if ($web) {
  201. $s.="</td></tr>";
  202. }
  203. $s.="\n";
  204. if ($duplicateinsamefile) {
  205. $sduplicateinsamefile .= $s;
  206. } elseif ($inmain) {
  207. $sinmainandother .= $s;
  208. } elseif ($inadmin) {
  209. $sininstallandadmin .= $s;
  210. } else {
  211. $sother .= $s;
  212. }
  213. }
  214. if (! $web) {
  215. print "\n***** Entries duplicated in same file\n";
  216. }
  217. print $sduplicateinsamefile;
  218. if (! $web && empty($sduplicateinsamefile)) {
  219. print "None\n";
  220. }
  221. if (! $web) {
  222. print "\n";
  223. }
  224. if (! $web) {
  225. print "***** Entries in main and another (keep only entry in main)\n";
  226. }
  227. print $sinmainandother;
  228. if (! $web && empty($sinmainandother)) {
  229. print "None\n";
  230. }
  231. if (! $web) {
  232. print "\n";
  233. }
  234. if (! $web) {
  235. print "***** Entries in admin and another\n";
  236. }
  237. print $sininstallandadmin;
  238. if (! $web && empty($sininstallandadmin)) {
  239. print "None\n";
  240. }
  241. if (! $web) {
  242. print "\n";
  243. }
  244. if (! $web) {
  245. print "***** Other\n";
  246. }
  247. print $sother;
  248. if (! $web && empty($sother)) {
  249. print "None\n";
  250. }
  251. if (! $web) {
  252. print "\n";
  253. }
  254. if ($web) {
  255. echo "</tbody>\n";
  256. echo "</table>\n";
  257. }
  258. // STEP 2 - Search key not used
  259. if ((! empty($_REQUEST['unused']) && $_REQUEST['unused'] == 'true') || (isset($argv[1]) && $argv[1]=='unused=true')) {
  260. print "***** Strings in en_US that are never used:\n";
  261. $unused=array();
  262. foreach ($langstrings_dist as $value => $line) {
  263. $qualifiedforclean=1;
  264. // Check if we must keep this key to be into file for removal
  265. if (preg_match('/^Module\d+/', $value)) {
  266. $qualifiedforclean=0;
  267. }
  268. if (preg_match('/^Permission\d+/', $value)) {
  269. $qualifiedforclean=0;
  270. }
  271. if (preg_match('/^PermissionAdvanced\d+/', $value)) {
  272. $qualifiedforclean=0;
  273. }
  274. if (preg_match('/^ProfId\d+/', $value)) {
  275. $qualifiedforclean=0;
  276. }
  277. if (preg_match('/^Delays_/', $value)) {
  278. $qualifiedforclean=0;
  279. }
  280. if (preg_match('/^BarcodeDesc/', $value)) {
  281. $qualifiedforclean=0;
  282. }
  283. if (preg_match('/^Extrafield/', $value)) {
  284. $qualifiedforclean=0;
  285. }
  286. if (preg_match('/^LocalTax/', $value)) {
  287. $qualifiedforclean=0;
  288. }
  289. if (preg_match('/^Country/', $value)) {
  290. $qualifiedforclean=0;
  291. }
  292. if (preg_match('/^Civility/', $value)) {
  293. $qualifiedforclean=0;
  294. }
  295. if (preg_match('/^Currency/', $value)) {
  296. $qualifiedforclean=0;
  297. }
  298. if (preg_match('/^DemandReasonTypeSRC/', $value)) {
  299. $qualifiedforclean=0;
  300. }
  301. if (preg_match('/^PaperFormat/', $value)) {
  302. $qualifiedforclean=0;
  303. }
  304. if (preg_match('/^Duration/', $value)) {
  305. $qualifiedforclean=0;
  306. }
  307. if (preg_match('/^AmountLT/', $value)) {
  308. $qualifiedforclean=0;
  309. }
  310. if (preg_match('/^TotalLT/', $value)) {
  311. $qualifiedforclean=0;
  312. }
  313. if (preg_match('/^Month/', $value)) {
  314. $qualifiedforclean=0;
  315. }
  316. if (preg_match('/^MonthShort/', $value)) {
  317. $qualifiedforclean=0;
  318. }
  319. if (preg_match('/^Day\d/', $value)) {
  320. $qualifiedforclean=0;
  321. }
  322. if (preg_match('/^Short/', $value)) {
  323. $qualifiedforclean=0;
  324. }
  325. if (preg_match('/^ExportDataset_/', $value)) {
  326. $qualifiedforclean=0;
  327. }
  328. if (preg_match('/^ImportDataset_/', $value)) {
  329. $qualifiedforclean=0;
  330. }
  331. if (preg_match('/^ActionAC_/', $value)) {
  332. $qualifiedforclean=0;
  333. }
  334. if (preg_match('/^TypeLocaltax/', $value)) {
  335. $qualifiedforclean=0;
  336. }
  337. if (preg_match('/^StatusProspect/', $value)) {
  338. $qualifiedforclean=0;
  339. }
  340. if (preg_match('/^PL_/', $value)) {
  341. $qualifiedforclean=0;
  342. }
  343. if (preg_match('/^TE_/', $value)) {
  344. $qualifiedforclean=0;
  345. }
  346. if (preg_match('/^JuridicalStatus/', $value)) {
  347. $qualifiedforclean=0;
  348. }
  349. if (preg_match('/^CalcMode/', $value)) {
  350. $qualifiedforclean=0;
  351. }
  352. if (preg_match('/^newLT/', $value)) {
  353. $qualifiedforclean=0;
  354. }
  355. if (preg_match('/^LT[0-9]/', $value)) {
  356. $qualifiedforclean=0;
  357. }
  358. if (preg_match('/^TypeContact_contrat_/', $value)) {
  359. $qualifiedforclean=0;
  360. }
  361. if (preg_match('/^ErrorPriceExpression/', $value)) {
  362. $qualifiedforclean=0;
  363. }
  364. if (preg_match('/^Language_/', $value)) {
  365. $qualifiedforclean=0;
  366. }
  367. if (preg_match('/^DescADHERENT_/', $value)) {
  368. $qualifiedforclean=0;
  369. }
  370. if (preg_match('/^SubmitTranslation/', $value)) {
  371. $qualifiedforclean=0;
  372. }
  373. if (preg_match('/^ModuleCompanyCode/', $value)) {
  374. $qualifiedforclean=0;
  375. }
  376. if (preg_match('/InDolibarr$/', $value)) {
  377. $qualifiedforclean=0;
  378. }
  379. // admin.lang
  380. if (preg_match('/^DAV_ALLOW_PUBLIC_DIR/i', $value)) {
  381. $qualifiedforclean=0;
  382. }
  383. if (preg_match('/^DAV_ALLOW_ECM_DIR/i', $value)) {
  384. $qualifiedforclean=0;
  385. }
  386. // boxes.lang
  387. if (preg_match('/^BoxTitleLast/', $value)) {
  388. $qualifiedforclean=0;
  389. }
  390. if (preg_match('/^BoxTitleLatest/', $value)) {
  391. $qualifiedforclean=0;
  392. }
  393. // install.lang
  394. if (preg_match('/^KeepDefaultValues/', $value)) {
  395. $qualifiedforclean=0;
  396. }
  397. // mail.lang
  398. if (preg_match('/MailingModuleDesc/i', $value)) {
  399. $qualifiedforclean=0;
  400. }
  401. // main.lang
  402. if (preg_match('/^Duration/', $value)) {
  403. $qualifiedforclean=0;
  404. }
  405. if (preg_match('/^FormatDate/', $value)) {
  406. $qualifiedforclean=0;
  407. }
  408. if (preg_match('/^DateFormat/', $value)) {
  409. $qualifiedforclean=0;
  410. }
  411. if (preg_match('/^.b$/', $value)) {
  412. $qualifiedforclean=0;
  413. }
  414. if (preg_match('/^.*Bytes$/', $value)) {
  415. $qualifiedforclean=0;
  416. }
  417. if (preg_match('/^NoteSomeFeaturesAreDisabled/', $value)) {
  418. $qualifiedforclean=0;
  419. }
  420. if (preg_match('/^(DoTest|Under|Limits|Cards|CurrentValue|DateLimit|DateAndHour|NbOfLines|NbOfObjects|NbOfReferes|TotalTTCShort|VATs)/', $value)) {
  421. $qualifiedforclean=0;
  422. }
  423. // modulebuilder
  424. if (preg_match('/^ModuleBuilderDesc/', $value)) {
  425. $qualifiedforclean=0;
  426. }
  427. // orders
  428. if (preg_match('/^OrderSource/', $value)) {
  429. $qualifiedforclean=0;
  430. }
  431. if (preg_match('/^TypeContact_/', $value)) {
  432. $qualifiedforclean=0;
  433. }
  434. // other.lang
  435. if (preg_match('/^Notify_/', $value)) {
  436. $qualifiedforclean=0;
  437. }
  438. if (preg_match('/^PredefinedMail/', $value)) {
  439. $qualifiedforclean=0;
  440. }
  441. if (preg_match('/^DemoCompany/', $value)) {
  442. $qualifiedforclean=0;
  443. }
  444. if (preg_match('/^WeightUnit/', $value)) {
  445. $qualifiedforclean=0;
  446. }
  447. if (preg_match('/^LengthUnit/', $value)) {
  448. $qualifiedforclean=0;
  449. }
  450. if (preg_match('/^SurfaceUnit/', $value)) {
  451. $qualifiedforclean=0;
  452. }
  453. if (preg_match('/^VolumeUnit/', $value)) {
  454. $qualifiedforclean=0;
  455. }
  456. if (preg_match('/^SizeUnit/', $value)) {
  457. $qualifiedforclean=0;
  458. }
  459. if (preg_match('/^EMailText/', $value)) {
  460. $qualifiedforclean=0;
  461. }
  462. if (preg_match('/ById$/', $value)) {
  463. $qualifiedforclean=0;
  464. }
  465. if (preg_match('/ByLogin$/', $value)) {
  466. $qualifiedforclean=0;
  467. }
  468. // printing
  469. if (preg_match('/PrintingDriverDesc$/', $value)) {
  470. $qualifiedforclean=0;
  471. }
  472. if (preg_match('/PrintTestDesc$/', $value)) {
  473. $qualifiedforclean=0;
  474. }
  475. // products
  476. if (preg_match('/GlobalVariableUpdaterType$/', $value)) {
  477. $qualifiedforclean=0;
  478. }
  479. if (preg_match('/GlobalVariableUpdaterHelp$/', $value)) {
  480. $qualifiedforclean=0;
  481. }
  482. if (preg_match('/OppStatus/', $value)) {
  483. $qualifiedforclean=0;
  484. }
  485. if (preg_match('/AvailabilityType/', $value)) {
  486. $qualifiedforclean=0;
  487. }
  488. if (preg_match('/CardProduct/', $value)) {
  489. $qualifiedforclean=0;
  490. }
  491. if (preg_match('/sms/i', $value)) {
  492. $qualifiedforclean=0;
  493. }
  494. if (preg_match('/TF_/i', $value)) {
  495. $qualifiedforclean=0;
  496. }
  497. if (preg_match('/WithBankUsing/i', $value)) {
  498. $qualifiedforclean=0;
  499. }
  500. if (preg_match('/descWORKFLOW_/i', $value)) {
  501. $qualifiedforclean=0;
  502. }
  503. if (! $qualifiedforclean) {
  504. continue;
  505. }
  506. //$search = '\'trans("'.$value.'")\'';
  507. $search = '-e "\''.$value.'\'" -e \'"'.$value.'"\' -e "('.$value.')" -e "('.$value.',"';
  508. $string = 'grep -R -m 1 -F --exclude=includes/* --include=*.php '.$search.' '.$htdocs.'* '.$scripts.'*';
  509. //print $string."<br>\n";
  510. exec($string, $output);
  511. if (empty($output)) {
  512. $unused[$value] = $line;
  513. echo $line; // $trad contains the \n
  514. } else {
  515. unset($output);
  516. //print 'X'.$output.'Y';
  517. }
  518. }
  519. if (empty($unused)) {
  520. print "No string not used found.\n";
  521. } else {
  522. $filetosave='/tmp/'.($argv[2]?$argv[2]:"").'notused.lang';
  523. print "Strings in en_US that are never used are saved into file ".$filetosave.":\n";
  524. file_put_contents($filetosave, implode("", $unused));
  525. print "To remove from original file, run command :\n";
  526. if (($argv[2]?$argv[2]:"")) {
  527. print 'cd htdocs/langs/en_US; mv '.($argv[2]?$argv[2]:"")." ".($argv[2]?$argv[2]:"").".tmp; ";
  528. }
  529. print "diff ".($argv[2]?$argv[2]:"").".tmp ".$filetosave." | grep \< | cut -b 3- > ".($argv[2]?$argv[2]:"");
  530. if (($argv[2]?$argv[2]:"")) {
  531. print "; rm ".($argv[2]?$argv[2]:"").".tmp;\n";
  532. }
  533. }
  534. }
  535. echo "\n";
  536. if ($web) {
  537. echo "</body>\n";
  538. echo "</html>\n";
  539. }
  540. exit;