PageRenderTime 57ms CodeModel.GetById 27ms RepoModel.GetById 0ms app.codeStats 0ms

/wp-includes/pomo/po.php

https://bitbucket.org/MaheshDhaduk/androidmobiles
PHP | 363 lines | 272 code | 18 blank | 73 comment | 71 complexity | 25797830621b132daa3dfd9b98475f84 MD5 | raw file
Possible License(s): GPL-3.0, LGPL-2.1, AGPL-1.0
  1. <?php
  2. /**
  3. * Class for working with PO files
  4. *
  5. * @version $Id: po.php 406 2010-02-07 11:10:24Z nbachiyski $
  6. * @package pomo
  7. * @subpackage po
  8. */
  9. require_once dirname(__FILE__) . '/translations.php';
  10. define('PO_MAX_LINE_LEN', 79);
  11. ini_set('auto_detect_line_endings', 1);
  12. /**
  13. * Routines for working with PO files
  14. */
  15. if ( !class_exists( 'PO' ) ):
  16. class PO extends Gettext_Translations {
  17. /**
  18. * Exports headers to a PO entry
  19. *
  20. * @return string msgid/msgstr PO entry for this PO file headers, doesn't contain newline at the end
  21. */
  22. function export_headers() {
  23. $header_string = '';
  24. foreach($this->headers as $header => $value) {
  25. $header_string.= "$header: $value\n";
  26. }
  27. $poified = PO::poify($header_string);
  28. return rtrim("msgid \"\"\nmsgstr $poified");
  29. }
  30. /**
  31. * Exports all entries to PO format
  32. *
  33. * @return string sequence of mgsgid/msgstr PO strings, doesn't containt newline at the end
  34. */
  35. function export_entries() {
  36. //TODO sorting
  37. return implode("\n\n", array_map(array('PO', 'export_entry'), $this->entries));
  38. }
  39. /**
  40. * Exports the whole PO file as a string
  41. *
  42. * @param bool $include_headers whether to include the headers in the export
  43. * @return string ready for inclusion in PO file string for headers and all the enrtries
  44. */
  45. function export($include_headers = true) {
  46. $res = '';
  47. if ($include_headers) {
  48. $res .= $this->export_headers();
  49. $res .= "\n\n";
  50. }
  51. $res .= $this->export_entries();
  52. return $res;
  53. }
  54. /**
  55. * Same as {@link export}, but writes the result to a file
  56. *
  57. * @param string $filename where to write the PO string
  58. * @param bool $include_headers whether to include tje headers in the export
  59. * @return bool true on success, false on error
  60. */
  61. function export_to_file($filename, $include_headers = true) {
  62. $fh = fopen($filename, 'w');
  63. if (false === $fh) return false;
  64. $export = $this->export($include_headers);
  65. $res = fwrite($fh, $export);
  66. if (false === $res) return false;
  67. return fclose($fh);
  68. }
  69. /**
  70. * Formats a string in PO-style
  71. *
  72. * @static
  73. * @param string $string the string to format
  74. * @return string the poified string
  75. */
  76. function poify($string) {
  77. $quote = '"';
  78. $slash = '\\';
  79. $newline = "\n";
  80. $replaces = array(
  81. "$slash" => "$slash$slash",
  82. "$quote" => "$slash$quote",
  83. "\t" => '\t',
  84. );
  85. $string = str_replace(array_keys($replaces), array_values($replaces), $string);
  86. $po = $quote.implode("${slash}n$quote$newline$quote", explode($newline, $string)).$quote;
  87. // add empty string on first line for readbility
  88. if (false !== strpos($string, $newline) &&
  89. (substr_count($string, $newline) > 1 || !($newline === substr($string, -strlen($newline))))) {
  90. $po = "$quote$quote$newline$po";
  91. }
  92. // remove empty strings
  93. $po = str_replace("$newline$quote$quote", '', $po);
  94. return $po;
  95. }
  96. /**
  97. * Gives back the original string from a PO-formatted string
  98. *
  99. * @static
  100. * @param string $string PO-formatted string
  101. * @return string enascaped string
  102. */
  103. function unpoify($string) {
  104. $escapes = array('t' => "\t", 'n' => "\n", '\\' => '\\');
  105. $lines = array_map('trim', explode("\n", $string));
  106. $lines = array_map(array('PO', 'trim_quotes'), $lines);
  107. $unpoified = '';
  108. $previous_is_backslash = false;
  109. foreach($lines as $line) {
  110. preg_match_all('/./u', $line, $chars);
  111. $chars = $chars[0];
  112. foreach($chars as $char) {
  113. if (!$previous_is_backslash) {
  114. if ('\\' == $char)
  115. $previous_is_backslash = true;
  116. else
  117. $unpoified .= $char;
  118. } else {
  119. $previous_is_backslash = false;
  120. $unpoified .= isset($escapes[$char])? $escapes[$char] : $char;
  121. }
  122. }
  123. }
  124. return $unpoified;
  125. }
  126. /**
  127. * Inserts $with in the beginning of every new line of $string and
  128. * returns the modified string
  129. *
  130. * @static
  131. * @param string $string prepend lines in this string
  132. * @param string $with prepend lines with this string
  133. */
  134. function prepend_each_line($string, $with) {
  135. $php_with = var_export($with, true);
  136. $lines = explode("\n", $string);
  137. // do not prepend the string on the last empty line, artefact by explode
  138. if ("\n" == substr($string, -1)) unset($lines[count($lines) - 1]);
  139. $res = implode("\n", array_map(create_function('$x', "return $php_with.\$x;"), $lines));
  140. // give back the empty line, we ignored above
  141. if ("\n" == substr($string, -1)) $res .= "\n";
  142. return $res;
  143. }
  144. /**
  145. * Prepare a text as a comment -- wraps the lines and prepends #
  146. * and a special character to each line
  147. *
  148. * @access private
  149. * @param string $text the comment text
  150. * @param string $char character to denote a special PO comment,
  151. * like :, default is a space
  152. */
  153. function comment_block($text, $char=' ') {
  154. $text = wordwrap($text, PO_MAX_LINE_LEN - 3);
  155. return PO::prepend_each_line($text, "#$char ");
  156. }
  157. /**
  158. * Builds a string from the entry for inclusion in PO file
  159. *
  160. * @static
  161. * @param object &$entry the entry to convert to po string
  162. * @return string|bool PO-style formatted string for the entry or
  163. * false if the entry is empty
  164. */
  165. function export_entry(&$entry) {
  166. if (is_null($entry->singular)) return false;
  167. $po = array();
  168. if (!empty($entry->translator_comments)) $po[] = PO::comment_block($entry->translator_comments);
  169. if (!empty($entry->extracted_comments)) $po[] = PO::comment_block($entry->extracted_comments, '.');
  170. if (!empty($entry->references)) $po[] = PO::comment_block(implode(' ', $entry->references), ':');
  171. if (!empty($entry->flags)) $po[] = PO::comment_block(implode(", ", $entry->flags), ',');
  172. if (!is_null($entry->context)) $po[] = 'msgctxt '.PO::poify($entry->context);
  173. $po[] = 'msgid '.PO::poify($entry->singular);
  174. if (!$entry->is_plural) {
  175. $translation = empty($entry->translations)? '' : $entry->translations[0];
  176. $po[] = 'msgstr '.PO::poify($translation);
  177. } else {
  178. $po[] = 'msgid_plural '.PO::poify($entry->plural);
  179. $translations = empty($entry->translations)? array('', '') : $entry->translations;
  180. foreach($translations as $i => $translation) {
  181. $po[] = "msgstr[$i] ".PO::poify($translation);
  182. }
  183. }
  184. return implode("\n", $po);
  185. }
  186. function import_from_file($filename) {
  187. $f = fopen($filename, 'r');
  188. if (!$f) return false;
  189. $lineno = 0;
  190. while (true) {
  191. $res = $this->read_entry($f, $lineno);
  192. if (!$res) break;
  193. if ($res['entry']->singular == '') {
  194. $this->set_headers($this->make_headers($res['entry']->translations[0]));
  195. } else {
  196. $this->add_entry($res['entry']);
  197. }
  198. }
  199. PO::read_line($f, 'clear');
  200. return $res !== false;
  201. }
  202. function read_entry($f, $lineno = 0) {
  203. $entry = new Translation_Entry();
  204. // where were we in the last step
  205. // can be: comment, msgctxt, msgid, msgid_plural, msgstr, msgstr_plural
  206. $context = '';
  207. $msgstr_index = 0;
  208. $is_final = create_function('$context', 'return $context == "msgstr" || $context == "msgstr_plural";');
  209. while (true) {
  210. $lineno++;
  211. $line = PO::read_line($f);
  212. if (!$line) {
  213. if (feof($f)) {
  214. if ($is_final($context))
  215. break;
  216. elseif (!$context) // we haven't read a line and eof came
  217. return null;
  218. else
  219. return false;
  220. } else {
  221. return false;
  222. }
  223. }
  224. if ($line == "\n") continue;
  225. $line = trim($line);
  226. if (preg_match('/^#/', $line, $m)) {
  227. // the comment is the start of a new entry
  228. if ($is_final($context)) {
  229. PO::read_line($f, 'put-back');
  230. $lineno--;
  231. break;
  232. }
  233. // comments have to be at the beginning
  234. if ($context && $context != 'comment') {
  235. return false;
  236. }
  237. // add comment
  238. $this->add_comment_to_entry($entry, $line);
  239. } elseif (preg_match('/^msgctxt\s+(".*")/', $line, $m)) {
  240. if ($is_final($context)) {
  241. PO::read_line($f, 'put-back');
  242. $lineno--;
  243. break;
  244. }
  245. if ($context && $context != 'comment') {
  246. return false;
  247. }
  248. $context = 'msgctxt';
  249. $entry->context .= PO::unpoify($m[1]);
  250. } elseif (preg_match('/^msgid\s+(".*")/', $line, $m)) {
  251. if ($is_final($context)) {
  252. PO::read_line($f, 'put-back');
  253. $lineno--;
  254. break;
  255. }
  256. if ($context && $context != 'msgctxt' && $context != 'comment') {
  257. return false;
  258. }
  259. $context = 'msgid';
  260. $entry->singular .= PO::unpoify($m[1]);
  261. } elseif (preg_match('/^msgid_plural\s+(".*")/', $line, $m)) {
  262. if ($context != 'msgid') {
  263. return false;
  264. }
  265. $context = 'msgid_plural';
  266. $entry->is_plural = true;
  267. $entry->plural .= PO::unpoify($m[1]);
  268. } elseif (preg_match('/^msgstr\s+(".*")/', $line, $m)) {
  269. if ($context != 'msgid') {
  270. return false;
  271. }
  272. $context = 'msgstr';
  273. $entry->translations = array(PO::unpoify($m[1]));
  274. } elseif (preg_match('/^msgstr\[(\d+)\]\s+(".*")/', $line, $m)) {
  275. if ($context != 'msgid_plural' && $context != 'msgstr_plural') {
  276. return false;
  277. }
  278. $context = 'msgstr_plural';
  279. $msgstr_index = $m[1];
  280. $entry->translations[$m[1]] = PO::unpoify($m[2]);
  281. } elseif (preg_match('/^".*"$/', $line)) {
  282. $unpoified = PO::unpoify($line);
  283. switch ($context) {
  284. case 'msgid':
  285. $entry->singular .= $unpoified; break;
  286. case 'msgctxt':
  287. $entry->context .= $unpoified; break;
  288. case 'msgid_plural':
  289. $entry->plural .= $unpoified; break;
  290. case 'msgstr':
  291. $entry->translations[0] .= $unpoified; break;
  292. case 'msgstr_plural':
  293. $entry->translations[$msgstr_index] .= $unpoified; break;
  294. default:
  295. return false;
  296. }
  297. } else {
  298. return false;
  299. }
  300. }
  301. if (array() == array_filter($entry->translations, create_function('$t', 'return $t || "0" === $t;'))) {
  302. $entry->translations = array();
  303. }
  304. return array('entry' => $entry, 'lineno' => $lineno);
  305. }
  306. function read_line($f, $action = 'read') {
  307. static $last_line = '';
  308. static $use_last_line = false;
  309. if ('clear' == $action) {
  310. $last_line = '';
  311. return true;
  312. }
  313. if ('put-back' == $action) {
  314. $use_last_line = true;
  315. return true;
  316. }
  317. $line = $use_last_line? $last_line : fgets($f);
  318. $last_line = $line;
  319. $use_last_line = false;
  320. return $line;
  321. }
  322. function add_comment_to_entry(&$entry, $po_comment_line) {
  323. $first_two = substr($po_comment_line, 0, 2);
  324. $comment = trim(substr($po_comment_line, 2));
  325. if ('#:' == $first_two) {
  326. $entry->references = array_merge($entry->references, preg_split('/\s+/', $comment));
  327. } elseif ('#.' == $first_two) {
  328. $entry->extracted_comments = trim($entry->extracted_comments . "\n" . $comment);
  329. } elseif ('#,' == $first_two) {
  330. $entry->flags = array_merge($entry->flags, preg_split('/,\s*/', $comment));
  331. } else {
  332. $entry->translator_comments = trim($entry->translator_comments . "\n" . $comment);
  333. }
  334. }
  335. function trim_quotes($s) {
  336. if ( substr($s, 0, 1) == '"') $s = substr($s, 1);
  337. if ( substr($s, -1, 1) == '"') $s = substr($s, 0, -1);
  338. return $s;
  339. }
  340. }
  341. endif;