PageRenderTime 48ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/gallery2/lib/tools/po/extract.php

#
PHP | 288 lines | 208 code | 16 blank | 64 comment | 93 complexity | 8bdcecf10408905cf35c1d59d45a9030 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.1, MIT, LGPL-3.0
  1. #!/usr/bin/php -f
  2. <?php
  3. /*
  4. * PHP script to extract strings from all the files and print
  5. * to stdout for use with xgettext.
  6. *
  7. * This script is based on the perl script provided with the Horde project
  8. * http://www.horde.org/. As such, it inherits the license from the
  9. * original version. You can find that license here:
  10. *
  11. * http://cvs.horde.org/co.php/horde/COPYING?r=2.1
  12. *
  13. * I'm not exactly sure what the license restrictions are in this case,
  14. * but I want to give full credit to the original authors:
  15. *
  16. * Copyright 2000-2002 Joris Braakman <jbraakman@yahoo.com>
  17. * Copyright 2001-2002 Chuck Hagenbuch <chuck@horde.org>
  18. * Copyright 2001-2002 Jan Schneider <jan@horde.org>
  19. *
  20. * We've modified the script somewhat to make it work cleanly with the
  21. * way that Gallery embeds internationalized text, so let's tack on our
  22. * own copyrights.
  23. *
  24. * Copyright (C) 2002-2008 Bharat Mediratta <bharat@menalto.com>
  25. *
  26. * $Id: extract.php 17580 2008-04-13 00:38:13Z tnalmdal $
  27. */
  28. if (!empty($_SERVER['SERVER_NAME'])) {
  29. errorExit("You must run this from the command line\n");
  30. }
  31. if (!function_exists('token_get_all')) {
  32. errorExit("PHP tokenizer required.\n"
  33. . "Must use a PHP binary that is NOT built with --disable-tokenizer\n");
  34. }
  35. $exts = '(class|php|inc|css|html|tpl)';
  36. $idEmitted = false;
  37. $strings = array();
  38. array_shift($_SERVER['argv']);
  39. foreach ($_SERVER['argv'] as $moduleDir) {
  40. if (preg_match('#^/cygdrive/(\w+)/(.*)$#', trim($moduleDir), $matches)) {
  41. /* Cygwin and Window PHP filesystem function don't play nice together. */
  42. $moduleDir = $matches[1] . ':\\' . str_replace('/', '\\', $matches[2]);
  43. }
  44. if (!is_dir($moduleDir)) {
  45. continue;
  46. }
  47. chdir($moduleDir);
  48. find('.');
  49. $oldStringsRaw = "$moduleDir/po/strings.raw";
  50. if (file_exists($oldStringsRaw)) {
  51. $lines = file($oldStringsRaw);
  52. if (preg_match('/^#.*Id/', $lines[0])) {
  53. print $lines[0];
  54. $idEmitted = true;
  55. }
  56. }
  57. }
  58. if (!$idEmitted) {
  59. print '# $' . 'Id$' . "\n";
  60. }
  61. foreach ($strings as $string => $otherFiles) {
  62. print $string;
  63. if (!empty($otherFiles)) {
  64. print ' /* also in: ' . implode(' ', $otherFiles) . ' */';
  65. }
  66. print "\n";
  67. }
  68. /**
  69. * Recursive go through subdirectories
  70. */
  71. function find($dir) {
  72. if ($dh = opendir($dir)) {
  73. $listing = $subdirs = array();
  74. while (($file = readdir($dh)) !== false) {
  75. if ($file == '.' || $file == '..') {
  76. continue;
  77. }
  78. $listing[] = $file;
  79. }
  80. closedir($dh);
  81. sort($listing);
  82. global $exts;
  83. $dir = ($dir == '.') ? '' : ($dir . '/');
  84. foreach ($listing as $file) {
  85. $filename = $dir . $file;
  86. if (is_dir($filename)) {
  87. /* Don't parse unit tests */
  88. if ($file != 'test') {
  89. $subdirs[] = $filename;
  90. }
  91. } else if (preg_match('/\.' . $exts . '$/', $file)) {
  92. extractStrings($filename);
  93. }
  94. }
  95. foreach ($subdirs as $dir) {
  96. find($dir);
  97. }
  98. }
  99. }
  100. /**
  101. * Grab all translatable strings in a file into $strings array
  102. */
  103. function extractStrings($filename) {
  104. global $strings;
  105. $strings["\n/* $filename */"] = array();
  106. $startSize = count($strings);
  107. $localStrings = array();
  108. $data = file_get_contents($filename);
  109. /*
  110. * class|inc|php are module and core PHP files.
  111. * Parse .html as PHP for installer/upgrader templates/*.html files.
  112. * Parse .css as PHP for modules/colorpack/packs/{name}/color.css files.
  113. */
  114. if (preg_match('/\.(class|inc|php|css|html)$/', $filename)) {
  115. /* Tokenize PHP code and process to find translate( or i18n( or _( calls */
  116. $tokens = token_get_all($data);
  117. for ($i = 0; $i < count($tokens); $i++) {
  118. if (is_array($tokens[$i]) && $tokens[$i][0] == T_STRING
  119. && in_array($tokens[$i][1], array('translate', '_translate', 'i18n', '_'))
  120. && $tokens[$i + 1] === '(') {
  121. /* Found a function call for translation, process the contents */
  122. for ($i += 2; is_array($tokens[$i]) && $tokens[$i][0] == T_WHITESPACE; $i++) { }
  123. if (is_array($tokens[$i]) && $tokens[$i][0] == T_VARIABLE) {
  124. /* Skip translate($variable) */
  125. continue;
  126. }
  127. for ($buf = '', $parenCount = $ignore = 0; $i < count($tokens); $i++) {
  128. /* Fill $buf with translation params; so end at , or ) not in a nested () */
  129. if (!$parenCount && ($tokens[$i] === ')' || $tokens[$i] === ',')) {
  130. break;
  131. }
  132. if ($ignore && $parenCount == $ignore
  133. && ($tokens[$i] === ',' || $tokens[$i] === ')')) {
  134. $ignore = false;
  135. }
  136. if ($tokens[$i] === '(') {
  137. $parenCount++;
  138. } else if ($tokens[$i] === ')') {
  139. $parenCount--;
  140. }
  141. if (is_array($tokens[$i]) && $tokens[$i][0] == T_CONSTANT_ENCAPSED_STRING) {
  142. $lastString = $tokens[$i][1];
  143. }
  144. if (!$ignore) {
  145. $buf .= is_array($tokens[$i]) ? $tokens[$i][1] : $tokens[$i];
  146. }
  147. if (is_array($tokens[$i]) && $tokens[$i][0] == T_DOUBLE_ARROW
  148. && (substr($lastString, 1, 3) === 'arg'
  149. || substr($lastString, 1, 5) === 'count')) {
  150. /*
  151. * Convert 'argN' => code to 'argN' => null so we don't eval that code.
  152. * Add 'null' to $buf now, then ignore content until next , or ) not in
  153. * a deeper nested ().
  154. */
  155. $buf .= 'null';
  156. $ignore = $parenCount;
  157. }
  158. }
  159. $param = eval('return ' . $buf . ';');
  160. if (is_string($param)) {
  161. /* Escape double quotes and newlines */
  162. $text = strtr($param, array('"' => '\"', "\r\n" => '\n', "\n" => '\n'));
  163. $string = 'gettext("' . $text . '")';
  164. if (!isset($strings[$string])) {
  165. $strings[$string] = array();
  166. } else if (!isset($localStrings[$string])) {
  167. $strings[$string][] = $filename;
  168. }
  169. $localStrings[$string] = true;
  170. } else if (is_array($param)) {
  171. foreach (array('text', 'one', 'many') as $key) {
  172. if (isset($param[$key])) {
  173. /* Escape double quotes and newlines */
  174. $param[$key] = strtr($param[$key],
  175. array('"' => '\\"', "\r\n" => '\n', "\n" => '\n'));
  176. }
  177. }
  178. if (isset($param['one'])) {
  179. $string = 'ngettext("' . $param['one'] . '", "' . $param['many'] . '")';
  180. } else {
  181. $string = 'gettext("' . $param['text'] . '")';
  182. }
  183. if (isset($param['cFormat'])) {
  184. $string = '/* xgettext:'
  185. . ($param['cFormat'] ? '' : 'no-') . "c-format */\n$string";
  186. }
  187. if (!empty($param['hint'])) {
  188. $string = '// HINT: ' . str_replace("\n", "\n// ", $param['hint'])
  189. . "\n$string";
  190. }
  191. if (!isset($strings[$string])) {
  192. $strings[$string] = array();
  193. } else if (!isset($localStrings[$string])) {
  194. $strings[$string][] = $filename;
  195. }
  196. $localStrings[$string] = true;
  197. }
  198. }
  199. }
  200. } else if (preg_match_all('/{\s*g->(?:text|changeInDescendents)\s+.*?[^\\\\]}/s',
  201. $data, $matches)) {
  202. /* Use regexp to process tpl files for {g->text ..} and {g->changeInDescendents ..} */
  203. foreach ($matches[0] as $string) {
  204. $text = $one = $many = null;
  205. /*
  206. * Ignore translations of the form:
  207. * text=$foo
  208. * as we expect those to be variables containing values that
  209. * have been marked elsewhere with the i18n() function.
  210. */
  211. if (preg_match('/\stext=\$/', $string)) {
  212. continue;
  213. }
  214. /* text=..... */
  215. if (preg_match('/\stext="(.*?[^\\\\])"/s', $string, $matches)) {
  216. $text = $matches[1];
  217. } else if (preg_match("/text='(.*?)'/s", $string, $matches)) {
  218. $text = str_replace('"', '\"', $matches[1]); /* Escape double quotes */
  219. }
  220. /* one=..... */
  221. if (preg_match('/\sone="(.*?[^\\\\])"/s', $string, $matches)) {
  222. $one = $matches[1];
  223. } else if (preg_match("/\sone='(.*?)'/s", $string, $matches)) {
  224. $one = str_replace('"', '\"', $matches[1]); /* Escape double quotes */
  225. }
  226. /* many=..... */
  227. if (preg_match('/\smany="(.*?[^\\\\])"/s', $string, $matches)) {
  228. $many = $matches[1];
  229. } else if (preg_match("/\smany='(.*?)'/s", $string, $matches)) {
  230. $many = str_replace('"', '\"', $matches[1]); /* Escape double quotes */
  231. }
  232. /* Hint for translators */
  233. $translatorHint = preg_match('/\shint=((["\']).*?[^\\\\]\2)/s', $string, $matches)
  234. ? eval('return ' . $matches[1] . ';') : '';
  235. /* c-format hint for xgettext */
  236. $cFormatHint = preg_match('/\sc[Ff]ormat=(true|false)/s', $string, $matches)
  237. ? '/* xgettext:' . ($matches[1] == 'false' ? 'no-' : '') . "c-format */\n" : '';
  238. /* Pick gettext() or ngettext() and escape newlines */
  239. if (isset($text)) {
  240. $string = 'gettext("' . strtr($text, array("\r\n" => '\n', "\n" => '\n')) . '")';
  241. } else if (isset($one) && isset($many)) {
  242. $string = 'ngettext("' . strtr($one, array("\r\n" => '\n', "\n" => '\n')) . '", "'
  243. . strtr($many, array("\r\n" => '\n', "\n" => '\n')) . '")';
  244. } else {
  245. /* Parse error */
  246. $string = str_replace("\n", '\n> ', $string);
  247. errorExit("extract.php parse error: $filename:\n> $string\n");
  248. }
  249. if ($cFormatHint) {
  250. $string = $cFormatHint . $string;
  251. }
  252. if ($translatorHint) {
  253. $string = "// HINT: $translatorHint\n$string";
  254. }
  255. if (!isset($strings[$string])) {
  256. $strings[$string] = array();
  257. } else if (!isset($localStrings[$string])) {
  258. $strings[$string][] = $filename;
  259. }
  260. $localStrings[$string] = true;
  261. }
  262. }
  263. if (count($strings) == $startSize) {
  264. unset($strings["\n/* $filename */"]);
  265. }
  266. }
  267. function errorExit($message) {
  268. $stderr = fopen('php://stderr', 'w');
  269. fwrite($stderr, $message);
  270. exit(1);
  271. }
  272. ?>