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