*/ public $headers = array(); /** * Adds an entry to the PO structure. * * @since 2.8.0 * * @param array|Translation_Entry $entry * @return bool True on success, false if the entry doesn't have a key. */ public function add_entry( $entry ) { if ( is_array( $entry ) ) { $entry = new Translation_Entry( $entry ); } $key = $entry->key(); if ( false === $key ) { return false; } $this->entries[ $key ] = &$entry; return true; } /** * Adds or merges an entry to the PO structure. * * @since 2.8.0 * * @param array|Translation_Entry $entry * @return bool True on success, false if the entry doesn't have a key. */ public function add_entry_or_merge( $entry ) { if ( is_array( $entry ) ) { $entry = new Translation_Entry( $entry ); } $key = $entry->key(); if ( false === $key ) { return false; } if ( isset( $this->entries[ $key ] ) ) { $this->entries[ $key ]->merge_with( $entry ); } else { $this->entries[ $key ] = &$entry; } return true; } /** * Sets $header PO header to $value * * If the header already exists, it will be overwritten * * TODO: this should be out of this class, it is gettext specific * * @since 2.8.0 * * @param string $header header name, without trailing : * @param string $value header value, without trailing \n */ public function set_header( $header, $value ) { $this->headers[ $header ] = $value; } /** * Sets translation headers. * * @since 2.8.0 * * @param array $headers Associative array of headers. */ public function set_headers( $headers ) { foreach ( $headers as $header => $value ) { $this->set_header( $header, $value ); } } /** * Returns a given translation header. * * @since 2.8.0 * * @param string $header * @return string|false Header if it exists, false otherwise. */ public function get_header( $header ) { return isset( $this->headers[ $header ] ) ? $this->headers[ $header ] : false; } /** * Returns a given translation entry. * * @since 2.8.0 * * @param Translation_Entry $entry Translation entry. * @return Translation_Entry|false Translation entry if it exists, false otherwise. */ public function translate_entry( &$entry ) { $key = $entry->key(); return isset( $this->entries[ $key ] ) ? $this->entries[ $key ] : false; } /** * Translates a singular string. * * @since 2.8.0 * * @param string $singular * @param string $context * @return string */ public function translate( $singular, $context = null ) { $entry = new Translation_Entry( array( 'singular' => $singular, 'context' => $context, ) ); $translated = $this->translate_entry( $entry ); return ( $translated && ! empty( $translated->translations ) ) ? $translated->translations[0] : $singular; } /** * Given the number of items, returns the 0-based index of the plural form to use * * Here, in the base Translations class, the common logic for English is implemented: * 0 if there is one element, 1 otherwise * * This function should be overridden by the subclasses. For example MO/PO can derive the logic * from their headers. * * @since 2.8.0 * * @param int $count Number of items. * @return int Plural form to use. */ public function select_plural_form( $count ) { return 1 === (int) $count ? 0 : 1; } /** * Returns the plural forms count. * * @since 2.8.0 * * @return int Plural forms count. */ public function get_plural_forms_count() { return 2; } /** * Translates a plural string. * * @since 2.8.0 * * @param string $singular * @param string $plural * @param int $count * @param string $context * @return string */ public function translate_plural( $singular, $plural, $count, $context = null ) { $entry = new Translation_Entry( array( 'singular' => $singular, 'plural' => $plural, 'context' => $context, ) ); $translated = $this->translate_entry( $entry ); $index = $this->select_plural_form( $count ); $total_plural_forms = $this->get_plural_forms_count(); if ( $translated && 0 <= $index && $index < $total_plural_forms && is_array( $translated->translations ) && isset( $translated->translations[ $index ] ) ) { return $translated->translations[ $index ]; } else { return 1 === (int) $count ? $singular : $plural; } } /** * Merges other translations into the current one. * * @since 2.8.0 * * @param Translations $other Another Translation object, whose translations will be merged in this one (passed by reference). */ public function merge_with( &$other ) { foreach ( $other->entries as $entry ) { $this->entries[ $entry->key() ] = $entry; } } /** * Merges originals with existing entries. * * @since 2.8.0 * * @param Translations $other */ public function merge_originals_with( &$other ) { foreach ( $other->entries as $entry ) { if ( ! isset( $this->entries[ $entry->key() ] ) ) { $this->entries[ $entry->key() ] = $entry; } else { $this->entries[ $entry->key() ]->merge_with( $entry ); } } } } /** * Gettext_Translations class. * * @since 2.8.0 */ class Gettext_Translations extends Translations { /** * Number of plural forms. * * @var int * * @since 2.8.0 */ public $_nplurals; /** * Callback to retrieve the plural form. * * @var callable * * @since 2.8.0 */ public $_gettext_select_plural_form; /** * The gettext implementation of select_plural_form. * * It lives in this class, because there are more than one descendant, which will use it and * they can't share it effectively. * * @since 2.8.0 * * @param int $count Plural forms count. * @return int Plural form to use. */ public function gettext_select_plural_form( $count ) { if ( ! isset( $this->_gettext_select_plural_form ) || is_null( $this->_gettext_select_plural_form ) ) { list( $nplurals, $expression ) = $this->nplurals_and_expression_from_header( $this->get_header( 'Plural-Forms' ) ); $this->_nplurals = $nplurals; $this->_gettext_select_plural_form = $this->make_plural_form_function( $nplurals, $expression ); } return call_user_func( $this->_gettext_select_plural_form, $count ); } /** * Returns the nplurals and plural forms expression from the Plural-Forms header. * * @since 2.8.0 * * @param string $header * @return array{0: int, 1: string} */ public function nplurals_and_expression_from_header( $header ) { if ( preg_match( '/^\s*nplurals\s*=\s*(\d+)\s*;\s+plural\s*=\s*(.+)$/', $header, $matches ) ) { $nplurals = (int) $matches[1]; $expression = trim( $matches[2] ); return array( $nplurals, $expression ); } else { return array( 2, 'n != 1' ); } } /** * Makes a function, which will return the right translation index, according to the * plural forms header. * * @since 2.8.0 * * @param int $nplurals * @param string $expression * @return callable */ public function make_plural_form_function( $nplurals, $expression ) { try { $handler = new Plural_Forms( rtrim( $expression, ';' ) ); return array( $handler, 'get' ); } catch ( Exception $e ) { // Fall back to default plural-form function. return $this->make_plural_form_function( 2, 'n != 1' ); } } /** * Adds parentheses to the inner parts of ternary operators in * plural expressions, because PHP evaluates ternary operators from left to right * * @since 2.8.0 * @deprecated 6.5.0 Use the Plural_Forms class instead. * * @see Plural_Forms * * @param string $expression the expression without parentheses * @return string the expression with parentheses added */ public function parenthesize_plural_exression( $expression ) { $expression .= ';'; $res = ''; $depth = 0; for ( $i = 0; $i < strlen( $expression ); ++$i ) { $char = $expression[ $i ]; switch ( $char ) { case '?': $res .= ' ? ('; ++$depth; break; case ':': $res .= ') : ('; break; case ';': $res .= str_repeat( ')', $depth ) . ';'; $depth = 0; break; default: $res .= $char; } } return rtrim( $res, ';' ); } /** * Prepare translation headers. * * @since 2.8.0 * * @param string $translation * @return array Translation headers */ public function make_headers( $translation ) { $headers = array(); // Sometimes \n's are used instead of real new lines. $translation = str_replace( '\n', "\n", $translation ); $lines = explode( "\n", $translation ); foreach ( $lines as $line ) { $parts = explode( ':', $line, 2 ); if ( ! isset( $parts[1] ) ) { continue; } $headers[ trim( $parts[0] ) ] = trim( $parts[1] ); } return $headers; } /** * Sets translation headers. * * @since 2.8.0 * * @param string $header * @param string $value */ public function set_header( $header, $value ) { parent::set_header( $header, $value ); if ( 'Plural-Forms' === $header ) { list( $nplurals, $expression ) = $this->nplurals_and_expression_from_header( $this->get_header( 'Plural-Forms' ) ); $this->_nplurals = $nplurals; $this->_gettext_select_plural_form = $this->make_plural_form_function( $nplurals, $expression ); } } } endif; if ( ! class_exists( 'NOOP_Translations', false ) ) : /** * Provides the same interface as Translations, but doesn't do anything. * * @since 2.8.0 */ #[AllowDynamicProperties] class NOOP_Translations { /** * List of translation entries. * * @since 2.8.0 * * @var Translation_Entry[] */ public $entries = array(); /** * List of translation headers. * * @since 2.8.0 * * @var array */ public $headers = array(); public function add_entry( $entry ) { return true; } /** * Sets a translation header. * * @since 2.8.0 * * @param string $header * @param string $value */ public function set_header( $header, $value ) { } /** * Sets translation headers. * * @since 2.8.0 * * @param array $headers */ public function set_headers( $headers ) { } /** * Returns a translation header. * * @since 2.8.0 * * @param string $header * @return false */ public function get_header( $header ) { return false; } /** * Returns a given translation entry. * * @since 2.8.0 * * @param Translation_Entry $entry * @return false */ public function translate_entry( &$entry ) { return false; } /** * Translates a singular string. * * @since 2.8.0 * * @param string $singular * @param string $context */ public function translate( $singular, $context = null ) { return $singular; } /** * Returns the plural form to use. * * @since 2.8.0 * * @param int $count * @return int */ public function select_plural_form( $count ) { return 1 === (int) $count ? 0 : 1; } /** * Returns the plural forms count. * * @since 2.8.0 * * @return int */ public function get_plural_forms_count() { return 2; } /** * Translates a plural string. * * @since 2.8.0 * * @param string $singular * @param string $plural * @param int $count * @param string $context * @return string */ public function translate_plural( $singular, $plural, $count, $context = null ) { return 1 === (int) $count ? $singular : $plural; } /** * Merges other translations into the current one. * * @since 2.8.0 * * @param Translations $other */ public function merge_with( &$other ) { } } endif;