[ Index ] |
PHP Cross Reference of WordPress Trunk (Updated Daily) |
[Summary view] [Print] [Text view]
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 * @since 2.8.0 9 */ 10 11 require_once __DIR__ . '/plural-forms.php'; 12 require_once __DIR__ . '/entry.php'; 13 14 if ( ! class_exists( 'Translations', false ) ) : 15 /** 16 * Translations class. 17 * 18 * @since 2.8.0 19 */ 20 #[AllowDynamicProperties] 21 class Translations { 22 /** 23 * List of translation entries. 24 * 25 * @since 2.8.0 26 * 27 * @var Translation_Entry[] 28 */ 29 public $entries = array(); 30 31 /** 32 * List of translation headers. 33 * 34 * @since 2.8.0 35 * 36 * @var array<string, string> 37 */ 38 public $headers = array(); 39 40 /** 41 * Adds an entry to the PO structure. 42 * 43 * @since 2.8.0 44 * 45 * @param array|Translation_Entry $entry 46 * @return bool True on success, false if the entry doesn't have a key. 47 */ 48 public function add_entry( $entry ) { 49 if ( is_array( $entry ) ) { 50 $entry = new Translation_Entry( $entry ); 51 } 52 $key = $entry->key(); 53 if ( false === $key ) { 54 return false; 55 } 56 $this->entries[ $key ] = &$entry; 57 return true; 58 } 59 60 /** 61 * Adds or merges an entry to the PO structure. 62 * 63 * @since 2.8.0 64 * 65 * @param array|Translation_Entry $entry 66 * @return bool True on success, false if the entry doesn't have a key. 67 */ 68 public function add_entry_or_merge( $entry ) { 69 if ( is_array( $entry ) ) { 70 $entry = new Translation_Entry( $entry ); 71 } 72 $key = $entry->key(); 73 if ( false === $key ) { 74 return false; 75 } 76 if ( isset( $this->entries[ $key ] ) ) { 77 $this->entries[ $key ]->merge_with( $entry ); 78 } else { 79 $this->entries[ $key ] = &$entry; 80 } 81 return true; 82 } 83 84 /** 85 * Sets $header PO header to $value 86 * 87 * If the header already exists, it will be overwritten 88 * 89 * TODO: this should be out of this class, it is gettext specific 90 * 91 * @since 2.8.0 92 * 93 * @param string $header header name, without trailing : 94 * @param string $value header value, without trailing \n 95 */ 96 public function set_header( $header, $value ) { 97 $this->headers[ $header ] = $value; 98 } 99 100 /** 101 * Sets translation headers. 102 * 103 * @since 2.8.0 104 * 105 * @param array $headers Associative array of headers. 106 */ 107 public function set_headers( $headers ) { 108 foreach ( $headers as $header => $value ) { 109 $this->set_header( $header, $value ); 110 } 111 } 112 113 /** 114 * Returns a given translation header. 115 * 116 * @since 2.8.0 117 * 118 * @param string $header 119 * @return string|false Header if it exists, false otherwise. 120 */ 121 public function get_header( $header ) { 122 return isset( $this->headers[ $header ] ) ? $this->headers[ $header ] : false; 123 } 124 125 /** 126 * Returns a given translation entry. 127 * 128 * @since 2.8.0 129 * 130 * @param Translation_Entry $entry Translation entry. 131 * @return Translation_Entry|false Translation entry if it exists, false otherwise. 132 */ 133 public function translate_entry( &$entry ) { 134 $key = $entry->key(); 135 return isset( $this->entries[ $key ] ) ? $this->entries[ $key ] : false; 136 } 137 138 /** 139 * Translates a singular string. 140 * 141 * @since 2.8.0 142 * 143 * @param string $singular 144 * @param string $context 145 * @return string 146 */ 147 public function translate( $singular, $context = null ) { 148 $entry = new Translation_Entry( 149 array( 150 'singular' => $singular, 151 'context' => $context, 152 ) 153 ); 154 $translated = $this->translate_entry( $entry ); 155 return ( $translated && ! empty( $translated->translations ) ) ? $translated->translations[0] : $singular; 156 } 157 158 /** 159 * Given the number of items, returns the 0-based index of the plural form to use 160 * 161 * Here, in the base Translations class, the common logic for English is implemented: 162 * 0 if there is one element, 1 otherwise 163 * 164 * This function should be overridden by the subclasses. For example MO/PO can derive the logic 165 * from their headers. 166 * 167 * @since 2.8.0 168 * 169 * @param int $count Number of items. 170 * @return int Plural form to use. 171 */ 172 public function select_plural_form( $count ) { 173 return 1 === (int) $count ? 0 : 1; 174 } 175 176 /** 177 * Returns the plural forms count. 178 * 179 * @since 2.8.0 180 * 181 * @return int Plural forms count. 182 */ 183 public function get_plural_forms_count() { 184 return 2; 185 } 186 187 /** 188 * Translates a plural string. 189 * 190 * @since 2.8.0 191 * 192 * @param string $singular 193 * @param string $plural 194 * @param int $count 195 * @param string $context 196 * @return string 197 */ 198 public function translate_plural( $singular, $plural, $count, $context = null ) { 199 $entry = new Translation_Entry( 200 array( 201 'singular' => $singular, 202 'plural' => $plural, 203 'context' => $context, 204 ) 205 ); 206 $translated = $this->translate_entry( $entry ); 207 $index = $this->select_plural_form( $count ); 208 $total_plural_forms = $this->get_plural_forms_count(); 209 if ( $translated && 0 <= $index && $index < $total_plural_forms && 210 is_array( $translated->translations ) && 211 isset( $translated->translations[ $index ] ) ) { 212 return $translated->translations[ $index ]; 213 } else { 214 return 1 === (int) $count ? $singular : $plural; 215 } 216 } 217 218 /** 219 * Merges other translations into the current one. 220 * 221 * @since 2.8.0 222 * 223 * @param Translations $other Another Translation object, whose translations will be merged in this one (passed by reference). 224 */ 225 public function merge_with( &$other ) { 226 foreach ( $other->entries as $entry ) { 227 $this->entries[ $entry->key() ] = $entry; 228 } 229 } 230 231 /** 232 * Merges originals with existing entries. 233 * 234 * @since 2.8.0 235 * 236 * @param Translations $other 237 */ 238 public function merge_originals_with( &$other ) { 239 foreach ( $other->entries as $entry ) { 240 if ( ! isset( $this->entries[ $entry->key() ] ) ) { 241 $this->entries[ $entry->key() ] = $entry; 242 } else { 243 $this->entries[ $entry->key() ]->merge_with( $entry ); 244 } 245 } 246 } 247 } 248 249 /** 250 * Gettext_Translations class. 251 * 252 * @since 2.8.0 253 */ 254 class Gettext_Translations extends Translations { 255 256 /** 257 * Number of plural forms. 258 * 259 * @var int 260 * 261 * @since 2.8.0 262 */ 263 public $_nplurals; 264 265 /** 266 * Callback to retrieve the plural form. 267 * 268 * @var callable 269 * 270 * @since 2.8.0 271 */ 272 public $_gettext_select_plural_form; 273 274 /** 275 * The gettext implementation of select_plural_form. 276 * 277 * It lives in this class, because there are more than one descendant, which will use it and 278 * they can't share it effectively. 279 * 280 * @since 2.8.0 281 * 282 * @param int $count Plural forms count. 283 * @return int Plural form to use. 284 */ 285 public function gettext_select_plural_form( $count ) { 286 if ( ! isset( $this->_gettext_select_plural_form ) || is_null( $this->_gettext_select_plural_form ) ) { 287 list( $nplurals, $expression ) = $this->nplurals_and_expression_from_header( $this->get_header( 'Plural-Forms' ) ); 288 $this->_nplurals = $nplurals; 289 $this->_gettext_select_plural_form = $this->make_plural_form_function( $nplurals, $expression ); 290 } 291 return call_user_func( $this->_gettext_select_plural_form, $count ); 292 } 293 294 /** 295 * Returns the nplurals and plural forms expression from the Plural-Forms header. 296 * 297 * @since 2.8.0 298 * 299 * @param string $header 300 * @return array{0: int, 1: string} 301 */ 302 public function nplurals_and_expression_from_header( $header ) { 303 if ( preg_match( '/^\s*nplurals\s*=\s*(\d+)\s*;\s+plural\s*=\s*(.+)$/', $header, $matches ) ) { 304 $nplurals = (int) $matches[1]; 305 $expression = trim( $matches[2] ); 306 return array( $nplurals, $expression ); 307 } else { 308 return array( 2, 'n != 1' ); 309 } 310 } 311 312 /** 313 * Makes a function, which will return the right translation index, according to the 314 * plural forms header. 315 * 316 * @since 2.8.0 317 * 318 * @param int $nplurals 319 * @param string $expression 320 * @return callable 321 */ 322 public function make_plural_form_function( $nplurals, $expression ) { 323 try { 324 $handler = new Plural_Forms( rtrim( $expression, ';' ) ); 325 return array( $handler, 'get' ); 326 } catch ( Exception $e ) { 327 // Fall back to default plural-form function. 328 return $this->make_plural_form_function( 2, 'n != 1' ); 329 } 330 } 331 332 /** 333 * Adds parentheses to the inner parts of ternary operators in 334 * plural expressions, because PHP evaluates ternary operators from left to right 335 * 336 * @since 2.8.0 337 * @deprecated 6.5.0 Use the Plural_Forms class instead. 338 * 339 * @see Plural_Forms 340 * 341 * @param string $expression the expression without parentheses 342 * @return string the expression with parentheses added 343 */ 344 public function parenthesize_plural_exression( $expression ) { 345 $expression .= ';'; 346 $res = ''; 347 $depth = 0; 348 for ( $i = 0; $i < strlen( $expression ); ++$i ) { 349 $char = $expression[ $i ]; 350 switch ( $char ) { 351 case '?': 352 $res .= ' ? ('; 353 ++$depth; 354 break; 355 case ':': 356 $res .= ') : ('; 357 break; 358 case ';': 359 $res .= str_repeat( ')', $depth ) . ';'; 360 $depth = 0; 361 break; 362 default: 363 $res .= $char; 364 } 365 } 366 return rtrim( $res, ';' ); 367 } 368 369 /** 370 * Prepare translation headers. 371 * 372 * @since 2.8.0 373 * 374 * @param string $translation 375 * @return array<string, string> Translation headers 376 */ 377 public function make_headers( $translation ) { 378 $headers = array(); 379 // Sometimes \n's are used instead of real new lines. 380 $translation = str_replace( '\n', "\n", $translation ); 381 $lines = explode( "\n", $translation ); 382 foreach ( $lines as $line ) { 383 $parts = explode( ':', $line, 2 ); 384 if ( ! isset( $parts[1] ) ) { 385 continue; 386 } 387 $headers[ trim( $parts[0] ) ] = trim( $parts[1] ); 388 } 389 return $headers; 390 } 391 392 /** 393 * Sets translation headers. 394 * 395 * @since 2.8.0 396 * 397 * @param string $header 398 * @param string $value 399 */ 400 public function set_header( $header, $value ) { 401 parent::set_header( $header, $value ); 402 if ( 'Plural-Forms' === $header ) { 403 list( $nplurals, $expression ) = $this->nplurals_and_expression_from_header( $this->get_header( 'Plural-Forms' ) ); 404 $this->_nplurals = $nplurals; 405 $this->_gettext_select_plural_form = $this->make_plural_form_function( $nplurals, $expression ); 406 } 407 } 408 } 409 endif; 410 411 if ( ! class_exists( 'NOOP_Translations', false ) ) : 412 /** 413 * Provides the same interface as Translations, but doesn't do anything. 414 * 415 * @since 2.8.0 416 */ 417 #[AllowDynamicProperties] 418 class NOOP_Translations { 419 /** 420 * List of translation entries. 421 * 422 * @since 2.8.0 423 * 424 * @var Translation_Entry[] 425 */ 426 public $entries = array(); 427 428 /** 429 * List of translation headers. 430 * 431 * @since 2.8.0 432 * 433 * @var array<string, string> 434 */ 435 public $headers = array(); 436 437 public function add_entry( $entry ) { 438 return true; 439 } 440 441 /** 442 * Sets a translation header. 443 * 444 * @since 2.8.0 445 * 446 * @param string $header 447 * @param string $value 448 */ 449 public function set_header( $header, $value ) { 450 } 451 452 /** 453 * Sets translation headers. 454 * 455 * @since 2.8.0 456 * 457 * @param array $headers 458 */ 459 public function set_headers( $headers ) { 460 } 461 462 /** 463 * Returns a translation header. 464 * 465 * @since 2.8.0 466 * 467 * @param string $header 468 * @return false 469 */ 470 public function get_header( $header ) { 471 return false; 472 } 473 474 /** 475 * Returns a given translation entry. 476 * 477 * @since 2.8.0 478 * 479 * @param Translation_Entry $entry 480 * @return false 481 */ 482 public function translate_entry( &$entry ) { 483 return false; 484 } 485 486 /** 487 * Translates a singular string. 488 * 489 * @since 2.8.0 490 * 491 * @param string $singular 492 * @param string $context 493 */ 494 public function translate( $singular, $context = null ) { 495 return $singular; 496 } 497 498 /** 499 * Returns the plural form to use. 500 * 501 * @since 2.8.0 502 * 503 * @param int $count 504 * @return int 505 */ 506 public function select_plural_form( $count ) { 507 return 1 === (int) $count ? 0 : 1; 508 } 509 510 /** 511 * Returns the plural forms count. 512 * 513 * @since 2.8.0 514 * 515 * @return int 516 */ 517 public function get_plural_forms_count() { 518 return 2; 519 } 520 521 /** 522 * Translates a plural string. 523 * 524 * @since 2.8.0 525 * 526 * @param string $singular 527 * @param string $plural 528 * @param int $count 529 * @param string $context 530 * @return string 531 */ 532 public function translate_plural( $singular, $plural, $count, $context = null ) { 533 return 1 === (int) $count ? $singular : $plural; 534 } 535 536 /** 537 * Merges other translations into the current one. 538 * 539 * @since 2.8.0 540 * 541 * @param Translations $other 542 */ 543 public function merge_with( &$other ) { 544 } 545 } 546 endif;
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated : Thu Nov 21 08:20:01 2024 | Cross-referenced by PHPXref |