sanity_check_en_langfiles.php 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592
  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;
  182. $inadmin=0;
  183. }
  184. if ($file == 'admin.lang' && ! $inmain) {
  185. $inadmin=1;
  186. }
  187. $s.=$file." ";
  188. // Loop on each line keword was found into file.
  189. $listoffilesforthisentry=array();
  190. foreach ($lines as $line => $translatedvalue) {
  191. if (!empty($listoffilesforthisentry[$file])) {
  192. $duplicateinsamefile=1;
  193. }
  194. $listoffilesforthisentry[$file]=1;
  195. $s.= "(".$line." - ".htmlentities($translatedvalue).") ";
  196. }
  197. if ($web) {
  198. $s.="<br>";
  199. }
  200. }
  201. if ($web) {
  202. $s.="</td></tr>";
  203. }
  204. $s.="\n";
  205. if ($duplicateinsamefile) {
  206. $sduplicateinsamefile .= $s;
  207. } elseif ($inmain) {
  208. $sinmainandother .= $s;
  209. } elseif ($inadmin) {
  210. $sininstallandadmin .= $s;
  211. } else {
  212. $sother .= $s;
  213. }
  214. }
  215. if (! $web) {
  216. print "\n***** Entries duplicated in same file\n";
  217. }
  218. print $sduplicateinsamefile;
  219. if (! $web && empty($sduplicateinsamefile)) {
  220. print "None\n";
  221. }
  222. if (! $web) {
  223. print "\n";
  224. }
  225. if (! $web) {
  226. print "***** Entries in main and another (keep only entry in main)\n";
  227. }
  228. print $sinmainandother;
  229. if (! $web && empty($sinmainandother)) {
  230. print "None\n";
  231. }
  232. if (! $web) {
  233. print "\n";
  234. }
  235. if (! $web) {
  236. print "***** Entries in admin and another\n";
  237. }
  238. print $sininstallandadmin;
  239. if (! $web && empty($sininstallandadmin)) {
  240. print "None\n";
  241. }
  242. if (! $web) {
  243. print "\n";
  244. }
  245. if (! $web) {
  246. print "***** Other\n";
  247. }
  248. print $sother;
  249. if (! $web && empty($sother)) {
  250. print "None\n";
  251. }
  252. if (! $web) {
  253. print "\n";
  254. }
  255. if ($web) {
  256. echo "</tbody>\n";
  257. echo "</table>\n";
  258. }
  259. // STEP 2 - Search key not used
  260. if ((!empty($_REQUEST['unused']) && $_REQUEST['unused'] == 'true') || (isset($argv[1]) && $argv[1]=='unused=true')) {
  261. print "***** Strings in en_US that are never used:\n";
  262. $unused=array();
  263. foreach ($langstrings_dist as $value => $line) {
  264. $qualifiedforclean=1;
  265. // Check if we must keep this key to be into file for removal
  266. if (preg_match('/^Module\d+/', $value)) {
  267. $qualifiedforclean=0;
  268. }
  269. if (preg_match('/^Permission\d+/', $value)) {
  270. $qualifiedforclean=0;
  271. }
  272. if (preg_match('/^PermissionAdvanced\d+/', $value)) {
  273. $qualifiedforclean=0;
  274. }
  275. if (preg_match('/^ProfId\d+/', $value)) {
  276. $qualifiedforclean=0;
  277. }
  278. if (preg_match('/^Delays_/', $value)) {
  279. $qualifiedforclean=0;
  280. }
  281. if (preg_match('/^BarcodeDesc/', $value)) {
  282. $qualifiedforclean=0;
  283. }
  284. if (preg_match('/^Extrafield/', $value)) {
  285. $qualifiedforclean=0;
  286. }
  287. if (preg_match('/^LocalTax/', $value)) {
  288. $qualifiedforclean=0;
  289. }
  290. if (preg_match('/^Country/', $value)) {
  291. $qualifiedforclean=0;
  292. }
  293. if (preg_match('/^Civility/', $value)) {
  294. $qualifiedforclean=0;
  295. }
  296. if (preg_match('/^Currency/', $value)) {
  297. $qualifiedforclean=0;
  298. }
  299. if (preg_match('/^DemandReasonTypeSRC/', $value)) {
  300. $qualifiedforclean=0;
  301. }
  302. if (preg_match('/^PaperFormat/', $value)) {
  303. $qualifiedforclean=0;
  304. }
  305. if (preg_match('/^Duration/', $value)) {
  306. $qualifiedforclean=0;
  307. }
  308. if (preg_match('/^AmountLT/', $value)) {
  309. $qualifiedforclean=0;
  310. }
  311. if (preg_match('/^TotalLT/', $value)) {
  312. $qualifiedforclean=0;
  313. }
  314. if (preg_match('/^Month/', $value)) {
  315. $qualifiedforclean=0;
  316. }
  317. if (preg_match('/^MonthShort/', $value)) {
  318. $qualifiedforclean=0;
  319. }
  320. if (preg_match('/^Day\d/', $value)) {
  321. $qualifiedforclean=0;
  322. }
  323. if (preg_match('/^Short/', $value)) {
  324. $qualifiedforclean=0;
  325. }
  326. if (preg_match('/^ExportDataset_/', $value)) {
  327. $qualifiedforclean=0;
  328. }
  329. if (preg_match('/^ImportDataset_/', $value)) {
  330. $qualifiedforclean=0;
  331. }
  332. if (preg_match('/^ActionAC_/', $value)) {
  333. $qualifiedforclean=0;
  334. }
  335. if (preg_match('/^TypeLocaltax/', $value)) {
  336. $qualifiedforclean=0;
  337. }
  338. if (preg_match('/^StatusProspect/', $value)) {
  339. $qualifiedforclean=0;
  340. }
  341. if (preg_match('/^PL_/', $value)) {
  342. $qualifiedforclean=0;
  343. }
  344. if (preg_match('/^TE_/', $value)) {
  345. $qualifiedforclean=0;
  346. }
  347. if (preg_match('/^JuridicalStatus/', $value)) {
  348. $qualifiedforclean=0;
  349. }
  350. if (preg_match('/^CalcMode/', $value)) {
  351. $qualifiedforclean=0;
  352. }
  353. if (preg_match('/^newLT/', $value)) {
  354. $qualifiedforclean=0;
  355. }
  356. if (preg_match('/^LT[0-9]/', $value)) {
  357. $qualifiedforclean=0;
  358. }
  359. if (preg_match('/^TypeContact_contrat_/', $value)) {
  360. $qualifiedforclean=0;
  361. }
  362. if (preg_match('/^ErrorPriceExpression/', $value)) {
  363. $qualifiedforclean=0;
  364. }
  365. if (preg_match('/^Language_/', $value)) {
  366. $qualifiedforclean=0;
  367. }
  368. if (preg_match('/^DescADHERENT_/', $value)) {
  369. $qualifiedforclean=0;
  370. }
  371. if (preg_match('/^SubmitTranslation/', $value)) {
  372. $qualifiedforclean=0;
  373. }
  374. if (preg_match('/^ModuleCompanyCode/', $value)) {
  375. $qualifiedforclean=0;
  376. }
  377. if (preg_match('/InDolibarr$/', $value)) {
  378. $qualifiedforclean=0;
  379. }
  380. // admin.lang
  381. if (preg_match('/^DAV_ALLOW_PUBLIC_DIR/i', $value)) {
  382. $qualifiedforclean=0;
  383. }
  384. if (preg_match('/^DAV_ALLOW_ECM_DIR/i', $value)) {
  385. $qualifiedforclean=0;
  386. }
  387. // boxes.lang
  388. if (preg_match('/^BoxTitleLast/', $value)) {
  389. $qualifiedforclean=0;
  390. }
  391. if (preg_match('/^BoxTitleLatest/', $value)) {
  392. $qualifiedforclean=0;
  393. }
  394. // install.lang
  395. if (preg_match('/^KeepDefaultValues/', $value)) {
  396. $qualifiedforclean=0;
  397. }
  398. // mail.lang
  399. if (preg_match('/MailingModuleDesc/i', $value)) {
  400. $qualifiedforclean=0;
  401. }
  402. // main.lang
  403. if (preg_match('/^Duration/', $value)) {
  404. $qualifiedforclean=0;
  405. }
  406. if (preg_match('/^FormatDate/', $value)) {
  407. $qualifiedforclean=0;
  408. }
  409. if (preg_match('/^DateFormat/', $value)) {
  410. $qualifiedforclean=0;
  411. }
  412. if (preg_match('/^.b$/', $value)) {
  413. $qualifiedforclean=0;
  414. }
  415. if (preg_match('/^.*Bytes$/', $value)) {
  416. $qualifiedforclean=0;
  417. }
  418. if (preg_match('/^NoteSomeFeaturesAreDisabled/', $value)) {
  419. $qualifiedforclean=0;
  420. }
  421. if (preg_match('/^(DoTest|Under|Limits|Cards|CurrentValue|DateLimit|DateAndHour|NbOfLines|NbOfObjects|NbOfReferes|TotalTTCShort|VATs)/', $value)) {
  422. $qualifiedforclean=0;
  423. }
  424. // modulebuilder
  425. if (preg_match('/^ModuleBuilderDesc/', $value)) {
  426. $qualifiedforclean=0;
  427. }
  428. // orders
  429. if (preg_match('/^OrderSource/', $value)) {
  430. $qualifiedforclean=0;
  431. }
  432. if (preg_match('/^TypeContact_/', $value)) {
  433. $qualifiedforclean=0;
  434. }
  435. // other.lang
  436. if (preg_match('/^Notify_/', $value)) {
  437. $qualifiedforclean=0;
  438. }
  439. if (preg_match('/^PredefinedMail/', $value)) {
  440. $qualifiedforclean=0;
  441. }
  442. if (preg_match('/^DemoCompany/', $value)) {
  443. $qualifiedforclean=0;
  444. }
  445. if (preg_match('/^WeightUnit/', $value)) {
  446. $qualifiedforclean=0;
  447. }
  448. if (preg_match('/^LengthUnit/', $value)) {
  449. $qualifiedforclean=0;
  450. }
  451. if (preg_match('/^SurfaceUnit/', $value)) {
  452. $qualifiedforclean=0;
  453. }
  454. if (preg_match('/^VolumeUnit/', $value)) {
  455. $qualifiedforclean=0;
  456. }
  457. if (preg_match('/^SizeUnit/', $value)) {
  458. $qualifiedforclean=0;
  459. }
  460. if (preg_match('/^EMailText/', $value)) {
  461. $qualifiedforclean=0;
  462. }
  463. if (preg_match('/ById$/', $value)) {
  464. $qualifiedforclean=0;
  465. }
  466. if (preg_match('/ByLogin$/', $value)) {
  467. $qualifiedforclean=0;
  468. }
  469. // printing
  470. if (preg_match('/PrintingDriverDesc$/', $value)) {
  471. $qualifiedforclean=0;
  472. }
  473. if (preg_match('/PrintTestDesc$/', $value)) {
  474. $qualifiedforclean=0;
  475. }
  476. // products
  477. if (preg_match('/GlobalVariableUpdaterType$/', $value)) {
  478. $qualifiedforclean=0;
  479. }
  480. if (preg_match('/GlobalVariableUpdaterHelp$/', $value)) {
  481. $qualifiedforclean=0;
  482. }
  483. if (preg_match('/OppStatus/', $value)) {
  484. $qualifiedforclean=0;
  485. }
  486. if (preg_match('/AvailabilityType/', $value)) {
  487. $qualifiedforclean=0;
  488. }
  489. if (preg_match('/CardProduct/', $value)) {
  490. $qualifiedforclean=0;
  491. }
  492. if (preg_match('/sms/i', $value)) {
  493. $qualifiedforclean=0;
  494. }
  495. if (preg_match('/TF_/i', $value)) {
  496. $qualifiedforclean=0;
  497. }
  498. if (preg_match('/WithBankUsing/i', $value)) {
  499. $qualifiedforclean=0;
  500. }
  501. if (preg_match('/descWORKFLOW_/i', $value)) {
  502. $qualifiedforclean=0;
  503. }
  504. if (! $qualifiedforclean) {
  505. continue;
  506. }
  507. //$search = '\'trans("'.$value.'")\'';
  508. $search = '-e "\''.$value.'\'" -e \'"'.$value.'"\' -e "('.$value.')" -e "('.$value.',"';
  509. $string = 'grep -R -m 1 -F --exclude=includes/* --include=*.php '.$search.' '.$htdocs.'* '.$scripts.'*';
  510. //print $string."<br>\n";
  511. exec($string, $output);
  512. if (empty($output)) {
  513. $unused[$value] = $line;
  514. echo $line; // $trad contains the \n
  515. } else {
  516. unset($output);
  517. //print 'X'.$output.'Y';
  518. }
  519. }
  520. if (empty($unused)) {
  521. print "No string not used found.\n";
  522. } else {
  523. $filetosave='/tmp/'.($argv[2] ? $argv[2] : "").'notused.lang';
  524. print "Strings in en_US that are never used are saved into file ".$filetosave.":\n";
  525. file_put_contents($filetosave, implode("", $unused));
  526. print "To remove from original file, run command :\n";
  527. if (($argv[2] ? $argv[2] : "")) {
  528. print 'cd htdocs/langs/en_US; mv '.($argv[2] ? $argv[2] : "")." ".($argv[2] ? $argv[2] : "").".tmp; ";
  529. }
  530. print "diff ".($argv[2] ? $argv[2] : "").".tmp ".$filetosave." | grep \< | cut -b 3- > ".($argv[2] ? $argv[2] : "");
  531. if (($argv[2] ? $argv[2] : "")) {
  532. print "; rm ".($argv[2] ? $argv[2] : "").".tmp;\n";
  533. }
  534. }
  535. }
  536. echo "\n";
  537. if ($web) {
  538. echo "</body>\n";
  539. echo "</html>\n";
  540. }
  541. exit;