[ Index ]

PHP Cross Reference of BackPress

title

Body

[close]

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

   1  <?php
   2  /**
   3   * Class for a set of entries for translation and their associated headers
   4   *
   5   * @version $Id: translations.php 1157 2015-11-20 04:30:11Z dd32 $
   6   * @package pomo
   7   * @subpackage translations
   8   */
   9  
  10  require_once dirname(__FILE__) . '/entry.php';
  11  
  12  if ( ! class_exists( 'Translations', false ) ):
  13  class Translations {
  14      var $entries = array();
  15      var $headers = array();
  16  
  17      /**
  18       * Add entry to the PO structure
  19       *
  20       * @param array|Translation_Entry &$entry
  21       * @return bool true on success, false if the entry doesn't have a key
  22       */
  23  	function add_entry($entry) {
  24          if (is_array($entry)) {
  25              $entry = new Translation_Entry($entry);
  26          }
  27          $key = $entry->key();
  28          if (false === $key) return false;
  29          $this->entries[$key] = &$entry;
  30          return true;
  31      }
  32  
  33      /**
  34       * @param array|Translation_Entry $entry
  35       * @return bool
  36       */
  37  	function add_entry_or_merge($entry) {
  38          if (is_array($entry)) {
  39              $entry = new Translation_Entry($entry);
  40          }
  41          $key = $entry->key();
  42          if (false === $key) return false;
  43          if (isset($this->entries[$key]))
  44              $this->entries[$key]->merge_with($entry);
  45          else
  46              $this->entries[$key] = &$entry;
  47          return true;
  48      }
  49  
  50      /**
  51       * Sets $header PO header to $value
  52       *
  53       * If the header already exists, it will be overwritten
  54       *
  55       * TODO: this should be out of this class, it is gettext specific
  56       *
  57       * @param string $header header name, without trailing :
  58       * @param string $value header value, without trailing \n
  59       */
  60  	function set_header($header, $value) {
  61          $this->headers[$header] = $value;
  62      }
  63  
  64      /**
  65       * @param array $headers
  66       */
  67  	function set_headers($headers) {
  68          foreach($headers as $header => $value) {
  69              $this->set_header($header, $value);
  70          }
  71      }
  72  
  73      /**
  74       * @param string $header
  75       */
  76  	function get_header($header) {
  77          return isset($this->headers[$header])? $this->headers[$header] : false;
  78      }
  79  
  80      /**
  81       * @param Translation_Entry $entry
  82       */
  83  	function translate_entry(&$entry) {
  84          $key = $entry->key();
  85          return isset($this->entries[$key])? $this->entries[$key] : false;
  86      }
  87  
  88      /**
  89       * @param string $singular
  90       * @param string $context
  91       * @return string
  92       */
  93  	function translate($singular, $context=null) {
  94          $entry = new Translation_Entry(array('singular' => $singular, 'context' => $context));
  95          $translated = $this->translate_entry($entry);
  96          return ($translated && !empty($translated->translations))? $translated->translations[0] : $singular;
  97      }
  98  
  99      /**
 100       * Given the number of items, returns the 0-based index of the plural form to use
 101       *
 102       * Here, in the base Translations class, the common logic for English is implemented:
 103       *     0 if there is one element, 1 otherwise
 104       *
 105       * This function should be overrided by the sub-classes. For example MO/PO can derive the logic
 106       * from their headers.
 107       *
 108       * @param integer $count number of items
 109       */
 110  	function select_plural_form($count) {
 111          return 1 == $count? 0 : 1;
 112      }
 113  
 114      /**
 115       * @return int
 116       */
 117  	function get_plural_forms_count() {
 118          return 2;
 119      }
 120  
 121      /**
 122       * @param string $singular
 123       * @param string $plural
 124       * @param int    $count
 125       * @param string $context
 126       */
 127  	function translate_plural($singular, $plural, $count, $context = null) {
 128          $entry = new Translation_Entry(array('singular' => $singular, 'plural' => $plural, 'context' => $context));
 129          $translated = $this->translate_entry($entry);
 130          $index = $this->select_plural_form($count);
 131          $total_plural_forms = $this->get_plural_forms_count();
 132          if ($translated && 0 <= $index && $index < $total_plural_forms &&
 133                  is_array($translated->translations) &&
 134                  isset($translated->translations[$index]))
 135              return $translated->translations[$index];
 136          else
 137              return 1 == $count? $singular : $plural;
 138      }
 139  
 140      /**
 141       * Merge $other in the current object.
 142       *
 143       * @param Object &$other Another Translation object, whose translations will be merged in this one
 144       * @return void
 145       **/
 146  	function merge_with(&$other) {
 147          foreach( $other->entries as $entry ) {
 148              $this->entries[$entry->key()] = $entry;
 149          }
 150      }
 151  
 152      /**
 153       * @param object $other
 154       */
 155  	function merge_originals_with(&$other) {
 156          foreach( $other->entries as $entry ) {
 157              if ( !isset( $this->entries[$entry->key()] ) )
 158                  $this->entries[$entry->key()] = $entry;
 159              else
 160                  $this->entries[$entry->key()]->merge_with($entry);
 161          }
 162      }
 163  }
 164  
 165  class Gettext_Translations extends Translations {
 166      /**
 167       * The gettext implementation of select_plural_form.
 168       *
 169       * It lives in this class, because there are more than one descendand, which will use it and
 170       * they can't share it effectively.
 171       *
 172       * @param int $count
 173       */
 174  	function gettext_select_plural_form($count) {
 175          if (!isset($this->_gettext_select_plural_form) || is_null($this->_gettext_select_plural_form)) {
 176              list( $nplurals, $expression ) = $this->nplurals_and_expression_from_header($this->get_header('Plural-Forms'));
 177              $this->_nplurals = $nplurals;
 178              $this->_gettext_select_plural_form = $this->make_plural_form_function($nplurals, $expression);
 179          }
 180          return call_user_func($this->_gettext_select_plural_form, $count);
 181      }
 182  
 183      /**
 184       * @param string $header
 185       * @return array
 186       */
 187  	function nplurals_and_expression_from_header($header) {
 188          if (preg_match('/^\s*nplurals\s*=\s*(\d+)\s*;\s+plural\s*=\s*(.+)$/', $header, $matches)) {
 189              $nplurals = (int)$matches[1];
 190              $expression = trim($this->parenthesize_plural_exression($matches[2]));
 191              return array($nplurals, $expression);
 192          } else {
 193              return array(2, 'n != 1');
 194          }
 195      }
 196  
 197      /**
 198       * Makes a function, which will return the right translation index, according to the
 199       * plural forms header
 200       * @param int    $nplurals
 201       * @param string $expression
 202       */
 203  	function make_plural_form_function($nplurals, $expression) {
 204          $expression = str_replace('n', '$n', $expression);
 205          $func_body = "
 206              \$index = (int)($expression);
 207              return (\$index < $nplurals)? \$index : $nplurals - 1;";
 208          return create_function('$n', $func_body);
 209      }
 210  
 211      /**
 212       * Adds parentheses to the inner parts of ternary operators in
 213       * plural expressions, because PHP evaluates ternary oerators from left to right
 214       *
 215       * @param string $expression the expression without parentheses
 216       * @return string the expression with parentheses added
 217       */
 218  	function parenthesize_plural_exression($expression) {
 219          $expression .= ';';
 220          $res = '';
 221          $depth = 0;
 222          for ($i = 0; $i < strlen($expression); ++$i) {
 223              $char = $expression[$i];
 224              switch ($char) {
 225                  case '?':
 226                      $res .= ' ? (';
 227                      $depth++;
 228                      break;
 229                  case ':':
 230                      $res .= ') : (';
 231                      break;
 232                  case ';':
 233                      $res .= str_repeat(')', $depth) . ';';
 234                      $depth= 0;
 235                      break;
 236                  default:
 237                      $res .= $char;
 238              }
 239          }
 240          return rtrim($res, ';');
 241      }
 242  
 243      /**
 244       * @param string $translation
 245       * @return array
 246       */
 247  	function make_headers($translation) {
 248          $headers = array();
 249          // sometimes \ns are used instead of real new lines
 250          $translation = str_replace('\n', "\n", $translation);
 251          $lines = explode("\n", $translation);
 252          foreach($lines as $line) {
 253              $parts = explode(':', $line, 2);
 254              if (!isset($parts[1])) continue;
 255              $headers[trim($parts[0])] = trim($parts[1]);
 256          }
 257          return $headers;
 258      }
 259  
 260      /**
 261       * @param string $header
 262       * @param string $value
 263       */
 264  	function set_header($header, $value) {
 265          parent::set_header($header, $value);
 266          if ('Plural-Forms' == $header) {
 267              list( $nplurals, $expression ) = $this->nplurals_and_expression_from_header($this->get_header('Plural-Forms'));
 268              $this->_nplurals = $nplurals;
 269              $this->_gettext_select_plural_form = $this->make_plural_form_function($nplurals, $expression);
 270          }
 271      }
 272  }
 273  endif;
 274  
 275  if ( ! class_exists( 'NOOP_Translations', false ) ):
 276  /**
 277   * Provides the same interface as Translations, but doesn't do anything
 278   */
 279  class NOOP_Translations {
 280      var $entries = array();
 281      var $headers = array();
 282  
 283  	function add_entry($entry) {
 284          return true;
 285      }
 286  
 287      /**
 288       *
 289       * @param string $header
 290       * @param string $value
 291       */
 292  	function set_header($header, $value) {
 293      }
 294  
 295      /**
 296       *
 297       * @param array $headers
 298       */
 299  	function set_headers($headers) {
 300      }
 301  
 302      /**
 303       * @param string $header
 304       * @return false
 305       */
 306  	function get_header($header) {
 307          return false;
 308      }
 309  
 310      /**
 311       * @param Translation_Entry $entry
 312       * @return false
 313       */
 314  	function translate_entry(&$entry) {
 315          return false;
 316      }
 317  
 318      /**
 319       * @param string $singular
 320       * @param string $context
 321       */
 322  	function translate($singular, $context=null) {
 323          return $singular;
 324      }
 325  
 326      /**
 327       *
 328       * @param int $count
 329       * @return bool
 330       */
 331  	function select_plural_form($count) {
 332          return 1 == $count? 0 : 1;
 333      }
 334  
 335      /**
 336       * @return int
 337       */
 338  	function get_plural_forms_count() {
 339          return 2;
 340      }
 341  
 342      /**
 343       * @param string $singular
 344       * @param string $plural
 345       * @param int    $count
 346       * @param string $context
 347       */
 348  	function translate_plural($singular, $plural, $count, $context = null) {
 349              return 1 == $count? $singular : $plural;
 350      }
 351  
 352      /**
 353       * @param object $other
 354       */
 355  	function merge_with(&$other) {
 356      }
 357  }
 358  endif;


Generated: Mon Aug 26 01:01:34 2019 Cross-referenced by PHPXref 0.7.1