[ Index ]

PHP Cross Reference of Wordpress 2.9.1

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


Generated: Fri Jan 8 00:19:48 2010 Cross-referenced by PHPXref 0.7