PageRenderTime 43ms CodeModel.GetById 19ms RepoModel.GetById 1ms app.codeStats 0ms

/qa-include/qa-check-lang.php

http://github.com/q2a/question2answer
PHP | 283 lines | 217 code | 47 blank | 19 comment | 28 complexity | 9a5a18509b4af2e028e17dc5c5cc6259 MD5 | raw file
Possible License(s): LGPL-2.1
  1. <?php
  2. /*
  3. Question2Answer by Gideon Greenspan and contributors
  4. http://www.question2answer.org/
  5. Description: Development tool to see which language phrases are missing or unused
  6. This program is free software; you can redistribute it and/or
  7. modify it under the terms of the GNU General Public License
  8. as published by the Free Software Foundation; either version 2
  9. of the License, or (at your option) any later version.
  10. This program is distributed in the hope that it will be useful,
  11. but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. GNU General Public License for more details.
  14. More about this license: http://www.question2answer.org/license.php
  15. */
  16. define('QA_BASE_DIR', dirname(dirname(empty($_SERVER['SCRIPT_FILENAME']) ? __FILE__ : $_SERVER['SCRIPT_FILENAME'])) . '/');
  17. require 'qa-base.php';
  18. require_once QA_INCLUDE_DIR . 'app/users.php';
  19. if (qa_get_logged_in_level() < QA_USER_LEVEL_ADMIN)
  20. qa_redirect('admin/general', null, qa_opt('site_url'));
  21. header('Content-type: text/html; charset=utf-8');
  22. ?><!DOCTYPE html>
  23. <html>
  24. <head>
  25. <meta http-equiv="content-type" content="text/html; charset=utf-8"/>
  26. <title>Question2Answer Language Check</title>
  27. <style>
  28. body {
  29. font-family: arial;
  30. font-size: 12px;
  31. }
  32. code {font-size: 125%;}
  33. .severe {color: #b83939;}
  34. .warning {color: #d2930d;}
  35. .includes {color: #999;}
  36. </style>
  37. </head>
  38. <body>
  39. <?php
  40. function get_phrase_substitutions($phrase)
  41. {
  42. $substitutions = array();
  43. if (preg_match_all('/\^(([0-9]+)|([a-z_]+)|)/', $phrase, $matches)) {
  44. foreach ($matches[0] as $match) {
  45. @$substitutions[$match]++;
  46. }
  47. }
  48. return $substitutions;
  49. }
  50. echo '<code class="severe">Red = important to review.</code><br>';
  51. echo '<code class="warning">Orange = probably safe to ignore.</code>';
  52. echo '<h1>Checking US English files in <code>qa-include</code>...</h1>';
  53. $includefiles = array_merge(glob(QA_INCLUDE_DIR . 'qa-*.php'), glob(QA_INCLUDE_DIR . '*/qa-*.php'), glob(QA_PLUGIN_DIR . '*/qa-*.php'));
  54. $definite = array();
  55. $probable = array();
  56. $possible = array();
  57. $defined = array();
  58. $english = array();
  59. $backmap = array();
  60. $substitutions = array();
  61. output_start_includes();
  62. foreach ($includefiles as $includefile) {
  63. $contents = file_get_contents($includefile);
  64. preg_match_all('/qa_lang[a-z_]*\s*\(\s*[\'\"]([a-z]+)\/([0-9a-z_]+)[\'\"]/', $contents, $matches, PREG_SET_ORDER);
  65. foreach ($matches as $matchparts) {
  66. if ($matchparts[2] == 'date_month_') { // special case for month names
  67. for ($month = 1; $month <= 12; $month++) {
  68. @$definite[$matchparts[1]][$matchparts[2] . $month]++;
  69. }
  70. } else
  71. @$definite[$matchparts[1]][$matchparts[2]]++;
  72. }
  73. preg_match_all('/[\'\"]([a-z]+)\/([0-9a-z_]+)[\'\"]/', $contents, $matches, PREG_SET_ORDER);
  74. foreach ($matches as $matchparts) {
  75. @$probable[$matchparts[1]][$matchparts[2]]++;
  76. }
  77. if (preg_match('|/qa-include/qa-lang-([a-z]+)\.php$|', $includefile, $matches)) { // it's a lang file
  78. $prefix = $matches[1];
  79. output_reading_include($includefile);
  80. $phrases = @include $includefile;
  81. foreach ($phrases as $key => $value) {
  82. @$defined[$prefix][$key]++;
  83. $english[$prefix][$key] = $value;
  84. $backmap[$value][] = array('prefix' => $prefix, 'key' => $key);
  85. $substitutions[$prefix][$key] = get_phrase_substitutions($value);
  86. }
  87. } else { // it's a different file
  88. preg_match_all('/[\'\"\/]([0-9a-z_]+)[\'\"]/', $contents, $matches, PREG_SET_ORDER);
  89. foreach ($matches as $matchparts) {
  90. @$possible[$matchparts[1]]++;
  91. }
  92. }
  93. }
  94. output_finish_includes();
  95. foreach ($definite as $key => $valuecount) {
  96. foreach ($valuecount as $value => $count) {
  97. if (!@$defined[$key][$value]) {
  98. output_lang_issue($key, $value, 'used by ' . $count . ' file/s but not defined');
  99. }
  100. }
  101. }
  102. foreach ($defined as $key => $valuecount) {
  103. foreach ($valuecount as $value => $count) {
  104. if (!@$definite[$key][$value] && !@$probable[$key][$value] && !@$possible[$value]) {
  105. output_lang_issue($key, $value, 'defined but apparently not used');
  106. }
  107. }
  108. }
  109. foreach ($backmap as $phrase => $where) {
  110. if (count($where) > 1) {
  111. foreach ($where as $onewhere) {
  112. output_lang_issue($onewhere['prefix'], $onewhere['key'], 'contains the shared phrase "' . $phrase . '"', false);
  113. }
  114. }
  115. }
  116. require_once QA_INCLUDE_DIR . 'app/admin.php';
  117. $languages = qa_admin_language_options();
  118. unset($languages['']);
  119. foreach ($languages as $code => $language) {
  120. echo '<h1>Checking ' . $language . ' files in <code>qa-lang/' . $code . '</code>...</h1>';
  121. $langdefined = array();
  122. $langdifferent = array();
  123. $langsubstitutions = array();
  124. $langincludefiles = glob(QA_LANG_DIR . $code . '/qa-*.php');
  125. $langnewphrases = array();
  126. output_start_includes();
  127. foreach ($langincludefiles as $langincludefile) {
  128. if (preg_match('/qa-lang-([a-z]+)\.php$/', $langincludefile, $matches)) { // it's a lang file
  129. $prefix = $matches[1];
  130. output_reading_include($langincludefile);
  131. $phrases = @include $langincludefile;
  132. foreach ($phrases as $key => $value) {
  133. @$langdefined[$prefix][$key]++;
  134. $langdifferent[$prefix][$key] = ($value != @$english[$prefix][$key]);
  135. $langsubstitutions[$prefix][$key] = get_phrase_substitutions($value);
  136. }
  137. }
  138. }
  139. output_finish_includes();
  140. foreach ($langdefined as $key => $valuecount) {
  141. foreach ($valuecount as $value => $count) {
  142. if (!@$defined[$key][$value]) {
  143. output_lang_issue($key, $value, 'defined but not in US English files');
  144. } elseif (!$langdifferent[$key][$value]) {
  145. output_lang_issue($key, $value, 'identical to US English files', false);
  146. } else {
  147. foreach ($substitutions[$key][$value] as $substitution => $subcount) {
  148. if (!@$langsubstitutions[$key][$value][$substitution])
  149. output_lang_issue($key, $value, 'omitted the substitution ' . $substitution);
  150. elseif ($subcount > @$langsubstitutions[$key][$value][$substitution])
  151. output_lang_issue($key, $value, 'has fewer of the substitution ' . $substitution);
  152. }
  153. }
  154. }
  155. }
  156. foreach ($defined as $key => $valuecount) {
  157. $showaserror = !(($key == 'admin') || ($key == 'options') || ($code == 'en-GB'));
  158. if (@$langdefined[$key]) {
  159. if (count($langdefined[$key]) < (count($valuecount) / 2)) { // only a few phrases defined
  160. output_lang_issue($key, null, 'few translations provided so will use US English defaults', $showaserror);
  161. } else {
  162. foreach ($valuecount as $value => $count) {
  163. if (!@$langdefined[$key][$value]) {
  164. output_lang_issue($key, $value, 'undefined so will use US English defaults', $showaserror);
  165. $langnewphrases[$key][$value] = $english[$key][$value];
  166. }
  167. }
  168. }
  169. } else {
  170. output_lang_issue($key, null, 'no translations provided so will use US English defaults', $showaserror);
  171. }
  172. }
  173. foreach ($langnewphrases as $prefix => $phrases) {
  174. echo '<h2>' . $language . ' phrases to add to <code>qa-lang/' . $code . '/qa-lang-' . $prefix . '.php</code>:</h2>';
  175. echo 'Copy and paste this into the middle of <code>qa-lang/' . $code . '/qa-lang-' . $prefix . '.php</code> then translate the right-hand side after the <code>=></code> symbol.';
  176. echo '<pre>';
  177. foreach ($phrases as $key => $value) {
  178. echo '<span style="font-size:25%;">' . "\t\t</span>'" . $key . "' => \"" . strtr($value, array('\\' => '\\\\', '"' => '\"', '$' => '\$', "\n" => '\n', "\t" => '\t')) . "\",\n";
  179. }
  180. echo '</pre>';
  181. }
  182. }
  183. function output_lang_issue($prefix, $key, $issue, $error = true)
  184. {
  185. $colorClass = $error ? 'severe' : 'warning';
  186. $htmlKey = strlen($key) > 0 ? "'<strong>" . qa_html($key) . "</strong>'" : '';
  187. echo sprintf(
  188. '<code class="%s">qa-lang-<strong>%s</strong>.php:%s</code> &nbsp; %s<br>',
  189. $colorClass, qa_html($prefix), $htmlKey, qa_html($issue)
  190. );
  191. }
  192. function output_start_includes()
  193. {
  194. global $oneread;
  195. $oneread = false;
  196. echo '<p class="includes">Reading: ';
  197. }
  198. function output_reading_include($file)
  199. {
  200. global $oneread;
  201. echo ($oneread ? ', ' : '') . htmlspecialchars(basename($file));
  202. flush();
  203. $oneread = true;
  204. }
  205. function output_finish_includes()
  206. {
  207. echo '</p>';
  208. }
  209. echo '<h1>Finished scanning for problems!</h1>';
  210. ?>
  211. </body>
  212. </html>