[ Index ]

PHP Cross Reference of Wordpress 2.7.1

title

Body

[close]

/wp-includes/ -> gettext.php (source)

   1  <?php
   2  /**
   3   * PHP-Gettext External Library: gettext_reader class
   4   *
   5   * @package External
   6   * @subpackage PHP-gettext
   7   *
   8   * @internal
   9       Copyright (c) 2003 Danilo Segan <danilo@kvota.net>.
  10       Copyright (c) 2005 Nico Kaiser <nico@siriux.net>
  11  
  12       This file is part of PHP-gettext.
  13  
  14       PHP-gettext is free software; you can redistribute it and/or modify
  15       it under the terms of the GNU General Public License as published by
  16       the Free Software Foundation; either version 2 of the License, or
  17       (at your option) any later version.
  18  
  19       PHP-gettext is distributed in the hope that it will be useful,
  20       but WITHOUT ANY WARRANTY; without even the implied warranty of
  21       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  22       GNU General Public License for more details.
  23  
  24       You should have received a copy of the GNU General Public License
  25       along with PHP-gettext; if not, write to the Free Software
  26       Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  27  
  28  */
  29  
  30  /**
  31   * Provides a simple gettext replacement that works independently from
  32   * the system's gettext abilities.
  33   * It can read MO files and use them for translating strings.
  34   * The files are passed to gettext_reader as a Stream (see streams.php)
  35   *
  36   * This version has the ability to cache all strings and translations to
  37   * speed up the string lookup.
  38   * While the cache is enabled by default, it can be switched off with the
  39   * second parameter in the constructor (e.g. whenusing very large MO files
  40   * that you don't want to keep in memory)
  41   */
  42  class gettext_reader {
  43      //public:
  44       var $error = 0; // public variable that holds error code (0 if no error)
  45  
  46       //private:
  47      var $BYTEORDER = 0;        // 0: low endian, 1: big endian
  48      var $STREAM = NULL;
  49      var $short_circuit = false;
  50      var $enable_cache = false;
  51      var $originals = NULL;      // offset of original table
  52      var $translations = NULL;    // offset of translation table
  53      var $pluralheader = NULL;    // cache header field for plural forms
  54      var $select_string_function = NULL; // cache function, which chooses plural forms
  55      var $total = 0;          // total string count
  56      var $table_originals = NULL;  // table for original strings (offsets)
  57      var $table_translations = NULL;  // table for translated strings (offsets)
  58      var $cache_translations = NULL;  // original -> translation mapping
  59  
  60  
  61      /* Methods */
  62  
  63  
  64      /**
  65       * Reads a 32bit Integer from the Stream
  66       *
  67       * @access private
  68       * @return Integer from the Stream
  69       */
  70  	function readint() {
  71          if ($this->BYTEORDER == 0) {
  72              // low endian
  73              $low_end = unpack('V', $this->STREAM->read(4));
  74              return array_shift($low_end);
  75          } else {
  76              // big endian
  77              $big_end = unpack('N', $this->STREAM->read(4));
  78              return array_shift($big_end);
  79          }
  80      }
  81  
  82      /**
  83       * Reads an array of Integers from the Stream
  84       *
  85       * @param int count How many elements should be read
  86       * @return Array of Integers
  87       */
  88  	function readintarray($count) {
  89      if ($this->BYTEORDER == 0) {
  90              // low endian
  91              return unpack('V'.$count, $this->STREAM->read(4 * $count));
  92          } else {
  93              // big endian
  94              return unpack('N'.$count, $this->STREAM->read(4 * $count));
  95          }
  96      }
  97  
  98      /**
  99       * Constructor
 100       *
 101       * @param object Reader the StreamReader object
 102       * @param boolean enable_cache Enable or disable caching of strings (default on)
 103       */
 104  	function gettext_reader($Reader, $enable_cache = true) {
 105          // If there isn't a StreamReader, turn on short circuit mode.
 106          if (! $Reader || isset($Reader->error) ) {
 107              $this->short_circuit = true;
 108              return;
 109          }
 110  
 111          // Caching can be turned off
 112          $this->enable_cache = $enable_cache;
 113  
 114          // $MAGIC1 = (int)0x950412de; //bug in PHP 5.0.2, see https://savannah.nongnu.org/bugs/?func=detailitem&item_id=10565
 115          $MAGIC1 = (int) - 1794895138;
 116          // $MAGIC2 = (int)0xde120495; //bug
 117          $MAGIC2 = (int) - 569244523;
 118          // 64-bit fix
 119          $MAGIC3 = (int) 2500072158;
 120  
 121          $this->STREAM = $Reader;
 122          $magic = $this->readint();
 123          if ($magic == $MAGIC1 || $magic == $MAGIC3) { // to make sure it works for 64-bit platforms
 124              $this->BYTEORDER = 0;
 125          } elseif ($magic == ($MAGIC2 & 0xFFFFFFFF)) {
 126              $this->BYTEORDER = 1;
 127          } else {
 128              $this->error = 1; // not MO file
 129              return false;
 130          }
 131  
 132          // FIXME: Do we care about revision? We should.
 133          $revision = $this->readint();
 134  
 135          $this->total = $this->readint();
 136          $this->originals = $this->readint();
 137          $this->translations = $this->readint();
 138      }
 139  
 140      /**
 141       * Loads the translation tables from the MO file into the cache
 142       * If caching is enabled, also loads all strings into a cache
 143       * to speed up translation lookups
 144       *
 145       * @access private
 146       */
 147  	function load_tables() {
 148          if (is_array($this->cache_translations) &&
 149              is_array($this->table_originals) &&
 150              is_array($this->table_translations))
 151              return;
 152  
 153          /* get original and translations tables */
 154          $this->STREAM->seekto($this->originals);
 155          $this->table_originals = $this->readintarray($this->total * 2);
 156          $this->STREAM->seekto($this->translations);
 157          $this->table_translations = $this->readintarray($this->total * 2);
 158  
 159          if ($this->enable_cache) {
 160              $this->cache_translations = array ();
 161              /* read all strings in the cache */
 162              for ($i = 0; $i < $this->total; $i++) {
 163                  $this->STREAM->seekto($this->table_originals[$i * 2 + 2]);
 164                  $original = $this->STREAM->read($this->table_originals[$i * 2 + 1]);
 165                  $this->STREAM->seekto($this->table_translations[$i * 2 + 2]);
 166                  $translation = $this->STREAM->read($this->table_translations[$i * 2 + 1]);
 167                  $this->cache_translations[$original] = $translation;
 168              }
 169          }
 170      }
 171  
 172      /**
 173       * Returns a string from the "originals" table
 174       *
 175       * @access private
 176       * @param int num Offset number of original string
 177       * @return string Requested string if found, otherwise ''
 178       */
 179  	function get_original_string($num) {
 180          $length = $this->table_originals[$num * 2 + 1];
 181          $offset = $this->table_originals[$num * 2 + 2];
 182          if (! $length)
 183              return '';
 184          $this->STREAM->seekto($offset);
 185          $data = $this->STREAM->read($length);
 186          return (string)$data;
 187      }
 188  
 189      /**
 190       * Returns a string from the "translations" table
 191       *
 192       * @access private
 193       * @param int num Offset number of original string
 194       * @return string Requested string if found, otherwise ''
 195       */
 196  	function get_translation_string($num) {
 197          $length = $this->table_translations[$num * 2 + 1];
 198          $offset = $this->table_translations[$num * 2 + 2];
 199          if (! $length)
 200              return '';
 201          $this->STREAM->seekto($offset);
 202          $data = $this->STREAM->read($length);
 203          return (string)$data;
 204      }
 205  
 206      /**
 207       * Binary search for string
 208       *
 209       * @access private
 210       * @param string string
 211       * @param int start (internally used in recursive function)
 212       * @param int end (internally used in recursive function)
 213       * @return int string number (offset in originals table)
 214       */
 215  	function find_string($string, $start = -1, $end = -1) {
 216          if (($start == -1) or ($end == -1)) {
 217              // find_string is called with only one parameter, set start end end
 218              $start = 0;
 219              $end = $this->total;
 220          }
 221          if (abs($start - $end) <= 1) {
 222              // We're done, now we either found the string, or it doesn't exist
 223              $txt = $this->get_original_string($start);
 224              if ($string == $txt)
 225                  return $start;
 226              else
 227                  return -1;
 228          } else if ($start > $end) {
 229              // start > end -> turn around and start over
 230              return $this->find_string($string, $end, $start);
 231          } else {
 232              // Divide table in two parts
 233              $half = (int)(($start + $end) / 2);
 234              $cmp = strcmp($string, $this->get_original_string($half));
 235              if ($cmp == 0)
 236                  // string is exactly in the middle => return it
 237                  return $half;
 238              else if ($cmp < 0)
 239                  // The string is in the upper half
 240                  return $this->find_string($string, $start, $half);
 241              else
 242                  // The string is in the lower half
 243                  return $this->find_string($string, $half, $end);
 244          }
 245      }
 246  
 247      /**
 248       * Translates a string
 249       *
 250       * @access public
 251       * @param string string to be translated
 252       * @return string translated string (or original, if not found)
 253       */
 254  	function translate($string) {
 255          if ($this->short_circuit)
 256              return $string;
 257          $this->load_tables();
 258  
 259          if ($this->enable_cache) {
 260              // Caching enabled, get translated string from cache
 261              if (array_key_exists($string, $this->cache_translations))
 262                  return $this->cache_translations[$string];
 263              else
 264                  return $string;
 265          } else {
 266              // Caching not enabled, try to find string
 267              $num = $this->find_string($string);
 268              if ($num == -1)
 269                  return $string;
 270              else
 271                  return $this->get_translation_string($num);
 272          }
 273      }
 274  
 275      /**
 276       * Get possible plural forms from MO header
 277       *
 278       * @access private
 279       * @return string plural form header
 280       */
 281  	function get_plural_forms() {
 282          // lets assume message number 0 is header
 283          // this is true, right?
 284          $this->load_tables();
 285  
 286          // cache header field for plural forms
 287          if (! is_string($this->pluralheader)) {
 288              if ($this->enable_cache) {
 289                  $header = $this->cache_translations[""];
 290              } else {
 291                  $header = $this->get_translation_string(0);
 292              }
 293              $header .= "\n"; //make sure our regex matches
 294              if (eregi("plural-forms: ([^\n]*)\n", $header, $regs))
 295                  $expr = $regs[1];
 296              else
 297                  $expr = "nplurals=2; plural=n == 1 ? 0 : 1;";
 298  
 299              // add parentheses
 300               // important since PHP's ternary evaluates from left to right
 301               $expr.= ';';
 302               $res= '';
 303               $p= 0;
 304               for ($i= 0; $i < strlen($expr); $i++) {
 305                  $ch= $expr[$i];
 306                  switch ($ch) {
 307                      case '?':
 308                          $res.= ' ? (';
 309                          $p++;
 310                          break;
 311                      case ':':
 312                          $res.= ') : (';
 313                          break;
 314                      case ';':
 315                          $res.= str_repeat( ')', $p) . ';';
 316                          $p= 0;
 317                          break;
 318                      default:
 319                          $res.= $ch;
 320                  }
 321              }
 322              $this->pluralheader = $res;
 323          }
 324  
 325          return $this->pluralheader;
 326      }
 327  
 328      /**
 329       * Detects which plural form to take
 330       *
 331       * @access private
 332       * @param n count
 333       * @return int array index of the right plural form
 334       */
 335  	function select_string($n) {
 336          if (is_null($this->select_string_function)) {
 337              $string = $this->get_plural_forms();
 338              if (preg_match("/nplurals\s*=\s*(\d+)\s*\;\s*plural\s*=\s*(.*?)\;+/", $string, $matches)) {
 339                  $nplurals = $matches[1];
 340                  $expression = $matches[2];
 341                  $expression = str_replace("n", '$n', $expression);
 342              } else {
 343                  $nplurals = 2;
 344                  $expression = ' $n == 1 ? 0 : 1 ';
 345              }
 346              $func_body = "
 347                  \$plural = ($expression);
 348                  return (\$plural <= $nplurals)? \$plural : \$plural - 1;";
 349              $this->select_string_function = create_function('$n', $func_body);
 350          }
 351          return call_user_func($this->select_string_function, $n);
 352      }
 353  
 354      /**
 355       * Plural version of gettext
 356       *
 357       * @access public
 358       * @param string single
 359       * @param string plural
 360       * @param string number
 361       * @return translated plural form
 362       */
 363  	function ngettext($single, $plural, $number) {
 364          if ($this->short_circuit) {
 365              if ($number != 1)
 366                  return $plural;
 367              else
 368                  return $single;
 369          }
 370  
 371          // find out the appropriate form
 372          $select = $this->select_string($number);
 373  
 374          // this should contains all strings separated by NULLs
 375          $key = $single.chr(0).$plural;
 376  
 377  
 378          if ($this->enable_cache) {
 379              if (! array_key_exists($key, $this->cache_translations)) {
 380                  return ($number != 1) ? $plural : $single;
 381              } else {
 382                  $result = $this->cache_translations[$key];
 383                  $list = explode(chr(0), $result);
 384                  return $list[$select];
 385              }
 386          } else {
 387              $num = $this->find_string($key);
 388              if ($num == -1) {
 389                  return ($number != 1) ? $plural : $single;
 390              } else {
 391                  $result = $this->get_translation_string($num);
 392                  $list = explode(chr(0), $result);
 393                  return $list[$select];
 394              }
 395          }
 396      }
 397  
 398  }
 399  
 400  ?>


Generated: Mon Mar 23 16:23:02 2009 Cross-referenced by PHPXref 0.7