[ Index ]

PHP Cross Reference of BackPress

title

Body

[close]

/includes/pomo/ -> po.php (source)

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


Generated: Thu Jun 20 03:58:40 2013 Hosted by follow the white rabbit.