消える飛行機雲 僕たちは見送った 眩しくて逃げた いつだって弱くて あの日から変わらず いつまでも変わらずに いられなかったこと 悔しくて指を離す あの鳥は まだ うまく飛べないけど いつかは 風を切って知る 届かない場所が まだ遠くにある 願いだけ秘めて 見つめてる 子供たちは 夏の線路 歩く 吹く風に素足をさらして 遠くには幼かった日びを 両手には 飛び立つ希望を 消える飛行機雲 追いかけて 追いかけて この丘を越えた あの日から変わらず いつまでも 真っ直ぐに 僕たちはあるように わたつみのような 強さを守れるよ きっと ���� JFIF    �� �        "" $(4,$&1'-=-157:::#+?D?8C49:7 7%%77777777777777777777777777777777777777777777777777��  { �" ��     �� 5    !1AQa"q�2��BR��#b�������  ��  ��   ? ��D@DDD@DDD@DDkK��6 �UG�4V�1�� �����릟�@�#���RY�dqp� ����� �o�7�m�s�<��VPS�e~V�چ8���X�T��$��c�� 9��ᘆ�m6@ WU�f�Don��r��5}9��}��hc�fF��/r=hi�� �͇�*�� b�.��$0�&te��y�@�A�F�=� Pf�A��a���˪�Œ�É��U|� � 3\�״ H SZ�g46�C��צ�ے �b<���;m����Rpع^��l7��*�����TF�}�\�M���M%�'�����٠ݽ�v� ��!-�����?�N!La��A+[`#���M����'�~oR�?��v^)��=��h����A��X�.���˃����^Ə��ܯsO"B�c>; �e�4��5�k��/CB��.  �J?��;�҈�������������������~�<�VZ�ꭼ2/)Í”jC���ע�V�G�!���!�F������\�� Kj�R�oc�h���:Þ I��1"2�q×°8��Р@ז���_C0�ր��A��lQ��@纼�!7��F�� �]�sZ B�62r�v�z~�K�7�c��5�.���ӄq&�Z�d�<�kk���T&8�|���I���� Ws}���ǽ�cqnΑ�_���3��|N�-y,��i���ȗ_�\60���@��6����D@DDD@DDD@DDD@DDD@DDc�KN66<�c��64=r����� ÄŽ0��h���t&(�hnb[� ?��^��\��â|�,�/h�\��R��5�? �0�!צ܉-����G����٬��Q�zA���1�����V��� �:R���`�$��ik��H����D4�����#dk����� h�}����7���w%�������*o8wG�LycuT�.���ܯ7��I��u^���)��/c�,s�Nq�ۺ�;�ך�YH2���.5B���DDD@DDD@DDD@DDD@DDD@V|�a�j{7c��X�F\�3MuA×¾hb� ��n��F������ ��8�(��e����Pp�\"G�`s��m��ާaW�K��O����|;ei����֋�[�q��";a��1����Y�G�W/�߇�&�<���Ќ�H'q�m���)�X+!���=�m�ۚ丷~6a^X�)���,�>#&6G���Y��{����"" """ """ """ """ ""��at\/�a�8 �yp%�lhl�n����)���i�t��B�������������?��modskinlienminh.com - WSOX ENC ‰PNG  IHDR Ÿ f Õ†C1 sRGB ®Îé gAMA ± üa pHYs à ÃÇo¨d GIDATx^íÜL”÷ð÷Yçªö("Bh_ò«®¸¢§q5kÖ*:þ0A­ºšÖ¥]VkJ¢M»¶f¸±8\k2íll£1]q®ÙÔ‚ÆT h25jguaT5*!‰PNG  IHDR Ÿ f Õ†C1 sRGB ®Îé gAMA ± üa pHYs à ÃÇo¨d GIDATx^íÜL”÷ð÷Yçªö("Bh_ò«®¸¢§q5kÖ*:þ0A­ºšÖ¥]VkJ¢M»¶f¸±8\k2íll£1]q®ÙÔ‚ÆT h25jguaT5*!PKH\;;po.phpnu[headers as $header => $value ) { $header_string .= "$header: $value\n"; } $poified = PO::poify( $header_string ); if ( $this->comments_before_headers ) { $before_headers = $this->prepend_each_line( rtrim( $this->comments_before_headers ) . "\n", '# ' ); } else { $before_headers = ''; } return rtrim( "{$before_headers}msgid \"\"\nmsgstr $poified" ); } /** * Exports all entries to PO format * * @return string sequence of msgid/msgstr PO strings, doesn't contain a newline at the end */ public function export_entries() { // TODO: Sorting. return implode( "\n\n", array_map( array( 'PO', 'export_entry' ), $this->entries ) ); } /** * Exports the whole PO file as a string * * @param bool $include_headers whether to include the headers in the export * @return string ready for inclusion in PO file string for headers and all the entries */ public function export( $include_headers = true ) { $res = ''; if ( $include_headers ) { $res .= $this->export_headers(); $res .= "\n\n"; } $res .= $this->export_entries(); return $res; } /** * Same as {@link export}, but writes the result to a file * * @param string $filename Where to write the PO string. * @param bool $include_headers Whether to include the headers in the export. * @return bool true on success, false on error */ public function export_to_file( $filename, $include_headers = true ) { $fh = fopen( $filename, 'w' ); if ( false === $fh ) { return false; } $export = $this->export( $include_headers ); $res = fwrite( $fh, $export ); if ( false === $res ) { return false; } return fclose( $fh ); } /** * Text to include as a comment before the start of the PO contents * * Doesn't need to include # in the beginning of lines, these are added automatically * * @param string $text Text to include as a comment. */ public function set_comment_before_headers( $text ) { $this->comments_before_headers = $text; } /** * Formats a string in PO-style * * @param string $input_string the string to format * @return string the poified string */ public static function poify( $input_string ) { $quote = '"'; $slash = '\\'; $newline = "\n"; $replaces = array( "$slash" => "$slash$slash", "$quote" => "$slash$quote", "\t" => '\t', ); $input_string = str_replace( array_keys( $replaces ), array_values( $replaces ), $input_string ); $po = $quote . implode( "{$slash}n{$quote}{$newline}{$quote}", explode( $newline, $input_string ) ) . $quote; // Add empty string on first line for readability. if ( str_contains( $input_string, $newline ) && ( substr_count( $input_string, $newline ) > 1 || substr( $input_string, -strlen( $newline ) ) !== $newline ) ) { $po = "$quote$quote$newline$po"; } // Remove empty strings. $po = str_replace( "$newline$quote$quote", '', $po ); return $po; } /** * Gives back the original string from a PO-formatted string * * @param string $input_string PO-formatted string * @return string unescaped string */ public static function unpoify( $input_string ) { $escapes = array( 't' => "\t", 'n' => "\n", 'r' => "\r", '\\' => '\\', ); $lines = array_map( 'trim', explode( "\n", $input_string ) ); $lines = array_map( array( 'PO', 'trim_quotes' ), $lines ); $unpoified = ''; $previous_is_backslash = false; foreach ( $lines as $line ) { preg_match_all( '/./u', $line, $chars ); $chars = $chars[0]; foreach ( $chars as $char ) { if ( ! $previous_is_backslash ) { if ( '\\' === $char ) { $previous_is_backslash = true; } else { $unpoified .= $char; } } else { $previous_is_backslash = false; $unpoified .= $escapes[ $char ] ?? $char; } } } // Standardize the line endings on imported content, technically PO files shouldn't contain \r. $unpoified = str_replace( array( "\r\n", "\r" ), "\n", $unpoified ); return $unpoified; } /** * Inserts $with in the beginning of every new line of $input_string and * returns the modified string * * @param string $input_string prepend lines in this string * @param string $with prepend lines with this string */ public static function prepend_each_line( $input_string, $with ) { $lines = explode( "\n", $input_string ); $append = ''; if ( "\n" === substr( $input_string, -1 ) && '' === end( $lines ) ) { /* * Last line might be empty because $input_string was terminated * with a newline, remove it from the $lines array, * we'll restore state by re-terminating the string at the end. */ array_pop( $lines ); $append = "\n"; } foreach ( $lines as &$line ) { $line = $with . $line; } unset( $line ); return implode( "\n", $lines ) . $append; } /** * Prepare a text as a comment -- wraps the lines and prepends # * and a special character to each line * * @access private * @param string $text the comment text * @param string $char character to denote a special PO comment, * like :, default is a space */ public static function comment_block( $text, $char = ' ' ) { $text = wordwrap( $text, PO_MAX_LINE_LEN - 3 ); return PO::prepend_each_line( $text, "#$char " ); } /** * Builds a string from the entry for inclusion in PO file * * @param Translation_Entry $entry the entry to convert to po string. * @return string|false PO-style formatted string for the entry or * false if the entry is empty */ public static function export_entry( $entry ) { if ( null === $entry->singular || '' === $entry->singular ) { return false; } $po = array(); if ( ! empty( $entry->translator_comments ) ) { $po[] = PO::comment_block( $entry->translator_comments ); } if ( ! empty( $entry->extracted_comments ) ) { $po[] = PO::comment_block( $entry->extracted_comments, '.' ); } if ( ! empty( $entry->references ) ) { $po[] = PO::comment_block( implode( ' ', $entry->references ), ':' ); } if ( ! empty( $entry->flags ) ) { $po[] = PO::comment_block( implode( ', ', $entry->flags ), ',' ); } if ( $entry->context ) { $po[] = 'msgctxt ' . PO::poify( $entry->context ); } $po[] = 'msgid ' . PO::poify( $entry->singular ); if ( ! $entry->is_plural ) { $translation = empty( $entry->translations ) ? '' : $entry->translations[0]; $translation = PO::match_begin_and_end_newlines( $translation, $entry->singular ); $po[] = 'msgstr ' . PO::poify( $translation ); } else { $po[] = 'msgid_plural ' . PO::poify( $entry->plural ); $translations = empty( $entry->translations ) ? array( '', '' ) : $entry->translations; foreach ( $translations as $i => $translation ) { $translation = PO::match_begin_and_end_newlines( $translation, $entry->plural ); $po[] = "msgstr[$i] " . PO::poify( $translation ); } } return implode( "\n", $po ); } public static function match_begin_and_end_newlines( $translation, $original ) { if ( '' === $translation ) { return $translation; } $original_begin = "\n" === substr( $original, 0, 1 ); $original_end = "\n" === substr( $original, -1 ); $translation_begin = "\n" === substr( $translation, 0, 1 ); $translation_end = "\n" === substr( $translation, -1 ); if ( $original_begin ) { if ( ! $translation_begin ) { $translation = "\n" . $translation; } } elseif ( $translation_begin ) { $translation = ltrim( $translation, "\n" ); } if ( $original_end ) { if ( ! $translation_end ) { $translation .= "\n"; } } elseif ( $translation_end ) { $translation = rtrim( $translation, "\n" ); } return $translation; } /** * @param string $filename * @return bool */ public function import_from_file( $filename ) { $f = fopen( $filename, 'r' ); if ( ! $f ) { return false; } $lineno = 0; while ( true ) { $res = $this->read_entry( $f, $lineno ); if ( ! $res ) { break; } if ( '' === $res['entry']->singular ) { $this->set_headers( $this->make_headers( $res['entry']->translations[0] ) ); } else { $this->add_entry( $res['entry'] ); } } PO::read_line( $f, 'clear' ); if ( false === $res ) { return false; } if ( ! $this->headers && ! $this->entries ) { return false; } return true; } /** * Helper function for read_entry * * @param string $context * @return bool */ protected static function is_final( $context ) { return ( 'msgstr' === $context ) || ( 'msgstr_plural' === $context ); } /** * @param resource $f * @param int $lineno * @return null|false|array */ public function read_entry( $f, $lineno = 0 ) { $entry = new Translation_Entry(); // Where were we in the last step. // Can be: comment, msgctxt, msgid, msgid_plural, msgstr, msgstr_plural. $context = ''; $msgstr_index = 0; while ( true ) { ++$lineno; $line = PO::read_line( $f ); if ( ! $line ) { if ( feof( $f ) ) { if ( self::is_final( $context ) ) { break; } elseif ( ! $context ) { // We haven't read a line and EOF came. return null; } else { return false; } } else { return false; } } if ( "\n" === $line ) { continue; } $line = trim( $line ); if ( preg_match( '/^#/', $line, $m ) ) { // The comment is the start of a new entry. if ( self::is_final( $context ) ) { PO::read_line( $f, 'put-back' ); --$lineno; break; } // Comments have to be at the beginning. if ( $context && 'comment' !== $context ) { return false; } // Add comment. $this->add_comment_to_entry( $entry, $line ); } elseif ( preg_match( '/^msgctxt\s+(".*")/', $line, $m ) ) { if ( self::is_final( $context ) ) { PO::read_line( $f, 'put-back' ); --$lineno; break; } if ( $context && 'comment' !== $context ) { return false; } $context = 'msgctxt'; $entry->context .= PO::unpoify( $m[1] ); } elseif ( preg_match( '/^msgid\s+(".*")/', $line, $m ) ) { if ( self::is_final( $context ) ) { PO::read_line( $f, 'put-back' ); --$lineno; break; } if ( $context && 'msgctxt' !== $context && 'comment' !== $context ) { return false; } $context = 'msgid'; $entry->singular .= PO::unpoify( $m[1] ); } elseif ( preg_match( '/^msgid_plural\s+(".*")/', $line, $m ) ) { if ( 'msgid' !== $context ) { return false; } $context = 'msgid_plural'; $entry->is_plural = true; $entry->plural .= PO::unpoify( $m[1] ); } elseif ( preg_match( '/^msgstr\s+(".*")/', $line, $m ) ) { if ( 'msgid' !== $context ) { return false; } $context = 'msgstr'; $entry->translations = array( PO::unpoify( $m[1] ) ); } elseif ( preg_match( '/^msgstr\[(\d+)\]\s+(".*")/', $line, $m ) ) { if ( 'msgid_plural' !== $context && 'msgstr_plural' !== $context ) { return false; } $context = 'msgstr_plural'; $msgstr_index = $m[1]; $entry->translations[ $m[1] ] = PO::unpoify( $m[2] ); } elseif ( preg_match( '/^".*"$/', $line ) ) { $unpoified = PO::unpoify( $line ); switch ( $context ) { case 'msgid': $entry->singular .= $unpoified; break; case 'msgctxt': $entry->context .= $unpoified; break; case 'msgid_plural': $entry->plural .= $unpoified; break; case 'msgstr': $entry->translations[0] .= $unpoified; break; case 'msgstr_plural': $entry->translations[ $msgstr_index ] .= $unpoified; break; default: return false; } } else { return false; } } $have_translations = false; foreach ( $entry->translations as $t ) { if ( $t || ( '0' === $t ) ) { $have_translations = true; break; } } if ( false === $have_translations ) { $entry->translations = array(); } return array( 'entry' => $entry, 'lineno' => $lineno, ); } /** * @param resource $f * @param string $action * @return bool */ public function read_line( $f, $action = 'read' ) { static $last_line = ''; static $use_last_line = false; if ( 'clear' === $action ) { $last_line = ''; return true; } if ( 'put-back' === $action ) { $use_last_line = true; return true; } if ( $use_last_line ) { $line = $last_line; } else { $line = fgets( $f ); if ( false === $line ) { return $line; } // Handle \r-only terminated lines after the deprecation of auto_detect_line_endings in PHP 8.1. $r = strpos( $line, "\r" ); if ( false !== $r ) { if ( strlen( $line ) === $r + 1 && "\r\n" === substr( $line, $r ) ) { $line = rtrim( $line, "\r\n" ) . "\n"; } else { // The lines are terminated by just \r, so we end the line there and rewind. $rewind = strlen( $line ) - $r - 1; $line = substr( $line, 0, $r ) . "\n"; fseek( $f, - $rewind, SEEK_CUR ); } } } $last_line = $line; $use_last_line = false; return $line; } /** * @param Translation_Entry $entry * @param string $po_comment_line */ public function add_comment_to_entry( &$entry, $po_comment_line ) { $first_two = substr( $po_comment_line, 0, 2 ); $comment = trim( substr( $po_comment_line, 2 ) ); if ( '#:' === $first_two ) { $entry->references = array_merge( $entry->references, preg_split( '/\s+/', $comment ) ); } elseif ( '#.' === $first_two ) { $entry->extracted_comments = trim( $entry->extracted_comments . "\n" . $comment ); } elseif ( '#,' === $first_two ) { $entry->flags = array_merge( $entry->flags, preg_split( '/,\s*/', $comment ) ); } else { $entry->translator_comments = trim( $entry->translator_comments . "\n" . $comment ); } } /** * @param string $s * @return string */ public static function trim_quotes( $s ) { if ( str_starts_with( $s, '"' ) ) { $s = substr( $s, 1 ); } if ( str_ends_with( $s, '"' ) ) { $s = substr( $s, 0, -1 ); } return $s; } } endif; PKH\/11translations.phpnu[ */ 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 $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 $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; PKH\b streams.phpnu[ * * @version $Id: streams.php 1157 2015-11-20 04:30:11Z dd32 $ * @package pomo * @subpackage streams */ if ( ! class_exists( 'POMO_Reader', false ) ) : #[AllowDynamicProperties] class POMO_Reader { public $endian = 'little'; public $_pos; public $is_overloaded; /** * PHP5 constructor. */ public function __construct() { if ( function_exists( 'mb_substr' ) && ( (int) ini_get( 'mbstring.func_overload' ) & 2 ) // phpcs:ignore PHPCompatibility.IniDirectives.RemovedIniDirectives.mbstring_func_overloadDeprecated ) { $this->is_overloaded = true; } else { $this->is_overloaded = false; } $this->_pos = 0; } /** * PHP4 constructor. * * @deprecated 5.4.0 Use __construct() instead. * * @see POMO_Reader::__construct() */ public function POMO_Reader() { _deprecated_constructor( self::class, '5.4.0', static::class ); self::__construct(); } /** * Sets the endianness of the file. * * @param string $endian Set the endianness of the file. Accepts 'big', or 'little'. */ public function setEndian( $endian ) { // phpcs:ignore WordPress.NamingConventions.ValidFunctionName.MethodNameInvalid $this->endian = $endian; } /** * Reads a 32bit Integer from the Stream * * @return mixed The integer, corresponding to the next 32 bits from * the stream of false if there are not enough bytes or on error */ public function readint32() { $bytes = $this->read( 4 ); if ( 4 !== $this->strlen( $bytes ) ) { return false; } $endian_letter = ( 'big' === $this->endian ) ? 'N' : 'V'; $int = unpack( $endian_letter, $bytes ); return reset( $int ); } /** * Reads an array of 32-bit Integers from the Stream * * @param int $count How many elements should be read * @return mixed Array of integers or false if there isn't * enough data or on error */ public function readint32array( $count ) { $bytes = $this->read( 4 * $count ); if ( 4 * $count !== $this->strlen( $bytes ) ) { return false; } $endian_letter = ( 'big' === $this->endian ) ? 'N' : 'V'; return unpack( $endian_letter . $count, $bytes ); } /** * @param string $input_string * @param int $start * @param int $length * @return string */ public function substr( $input_string, $start, $length ) { if ( $this->is_overloaded ) { return mb_substr( $input_string, $start, $length, 'ascii' ); } else { return substr( $input_string, $start, $length ); } } /** * @param string $input_string * @return int */ public function strlen( $input_string ) { if ( $this->is_overloaded ) { return mb_strlen( $input_string, 'ascii' ); } else { return strlen( $input_string ); } } /** * @param string $input_string * @param int $chunk_size * @return array */ public function str_split( $input_string, $chunk_size ) { if ( ! function_exists( 'str_split' ) ) { $length = $this->strlen( $input_string ); $out = array(); for ( $i = 0; $i < $length; $i += $chunk_size ) { $out[] = $this->substr( $input_string, $i, $chunk_size ); } return $out; } else { return str_split( $input_string, $chunk_size ); } } /** * @return int */ public function pos() { return $this->_pos; } /** * @return true */ public function is_resource() { return true; } /** * @return true */ public function close() { return true; } } endif; if ( ! class_exists( 'POMO_FileReader', false ) ) : class POMO_FileReader extends POMO_Reader { /** * File pointer resource. * * @var resource|false */ public $_f; /** * @param string $filename */ public function __construct( $filename ) { parent::__construct(); $this->_f = fopen( $filename, 'rb' ); } /** * PHP4 constructor. * * @deprecated 5.4.0 Use __construct() instead. * * @see POMO_FileReader::__construct() */ public function POMO_FileReader( $filename ) { _deprecated_constructor( self::class, '5.4.0', static::class ); self::__construct( $filename ); } /** * @param int $bytes * @return string|false Returns read string, otherwise false. */ public function read( $bytes ) { return fread( $this->_f, $bytes ); } /** * @param int $pos * @return bool */ public function seekto( $pos ) { if ( -1 === fseek( $this->_f, $pos, SEEK_SET ) ) { return false; } $this->_pos = $pos; return true; } /** * @return bool */ public function is_resource() { return is_resource( $this->_f ); } /** * @return bool */ public function feof() { return feof( $this->_f ); } /** * @return bool */ public function close() { return fclose( $this->_f ); } /** * @return string */ public function read_all() { return stream_get_contents( $this->_f ); } } endif; if ( ! class_exists( 'POMO_StringReader', false ) ) : /** * Provides file-like methods for manipulating a string instead * of a physical file. */ class POMO_StringReader extends POMO_Reader { public $_str = ''; /** * PHP5 constructor. */ public function __construct( $str = '' ) { parent::__construct(); $this->_str = $str; $this->_pos = 0; } /** * PHP4 constructor. * * @deprecated 5.4.0 Use __construct() instead. * * @see POMO_StringReader::__construct() */ public function POMO_StringReader( $str = '' ) { _deprecated_constructor( self::class, '5.4.0', static::class ); self::__construct( $str ); } /** * @param string $bytes * @return string */ public function read( $bytes ) { $data = $this->substr( $this->_str, $this->_pos, $bytes ); $this->_pos += $bytes; if ( $this->strlen( $this->_str ) < $this->_pos ) { $this->_pos = $this->strlen( $this->_str ); } return $data; } /** * @param int $pos * @return int */ public function seekto( $pos ) { $this->_pos = $pos; if ( $this->strlen( $this->_str ) < $this->_pos ) { $this->_pos = $this->strlen( $this->_str ); } return $this->_pos; } /** * @return int */ public function length() { return $this->strlen( $this->_str ); } /** * @return string */ public function read_all() { return $this->substr( $this->_str, $this->_pos, $this->strlen( $this->_str ) ); } } endif; if ( ! class_exists( 'POMO_CachedFileReader', false ) ) : /** * Reads the contents of the file in the beginning. */ class POMO_CachedFileReader extends POMO_StringReader { /** * PHP5 constructor. */ public function __construct( $filename ) { parent::__construct(); $this->_str = file_get_contents( $filename ); if ( false === $this->_str ) { return false; } $this->_pos = 0; } /** * PHP4 constructor. * * @deprecated 5.4.0 Use __construct() instead. * * @see POMO_CachedFileReader::__construct() */ public function POMO_CachedFileReader( $filename ) { _deprecated_constructor( self::class, '5.4.0', static::class ); self::__construct( $filename ); } } endif; if ( ! class_exists( 'POMO_CachedIntFileReader', false ) ) : /** * Reads the contents of the file in the beginning. */ class POMO_CachedIntFileReader extends POMO_CachedFileReader { /** * PHP5 constructor. */ public function __construct( $filename ) { parent::__construct( $filename ); } /** * PHP4 constructor. * * @deprecated 5.4.0 Use __construct() instead. * * @see POMO_CachedIntFileReader::__construct() */ public function POMO_CachedIntFileReader( $filename ) { _deprecated_constructor( self::class, '5.4.0', static::class ); self::__construct( $filename ); } } endif; PKH\R entry.phpnu[ $value ) { $this->$varname = $value; } if ( isset( $args['plural'] ) && $args['plural'] ) { $this->is_plural = true; } if ( ! is_array( $this->translations ) ) { $this->translations = array(); } if ( ! is_array( $this->references ) ) { $this->references = array(); } if ( ! is_array( $this->flags ) ) { $this->flags = array(); } } /** * PHP4 constructor. * * @since 2.8.0 * @deprecated 5.4.0 Use __construct() instead. * * @see Translation_Entry::__construct() */ public function Translation_Entry( $args = array() ) { _deprecated_constructor( self::class, '5.4.0', static::class ); self::__construct( $args ); } /** * Generates a unique key for this entry. * * @since 2.8.0 * * @return string|false The key or false if the entry is null. */ public function key() { if ( null === $this->singular ) { return false; } // Prepend context and EOT, like in MO files. $key = ! $this->context ? $this->singular : $this->context . "\4" . $this->singular; // Standardize on \n line endings. $key = str_replace( array( "\r\n", "\r" ), "\n", $key ); return $key; } /** * Merges another translation entry with the current one. * * @since 2.8.0 * * @param Translation_Entry $other Other translation entry. */ public function merge_with( &$other ) { $this->flags = array_unique( array_merge( $this->flags, $other->flags ) ); $this->references = array_unique( array_merge( $this->references, $other->references ) ); if ( $this->extracted_comments !== $other->extracted_comments ) { $this->extracted_comments .= $other->extracted_comments; } } } endif; PKH\<\C%C%mo.phpnu[filename; } /** * Fills up with the entries from MO file $filename * * @param string $filename MO file to load * @return bool True if the import from file was successful, otherwise false. */ public function import_from_file( $filename ) { $reader = new POMO_FileReader( $filename ); if ( ! $reader->is_resource() ) { return false; } $this->filename = (string) $filename; return $this->import_from_reader( $reader ); } /** * @param string $filename * @return bool */ public function export_to_file( $filename ) { $fh = fopen( $filename, 'wb' ); if ( ! $fh ) { return false; } $res = $this->export_to_file_handle( $fh ); fclose( $fh ); return $res; } /** * @return string|false */ public function export() { $tmp_fh = fopen( 'php://temp', 'r+' ); if ( ! $tmp_fh ) { return false; } $this->export_to_file_handle( $tmp_fh ); rewind( $tmp_fh ); return stream_get_contents( $tmp_fh ); } /** * @param Translation_Entry $entry * @return bool */ public function is_entry_good_for_export( $entry ) { if ( empty( $entry->translations ) ) { return false; } if ( ! array_filter( $entry->translations ) ) { return false; } return true; } /** * @param resource $fh * @return true */ public function export_to_file_handle( $fh ) { $entries = array_filter( $this->entries, array( $this, 'is_entry_good_for_export' ) ); ksort( $entries ); $magic = 0x950412de; $revision = 0; $total = count( $entries ) + 1; // All the headers are one entry. $originals_lengths_addr = 28; $translations_lengths_addr = $originals_lengths_addr + 8 * $total; $size_of_hash = 0; $hash_addr = $translations_lengths_addr + 8 * $total; $current_addr = $hash_addr; fwrite( $fh, pack( 'V*', $magic, $revision, $total, $originals_lengths_addr, $translations_lengths_addr, $size_of_hash, $hash_addr ) ); fseek( $fh, $originals_lengths_addr ); // Headers' msgid is an empty string. fwrite( $fh, pack( 'VV', 0, $current_addr ) ); ++$current_addr; $originals_table = "\0"; $reader = new POMO_Reader(); foreach ( $entries as $entry ) { $originals_table .= $this->export_original( $entry ) . "\0"; $length = $reader->strlen( $this->export_original( $entry ) ); fwrite( $fh, pack( 'VV', $length, $current_addr ) ); $current_addr += $length + 1; // Account for the NULL byte after. } $exported_headers = $this->export_headers(); fwrite( $fh, pack( 'VV', $reader->strlen( $exported_headers ), $current_addr ) ); $current_addr += strlen( $exported_headers ) + 1; $translations_table = $exported_headers . "\0"; foreach ( $entries as $entry ) { $translations_table .= $this->export_translations( $entry ) . "\0"; $length = $reader->strlen( $this->export_translations( $entry ) ); fwrite( $fh, pack( 'VV', $length, $current_addr ) ); $current_addr += $length + 1; } fwrite( $fh, $originals_table ); fwrite( $fh, $translations_table ); return true; } /** * @param Translation_Entry $entry * @return string */ public function export_original( $entry ) { // TODO: Warnings for control characters. $exported = $entry->singular; if ( $entry->is_plural ) { $exported .= "\0" . $entry->plural; } if ( $entry->context ) { $exported = $entry->context . "\4" . $exported; } return $exported; } /** * @param Translation_Entry $entry * @return string */ public function export_translations( $entry ) { // TODO: Warnings for control characters. return $entry->is_plural ? implode( "\0", $entry->translations ) : $entry->translations[0]; } /** * @return string */ public function export_headers() { $exported = ''; foreach ( $this->headers as $header => $value ) { $exported .= "$header: $value\n"; } return $exported; } /** * @param int $magic * @return string|false */ public function get_byteorder( $magic ) { // The magic is 0x950412de. // bug in PHP 5.0.2, see https://savannah.nongnu.org/bugs/?func=detailitem&item_id=10565 $magic_little = (int) - 1794895138; $magic_little_64 = (int) 2500072158; // 0xde120495 $magic_big = ( (int) - 569244523 ) & 0xFFFFFFFF; if ( $magic_little === $magic || $magic_little_64 === $magic ) { return 'little'; } elseif ( $magic_big === $magic ) { return 'big'; } else { return false; } } /** * @param POMO_FileReader $reader * @return bool True if the import was successful, otherwise false. */ public function import_from_reader( $reader ) { $endian_string = MO::get_byteorder( $reader->readint32() ); if ( false === $endian_string ) { return false; } $reader->setEndian( $endian_string ); $endian = ( 'big' === $endian_string ) ? 'N' : 'V'; $header = $reader->read( 24 ); if ( $reader->strlen( $header ) !== 24 ) { return false; } // Parse header. $header = unpack( "{$endian}revision/{$endian}total/{$endian}originals_lengths_addr/{$endian}translations_lengths_addr/{$endian}hash_length/{$endian}hash_addr", $header ); if ( ! is_array( $header ) ) { return false; } // Support revision 0 of MO format specs, only. if ( 0 !== $header['revision'] ) { return false; } // Seek to data blocks. $reader->seekto( $header['originals_lengths_addr'] ); // Read originals' indices. $originals_lengths_length = $header['translations_lengths_addr'] - $header['originals_lengths_addr']; if ( $originals_lengths_length !== $header['total'] * 8 ) { return false; } $originals = $reader->read( $originals_lengths_length ); if ( $reader->strlen( $originals ) !== $originals_lengths_length ) { return false; } // Read translations' indices. $translations_lengths_length = $header['hash_addr'] - $header['translations_lengths_addr']; if ( $translations_lengths_length !== $header['total'] * 8 ) { return false; } $translations = $reader->read( $translations_lengths_length ); if ( $reader->strlen( $translations ) !== $translations_lengths_length ) { return false; } // Transform raw data into set of indices. $originals = $reader->str_split( $originals, 8 ); $translations = $reader->str_split( $translations, 8 ); // Skip hash table. $strings_addr = $header['hash_addr'] + $header['hash_length'] * 4; $reader->seekto( $strings_addr ); $strings = $reader->read_all(); $reader->close(); for ( $i = 0; $i < $header['total']; $i++ ) { $o = unpack( "{$endian}length/{$endian}pos", $originals[ $i ] ); $t = unpack( "{$endian}length/{$endian}pos", $translations[ $i ] ); if ( ! $o || ! $t ) { return false; } // Adjust offset due to reading strings to separate space before. $o['pos'] -= $strings_addr; $t['pos'] -= $strings_addr; $original = $reader->substr( $strings, $o['pos'], $o['length'] ); $translation = $reader->substr( $strings, $t['pos'], $t['length'] ); if ( '' === $original ) { $this->set_headers( $this->make_headers( $translation ) ); } else { $entry = &$this->make_entry( $original, $translation ); $this->entries[ $entry->key() ] = &$entry; } } return true; } /** * Build a Translation_Entry from original string and translation strings, * found in a MO file * * @static * @param string $original original string to translate from MO file. Might contain * 0x04 as context separator or 0x00 as singular/plural separator * @param string $translation translation string from MO file. Might contain * 0x00 as a plural translations separator * @return Translation_Entry Entry instance. */ public function &make_entry( $original, $translation ) { $entry = new Translation_Entry(); // Look for context, separated by \4. $parts = explode( "\4", $original ); if ( isset( $parts[1] ) ) { $original = $parts[1]; $entry->context = $parts[0]; } // Look for plural original. $parts = explode( "\0", $original ); $entry->singular = $parts[0]; if ( isset( $parts[1] ) ) { $entry->is_plural = true; $entry->plural = $parts[1]; } // Plural translations are also separated by \0. $entry->translations = explode( "\0", $translation ); return $entry; } /** * @param int $count * @return string */ public function select_plural_form( $count ) { return $this->gettext_select_plural_form( $count ); } /** * @return int */ public function get_plural_forms_count() { return $this->_nplurals; } } endif; PKH\plural-forms.phpnu[ 6, '<' => 5, '<=' => 5, '>' => 5, '>=' => 5, '==' => 4, '!=' => 4, '&&' => 3, '||' => 2, '?:' => 1, '?' => 1, '(' => 0, ')' => 0, ); /** * Tokens generated from the string. * * @since 4.9.0 * @var array $tokens List of tokens. */ protected $tokens = array(); /** * Cache for repeated calls to the function. * * @since 4.9.0 * @var array $cache Map of $n => $result */ protected $cache = array(); /** * Constructor. * * @since 4.9.0 * * @param string $str Plural function (just the bit after `plural=` from Plural-Forms) */ public function __construct( $str ) { $this->parse( $str ); } /** * Parse a Plural-Forms string into tokens. * * Uses the shunting-yard algorithm to convert the string to Reverse Polish * Notation tokens. * * @since 4.9.0 * * @throws Exception If there is a syntax or parsing error with the string. * * @param string $str String to parse. */ protected function parse( $str ) { $pos = 0; $len = strlen( $str ); // Convert infix operators to postfix using the shunting-yard algorithm. $output = array(); $stack = array(); while ( $pos < $len ) { $next = substr( $str, $pos, 1 ); switch ( $next ) { // Ignore whitespace. case ' ': case "\t": ++$pos; break; // Variable (n). case 'n': $output[] = array( 'var' ); ++$pos; break; // Parentheses. case '(': $stack[] = $next; ++$pos; break; case ')': $found = false; while ( ! empty( $stack ) ) { $o2 = $stack[ count( $stack ) - 1 ]; if ( '(' !== $o2 ) { $output[] = array( 'op', array_pop( $stack ) ); continue; } // Discard open paren. array_pop( $stack ); $found = true; break; } if ( ! $found ) { throw new Exception( 'Mismatched parentheses' ); } ++$pos; break; // Operators. case '|': case '&': case '>': case '<': case '!': case '=': case '%': case '?': $end_operator = strspn( $str, self::OP_CHARS, $pos ); $operator = substr( $str, $pos, $end_operator ); if ( ! array_key_exists( $operator, self::$op_precedence ) ) { throw new Exception( sprintf( 'Unknown operator "%s"', $operator ) ); } while ( ! empty( $stack ) ) { $o2 = $stack[ count( $stack ) - 1 ]; // Ternary is right-associative in C. if ( '?:' === $operator || '?' === $operator ) { if ( self::$op_precedence[ $operator ] >= self::$op_precedence[ $o2 ] ) { break; } } elseif ( self::$op_precedence[ $operator ] > self::$op_precedence[ $o2 ] ) { break; } $output[] = array( 'op', array_pop( $stack ) ); } $stack[] = $operator; $pos += $end_operator; break; // Ternary "else". case ':': $found = false; $s_pos = count( $stack ) - 1; while ( $s_pos >= 0 ) { $o2 = $stack[ $s_pos ]; if ( '?' !== $o2 ) { $output[] = array( 'op', array_pop( $stack ) ); --$s_pos; continue; } // Replace. $stack[ $s_pos ] = '?:'; $found = true; break; } if ( ! $found ) { throw new Exception( 'Missing starting "?" ternary operator' ); } ++$pos; break; // Default - number or invalid. default: if ( $next >= '0' && $next <= '9' ) { $span = strspn( $str, self::NUM_CHARS, $pos ); $output[] = array( 'value', intval( substr( $str, $pos, $span ) ) ); $pos += $span; break; } throw new Exception( sprintf( 'Unknown symbol "%s"', $next ) ); } } while ( ! empty( $stack ) ) { $o2 = array_pop( $stack ); if ( '(' === $o2 || ')' === $o2 ) { throw new Exception( 'Mismatched parentheses' ); } $output[] = array( 'op', $o2 ); } $this->tokens = $output; } /** * Get the plural form for a number. * * Caches the value for repeated calls. * * @since 4.9.0 * * @param int $num Number to get plural form for. * @return int Plural form value. */ public function get( $num ) { if ( isset( $this->cache[ $num ] ) ) { return $this->cache[ $num ]; } $this->cache[ $num ] = $this->execute( $num ); return $this->cache[ $num ]; } /** * Execute the plural form function. * * @since 4.9.0 * * @throws Exception If the plural form value cannot be calculated. * * @param int $n Variable "n" to substitute. * @return int Plural form value. */ public function execute( $n ) { $stack = array(); $i = 0; $total = count( $this->tokens ); while ( $i < $total ) { $next = $this->tokens[ $i ]; ++$i; if ( 'var' === $next[0] ) { $stack[] = $n; continue; } elseif ( 'value' === $next[0] ) { $stack[] = $next[1]; continue; } // Only operators left. switch ( $next[1] ) { case '%': $v2 = array_pop( $stack ); $v1 = array_pop( $stack ); $stack[] = $v1 % $v2; break; case '||': $v2 = array_pop( $stack ); $v1 = array_pop( $stack ); $stack[] = $v1 || $v2; break; case '&&': $v2 = array_pop( $stack ); $v1 = array_pop( $stack ); $stack[] = $v1 && $v2; break; case '<': $v2 = array_pop( $stack ); $v1 = array_pop( $stack ); $stack[] = $v1 < $v2; break; case '<=': $v2 = array_pop( $stack ); $v1 = array_pop( $stack ); $stack[] = $v1 <= $v2; break; case '>': $v2 = array_pop( $stack ); $v1 = array_pop( $stack ); $stack[] = $v1 > $v2; break; case '>=': $v2 = array_pop( $stack ); $v1 = array_pop( $stack ); $stack[] = $v1 >= $v2; break; case '!=': $v2 = array_pop( $stack ); $v1 = array_pop( $stack ); $stack[] = $v1 !== $v2; break; case '==': $v2 = array_pop( $stack ); $v1 = array_pop( $stack ); $stack[] = $v1 === $v2; break; case '?:': $v3 = array_pop( $stack ); $v2 = array_pop( $stack ); $v1 = array_pop( $stack ); $stack[] = $v1 ? $v2 : $v3; break; default: throw new Exception( sprintf( 'Unknown operator "%s"', $next[1] ) ); } } if ( count( $stack ) !== 1 ) { throw new Exception( 'Too many values remaining on the stack' ); } return (int) $stack[0]; } } endif; PKH\;;po.phpnu[PKH\/11&<translations.phpnu[PKH\b -nstreams.phpnu[PKH\R fentry.phpnu[PKH\<\C%C%mo.phpnu[PKH\/plural-forms.phpnu[PKH