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

/saf/lib/I18n/Builder.php

https://github.com/wokkie/sitellite
PHP | 519 lines | 379 code | 59 blank | 81 comment | 113 complexity | 9df6cfac63695eb160b544eb5a16ea79 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.1, Apache-2.0
  1. <?php
  2. $GLOBALS['loader']->import ('saf.I18n');
  3. $GLOBALS['loader']->import ('saf.Parser.Tokenizer');
  4. $GLOBALS['loader']->import ('saf.Parser.Buffer');
  5. $GLOBALS['loader']->import ('saf.File.Directory');
  6. /**
  7. * @package I18n
  8. */
  9. class I18nBuilder {
  10. var $buffer;
  11. var $error = false;
  12. var $root; // starting point
  13. var $except; // directories or files to skip
  14. var $extensions = array (
  15. 'php' => '_php', // php script
  16. 'tpl' => '_tpl', // xt template
  17. 'spt' => '_spt', // simple template
  18. 'collection' => '_collection', // collection file
  19. 'settings' => '_settings', // form settings files
  20. 'db' => '_db', // list of database tables/fields to be translated
  21. 'config' => '_config', // config.ini.php: to get app name
  22. );
  23. function I18nBuilder ($root, $except = false) {
  24. $this->root = $root;
  25. if (is_string ($except)) {
  26. $this->except[] = $except;
  27. } elseif (is_array ($except)) {
  28. $this->except = $except;
  29. }
  30. $this->buffer = new Buffer;
  31. global $intl;
  32. $this->intl =& $intl;
  33. }
  34. function build ($path = false) {
  35. if ($path === false) {
  36. $path = $this->root;
  37. }
  38. $dir = new Dir ($path);
  39. if (! $dir->handle) {
  40. $this->error = 'Failed to read directory: ' . $path;
  41. return false;
  42. }
  43. echo '<ul>';
  44. foreach ($dir->read_all () as $file) {
  45. if (strpos ($file, '.') === 0 || in_array ($file, array ('CVS', 'PEAR', 'Ext', 'pix', 'install', 'data', 'lang', 'modes.php', 'images.php', 'access.php')) || preg_match ('/\.(jpg|gif|png|css|js)$/i', $file)) {
  46. continue;
  47. }
  48. if (in_array ($path . '/' . $file, $this->except) || in_array ($file, $this->except)) {
  49. continue;
  50. }
  51. echo '<li>' . $path . '/' . $file . '</li>';
  52. if (@is_dir ($path . '/' . $file)) {
  53. if (! $this->build ($path . '/' . $file)) {
  54. $dir->close ();
  55. return false;
  56. }
  57. } elseif (
  58. @is_file ($path . '/' . $file) &&
  59. preg_match ('/\.(' . join ('|', array_keys ($this->extensions)) . ')$/i', $file, $regs)
  60. ) {
  61. if ($file == 'settings.php' && strstr ($path, 'forms')) {
  62. $regs[1] = 'settings';
  63. } elseif ($file == 'translate.ini.php') {
  64. $regs[1] = 'db';
  65. } elseif (strstr ($path, 'conf/collections')) {
  66. $regs[1] = 'collection';
  67. } elseif ($file == 'config.ini.php') {
  68. $regs[1] = 'config';
  69. } elseif ($file == 'settings.ini.php') {
  70. $regs[1] = 'settings';
  71. }
  72. if (! $this->{$this->extensions[$regs[1]]} ($path . '/' . $file, $this->getContents ($path . '/' . $file))) {
  73. $dir->close ();
  74. return false;
  75. }
  76. } // else it's not a type of file we know how to parse
  77. }
  78. echo '</ul>';
  79. $dir->close ();
  80. return true;
  81. }
  82. function getList () {
  83. //info ($this->buffer, true);
  84. return $this->buffer->getAll ();
  85. }
  86. function getContents ($file) {
  87. if (@is_file ($file)) {
  88. return @join ('', @file ($file));
  89. }
  90. return $file;
  91. }
  92. function _config ($file, $data) {
  93. // get app name from config.ini.php
  94. if (! @file_exists ($file)) {
  95. return true;
  96. }
  97. $data = ini_parse ($file, false);
  98. ini_clear ();
  99. if (! is_array ($data)) {
  100. return true;
  101. }
  102. if (count ($data) == 0) {
  103. return true;
  104. }
  105. foreach ($data as $k => $v) {
  106. if ($k == 'app_name') {
  107. $this->buffer->set ($this->intl->serialize ($v), array (
  108. 'string' => $v,
  109. 'params' => false,
  110. 'file' => $file,
  111. 'line' => false,
  112. ));
  113. }
  114. }
  115. return true;
  116. }
  117. function _collection ($file, $data) {
  118. if (! @file_exists ($file)) {
  119. return true;
  120. }
  121. $data = ini_parse ($file);
  122. ini_clear ();
  123. if (! is_array ($data)) {
  124. return true;
  125. }
  126. if (count ($data) == 0) {
  127. return true;
  128. }
  129. foreach ($data as $section => $values) {
  130. foreach ($values as $k => $v) {
  131. switch ($k) {
  132. case 'alt':
  133. case 'display':
  134. case 'key_field_name':
  135. case 'header':
  136. case 'singular':
  137. case 'title_field_name':
  138. $this->buffer->set ($this->intl->serialize ($v), array (
  139. 'string' => $v,
  140. 'params' => false,
  141. 'file' => $file,
  142. 'line' => false,
  143. ));
  144. break;
  145. case 'values':
  146. if (substr ($v, 0, 5) == 'array') {
  147. eval ('$a = '.$v.";");
  148. if (is_array ($a)) {
  149. foreach ($a as $vv) {
  150. $this->buffer->set ($this->intl->serialize ($vv), array (
  151. 'string' => $vv,
  152. 'params' => false,
  153. 'file' => $file,
  154. 'line' => false,
  155. ));
  156. }
  157. }
  158. }
  159. break;
  160. }
  161. }
  162. }
  163. return true;
  164. }
  165. function _settings ($file, $data) {
  166. if (! @file_exists ($file)) {
  167. return true;
  168. }
  169. ini_add_filter ('ini_filter_split_comma_single', array (
  170. 'rule 0', 'rule 1', 'rule 2', 'rule 3', 'rule 4', 'rule 5', 'rule 6', 'rule 7', 'rule 8',
  171. 'button 0', 'button 1', 'button 2', 'button 3', 'button 4', 'button 5', 'button 6', 'button 7', 'button 8',
  172. ));
  173. $data = ini_parse ($file);
  174. ini_clear ();
  175. if (! is_array ($data)) {
  176. return true;
  177. }
  178. if (count ($data) == 0) {
  179. return true;
  180. }
  181. foreach ($data as $section => $values) {
  182. foreach ($values as $k => $v) {
  183. if ($section == 'Form' && ($k == 'title' || $k == 'message')) {
  184. $this->buffer->set ($this->intl->serialize ($v), array (
  185. 'string' => $v,
  186. 'params' => false,
  187. 'file' => $file,
  188. 'line' => false,
  189. ));
  190. } elseif ($k == 'alt' || $k == 'display_value' || $k == 'title' || $k == 'append' || $k == 'prepend' || $k == 'formhelp') {
  191. $this->buffer->set ($this->intl->serialize ($v), array (
  192. 'string' => $v,
  193. 'params' => false,
  194. 'file' => $file,
  195. 'line' => false,
  196. ));
  197. } elseif (strpos ($k, 'rule ') === 0 && is_array ($v)) {
  198. $this->buffer->set ($this->intl->serialize ($v[1]), array (
  199. 'string' => $v[1],
  200. 'params' => false,
  201. 'file' => $file,
  202. 'line' => false,
  203. ));
  204. } elseif (strpos ($k, 'button ') === 0) {
  205. if (is_array ($v)) {
  206. $v = $v[0];
  207. }
  208. $this->buffer->set ($this->intl->serialize ($v), array (
  209. 'string' => $v,
  210. 'params' => false,
  211. 'file' => $file,
  212. 'line' => false,
  213. ));
  214. } elseif (strpos ($v, 'eval:') === 0) {
  215. $this->_php ($file, OPEN_TAG . ' ' . substr ($v, 5) . ' ' . CLOSE_TAG);
  216. }
  217. }
  218. }
  219. return true;
  220. }
  221. function _db ($file, $data) {
  222. if (! @file_exists ($file)) {
  223. return true;
  224. }
  225. $data = parse_ini_file ($file);
  226. echo '<ul>';
  227. foreach ($data as $table => $fields) {
  228. echo '<li>table: ' . $table . '/' . $fields . '</li>';
  229. $fields = preg_split ('/, ?/', $fields);
  230. if (! is_array ($fields)) {
  231. $fields = array ($fields);
  232. }
  233. foreach ($fields as $field) {
  234. $res = db_shift_array (
  235. sprintf (
  236. 'select distinct %s from %s where %s is not null and %s != ""',
  237. $field, $table, $field, $field
  238. )
  239. );
  240. foreach ($res as $row) {
  241. $this->buffer->set ($this->intl->serialize ($row), array (
  242. 'string' => $row,
  243. 'params' => false,
  244. 'file' => $file,
  245. 'line' => false,
  246. ));
  247. }
  248. }
  249. }
  250. echo '</ul>';
  251. return true;
  252. }
  253. function _spt ($file, $data) {
  254. //echo 'Reading ' . $file . '<br />';
  255. global $simple;
  256. $delim_start = $simple->delim[$simple->use_delim][0];
  257. $literal_start = $simple->delim_literal[$simple->use_delim][0];
  258. $delim_end = $simple->delim[$simple->use_delim][1];
  259. $literal_end = $simple->delim_literal[$simple->use_delim][1];
  260. $tokens = preg_split ('/(' . $delim_start . '[\[\]\(\)a-zA-Z0-9\.,=<>\?&#$\'":;\!\=\/ _-]+' . $delim_end . ')/s', $data, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE);
  261. foreach ($tokens as $tok) {$_tok = substr (
  262. $tok,
  263. strlen ($literal_start),
  264. - strlen ($literal_end)
  265. );
  266. if (
  267. ! empty ($_tok) &&
  268. strpos ($tok, $literal_start) === 0 &&
  269. strrpos ($tok, $literal_end) === (strlen ($tok) - strlen ($literal_end))
  270. ) {
  271. $_is_tag = true;
  272. } else {
  273. $_is_tag = false;
  274. }
  275. if (strpos ($_tok, 'intl ') === 0) {
  276. $value = substr ($_tok, 5);
  277. $this->buffer->set ($this->intl->serialize ($value), array (
  278. 'string' => $value,
  279. 'params' => false,
  280. 'file' => $file,
  281. 'line' => false,
  282. ));
  283. }
  284. }
  285. return true;
  286. }
  287. // the purpose of the remaining methods is to parse file contents into
  288. // unnamed arrays in $buffer, each containing 'string', 'params', 'file',
  289. // and 'line' (if possible)
  290. function _tpl ($file, $data) {
  291. //echo 'Reading ' . $file . '<br />';
  292. /*
  293. $p = xml_parser_create ();
  294. if (! $p) {
  295. $this->error = 'Failed to create an XML parser!';
  296. return false;
  297. }
  298. if (! xml_parser_set_option ($p, XML_OPTION_CASE_FOLDING, false)) {
  299. xml_parser_free ($p);
  300. $this->error = 'Failed to disable case folding!';
  301. return false;
  302. }
  303. if (xml_parse_into_struct ($p, $data, $vals, $tags)) {
  304. xml_parser_free ($p);
  305. foreach ($vals as $node) {
  306. if (($node['type'] == 'open' || $node['type'] == 'complete') && $node['tag'] == 'xt:intl') {
  307. //echo 'Found One: ' . $node['value'] . '<br />';
  308. $this->buffer->set ($this->intl->serialize ($node['value']), array (
  309. 'string' => $node['value'],
  310. 'params' => false,
  311. 'file' => $file,
  312. 'line' => false,
  313. ));
  314. }
  315. }
  316. } else {
  317. $ec = xml_get_error_code ($p);
  318. $this->error = xml_error_string ($ec) . ' (Code ' . $ec;
  319. $this->error .= ', Line ' . xml_get_current_line_number ($p);
  320. $this->error .= ', Column ' . xml_get_current_column_number ($p) . ')';
  321. xml_parser_free ($p);
  322. //return false;
  323. */
  324. /* // parse with saf.HTML.Messy, works fine, but probably not worth the overhead
  325. global $loader;
  326. $loader->import ('saf.HTML.Messy');
  327. $messy = new Messy;
  328. $vals = $messy->parse ($data);
  329. foreach ($vals as $node) {
  330. if (($node['type'] == 'open' || $node['type'] == 'complete') && $node['tag'] == 'xt:intl') {
  331. //echo 'Found One: ' . $node['value'] . '<br />';
  332. $this->buffer->append (false, array (
  333. 'string' => $node['value'],
  334. 'params' => false,
  335. 'file' => $file,
  336. 'line' => false,
  337. ));
  338. }
  339. }
  340. */
  341. // parse via a regex, nice and simple
  342. preg_match_all ('/<xt:intl>(.*?)<\/xt:intl>/', $data, $keys, PREG_SET_ORDER);
  343. //info ($keys);
  344. foreach ($keys as $node) {
  345. //echo 'Found One: ' . $node[1] . '<br />';
  346. $this->buffer->set ($this->intl->serialize ($node[1]), array (
  347. 'string' => $node[1],
  348. 'params' => false,
  349. 'file' => $file,
  350. 'line' => false,
  351. ));
  352. }
  353. preg_match_all ('/intl_get ?\(\'(.*)?\'\)/', $data, $keys, PREG_SET_ORDER);
  354. //info ($keys);
  355. foreach ($keys as $node) {
  356. //echo 'Found One: ' . $node[1] . '<br />';
  357. $this->buffer->set ($this->intl->serialize ($node[1]), array (
  358. 'string' => $node[1],
  359. 'params' => false,
  360. 'file' => $file,
  361. 'line' => false,
  362. ));
  363. }
  364. // }
  365. return true;
  366. }
  367. function _php ($file, $data) {
  368. //echo 'Reading ' . $file . '<br />';
  369. if (! function_exists ('token_get_all')) {
  370. $this->error = 'Tokenizer PHP extension is not available.';
  371. return false;
  372. }
  373. $tokens = Tokenizer::normalize (token_get_all ($data));
  374. // compile array from first ( to next ) after $intl
  375. // or $GLOBALS['intl']
  376. $add = array ();
  377. $glob = false;
  378. $i = false;
  379. $a = false;
  380. $s = false;
  381. foreach ($tokens as $tok) {
  382. //echo token_name ($tok[0]) . ': ' . $tok[1] . BR;
  383. if ($tok[0] == T_VARIABLE && $tok[1] == '$intl') {
  384. // found intl
  385. // echo 'Found one ' . $tok[1] . '<br />';
  386. $i = true;
  387. } elseif ($tok[0] == T_STRING && $tok[1] == 'intl_get') {
  388. // found intl_get
  389. $i = true;
  390. } elseif ($tok[0] == T_STRING && $tok[1] == 'template_simple') {
  391. // we found a template_simple function!
  392. $s = true;
  393. } elseif ($tok[0] == T_VARIABLE && $tok[1] == '$GLOBALS') {
  394. // found global
  395. // echo 'Found one ' . $tok[1] . '<br />';
  396. $glob = true;
  397. continue;
  398. // } elseif ($glob && $tok[0] == 0 && $tok[1] == '[') {
  399. // $add[] = array ();
  400. } elseif ($globa && $tok[0] == 0 && $tok[1] == ']') {
  401. $glob = false;
  402. } elseif ($glob && $tok[0] == T_CONSTANT_ENCAPSED_STRING && $tok[1] == '\'intl\'') {
  403. $i = true;
  404. $glob = false;
  405. } elseif ($i && $tok[0] == 0 && $tok[1] == '(') {
  406. $add[] = array ('');
  407. $a = true;
  408. } elseif ($i && $tok[0] == 0 && ($tok[1] == ')' || $tok[1] == ';')) {
  409. $i = false;
  410. $a = false;
  411. } elseif ($i && $a && $tok[1] == ',') {
  412. $add[count ($add) - 1][] = '';
  413. } elseif ($i && $a && $tok[0] != T_WHITESPACE) {
  414. $add[count ($add) - 1][count ($add[count ($add) - 1]) - 1] .= $tok[1];
  415. // parse the content of template_simple function
  416. // like if it was a .spt file.
  417. } elseif ($s && $tok[0] == 0 && $tok[1] == '(') {
  418. $str = '';
  419. } else if ($s && $tok[0] == 0 && ($tok[1] == ')' || $tok[1] == ';')) {
  420. $s = false;
  421. $this->_spt($file, $str);
  422. } elseif ($s && $tok[0] != T_WHITESPACE) {
  423. $str .= $tok[1];
  424. }
  425. }
  426. foreach ($add as $item) {
  427. $string = array_shift ($item);
  428. if (strpos ($string, "'") === 0) {
  429. $string = ltrim (rtrim ($string, "'"), "'");
  430. } elseif (strpos ($string, '"') === 0) {
  431. $string = ltrim (rtrim ($string, '"'), '"');
  432. } else {
  433. continue;
  434. }
  435. // if (strstr ($string, "\\'") || strstr ($string, '\\"')) {
  436. // $string = stripslashes ($string);
  437. // }
  438. if (count ($item) > 0) {
  439. $params = $item;
  440. } else {
  441. $params = false;
  442. }
  443. $string = stripslashes ($string);
  444. $this->buffer->set ($this->intl->serialize ($string), array (
  445. 'string' => $string,
  446. 'params' => $params,
  447. 'file' => $file,
  448. 'line' => false,
  449. ));
  450. }
  451. return true;
  452. }
  453. }
  454. ?>