[ Index ]

WordPress Cross Reference

title

Body

[close]

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

   1  <?php
   2  /**
   3   * Class for working with PO files
   4   *
   5   * @version $Id: po.php 718 2012-10-31 00:32:02Z nbachiyski $
   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  	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  	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  	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  	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  	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                  $this->set_headers($this->make_headers($res['entry']->translations[0]));
 227              } else {
 228                  $this->add_entry($res['entry']);
 229              }
 230          }
 231          PO::read_line($f, 'clear');
 232          if ( false === $res ) {
 233              return false;
 234          }
 235          if ( ! $this->headers && ! $this->entries ) {
 236              return false;
 237          }
 238          return true;
 239      }
 240  
 241  	function read_entry($f, $lineno = 0) {
 242          $entry = new Translation_Entry();
 243          // where were we in the last step
 244          // can be: comment, msgctxt, msgid, msgid_plural, msgstr, msgstr_plural
 245          $context = '';
 246          $msgstr_index = 0;
 247          $is_final = create_function('$context', 'return $context == "msgstr" || $context == "msgstr_plural";');
 248          while (true) {
 249              $lineno++;
 250              $line = PO::read_line($f);
 251              if (!$line)  {
 252                  if (feof($f)) {
 253                      if ($is_final($context))
 254                          break;
 255                      elseif (!$context) // we haven't read a line and eof came
 256                          return null;
 257                      else
 258                          return false;
 259                  } else {
 260                      return false;
 261                  }
 262              }
 263              if ($line == "\n") continue;
 264              $line = trim($line);
 265              if (preg_match('/^#/', $line, $m)) {
 266                  // the comment is the start of a new entry
 267                  if ($is_final($context)) {
 268                      PO::read_line($f, 'put-back');
 269                      $lineno--;
 270                      break;
 271                  }
 272                  // comments have to be at the beginning
 273                  if ($context && $context != 'comment') {
 274                      return false;
 275                  }
 276                  // add comment
 277                  $this->add_comment_to_entry($entry, $line);
 278              } elseif (preg_match('/^msgctxt\s+(".*")/', $line, $m)) {
 279                  if ($is_final($context)) {
 280                      PO::read_line($f, 'put-back');
 281                      $lineno--;
 282                      break;
 283                  }
 284                  if ($context && $context != 'comment') {
 285                      return false;
 286                  }
 287                  $context = 'msgctxt';
 288                  $entry->context .= PO::unpoify($m[1]);
 289              } elseif (preg_match('/^msgid\s+(".*")/', $line, $m)) {
 290                  if ($is_final($context)) {
 291                      PO::read_line($f, 'put-back');
 292                      $lineno--;
 293                      break;
 294                  }
 295                  if ($context && $context != 'msgctxt' && $context != 'comment') {
 296                      return false;
 297                  }
 298                  $context = 'msgid';
 299                  $entry->singular .= PO::unpoify($m[1]);
 300              } elseif (preg_match('/^msgid_plural\s+(".*")/', $line, $m)) {
 301                  if ($context != 'msgid') {
 302                      return false;
 303                  }
 304                  $context = 'msgid_plural';
 305                  $entry->is_plural = true;
 306                  $entry->plural .= PO::unpoify($m[1]);
 307              } elseif (preg_match('/^msgstr\s+(".*")/', $line, $m)) {
 308                  if ($context != 'msgid') {
 309                      return false;
 310                  }
 311                  $context = 'msgstr';
 312                  $entry->translations = array(PO::unpoify($m[1]));
 313              } elseif (preg_match('/^msgstr\[(\d+)\]\s+(".*")/', $line, $m)) {
 314                  if ($context != 'msgid_plural' && $context != 'msgstr_plural') {
 315                      return false;
 316                  }
 317                  $context = 'msgstr_plural';
 318                  $msgstr_index = $m[1];
 319                  $entry->translations[$m[1]] = PO::unpoify($m[2]);
 320              } elseif (preg_match('/^".*"$/', $line)) {
 321                  $unpoified = PO::unpoify($line);
 322                  switch ($context) {
 323                      case 'msgid':
 324                          $entry->singular .= $unpoified; break;
 325                      case 'msgctxt':
 326                          $entry->context .= $unpoified; break;
 327                      case 'msgid_plural':
 328                          $entry->plural .= $unpoified; break;
 329                      case 'msgstr':
 330                          $entry->translations[0] .= $unpoified; break;
 331                      case 'msgstr_plural':
 332                          $entry->translations[$msgstr_index] .= $unpoified; break;
 333                      default:
 334                          return false;
 335                  }
 336              } else {
 337                  return false;
 338              }
 339          }
 340          if (array() == array_filter($entry->translations, create_function('$t', 'return $t || "0" === $t;'))) {
 341              $entry->translations = array();
 342          }
 343          return array('entry' => $entry, 'lineno' => $lineno);
 344      }
 345  
 346  	function read_line($f, $action = 'read') {
 347          static $last_line = '';
 348          static $use_last_line = false;
 349          if ('clear' == $action) {
 350              $last_line = '';
 351              return true;
 352          }
 353          if ('put-back' == $action) {
 354              $use_last_line = true;
 355              return true;
 356          }
 357          $line = $use_last_line? $last_line : fgets($f);
 358          $line = ( "\r\n" == substr( $line, -2 ) ) ? rtrim( $line, "\r\n" ) . "\n" : $line;
 359          $last_line = $line;
 360          $use_last_line = false;
 361          return $line;
 362      }
 363  
 364  	function add_comment_to_entry(&$entry, $po_comment_line) {
 365          $first_two = substr($po_comment_line, 0, 2);
 366          $comment = trim(substr($po_comment_line, 2));
 367          if ('#:' == $first_two) {
 368              $entry->references = array_merge($entry->references, preg_split('/\s+/', $comment));
 369          } elseif ('#.' == $first_two) {
 370              $entry->extracted_comments = trim($entry->extracted_comments . "\n" . $comment);
 371          } elseif ('#,' == $first_two) {
 372              $entry->flags = array_merge($entry->flags, preg_split('/,\s*/', $comment));
 373          } else {
 374              $entry->translator_comments = trim($entry->translator_comments . "\n" . $comment);
 375          }
 376      }
 377  
 378  	function trim_quotes($s) {
 379          if ( substr($s, 0, 1) == '"') $s = substr($s, 1);
 380          if ( substr($s, -1, 1) == '"') $s = substr($s, 0, -1);
 381          return $s;
 382      }
 383  }
 384  endif;


Generated: Tue Mar 25 01:41:18 2014 WordPress honlapkészítés: online1.hu