Changeset 42343 for trunk/src/wp-includes/pomo/po.php
- Timestamp:
- 11/30/2017 11:09:33 PM (6 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/wp-includes/pomo/po.php
r41686 r42343 8 8 */ 9 9 10 require_once dirname( __FILE__) . '/translations.php';10 require_once dirname( __FILE__ ) . '/translations.php'; 11 11 12 12 if ( ! defined( 'PO_MAX_LINE_LEN' ) ) { 13 define( 'PO_MAX_LINE_LEN', 79);13 define( 'PO_MAX_LINE_LEN', 79 ); 14 14 } 15 15 16 ini_set( 'auto_detect_line_endings', 1);16 ini_set( 'auto_detect_line_endings', 1 ); 17 17 18 18 /** 19 19 * Routines for working with PO files 20 20 */ 21 if ( ! class_exists( 'PO', false ) ): 22 class PO extends Gettext_Translations { 23 24 var $comments_before_headers = ''; 25 26 /** 27 * Exports headers to a PO entry 28 * 29 * @return string msgid/msgstr PO entry for this PO file headers, doesn't contain newline at the end 30 */ 31 function export_headers() { 32 $header_string = ''; 33 foreach($this->headers as $header => $value) { 34 $header_string.= "$header: $value\n"; 35 } 36 $poified = PO::poify($header_string); 37 if ($this->comments_before_headers) 38 $before_headers = $this->prepend_each_line(rtrim($this->comments_before_headers)."\n", '# '); 39 else 40 $before_headers = ''; 41 return rtrim("{$before_headers}msgid \"\"\nmsgstr $poified"); 42 } 43 44 /** 45 * Exports all entries to PO format 46 * 47 * @return string sequence of mgsgid/msgstr PO strings, doesn't containt newline at the end 48 */ 49 function export_entries() { 50 //TODO sorting 51 return implode("\n\n", array_map(array('PO', 'export_entry'), $this->entries)); 52 } 53 54 /** 55 * Exports the whole PO file as a string 56 * 57 * @param bool $include_headers whether to include the headers in the export 58 * @return string ready for inclusion in PO file string for headers and all the enrtries 59 */ 60 function export($include_headers = true) { 61 $res = ''; 62 if ($include_headers) { 63 $res .= $this->export_headers(); 64 $res .= "\n\n"; 65 } 66 $res .= $this->export_entries(); 67 return $res; 68 } 69 70 /** 71 * Same as {@link export}, but writes the result to a file 72 * 73 * @param string $filename where to write the PO string 74 * @param bool $include_headers whether to include tje headers in the export 75 * @return bool true on success, false on error 76 */ 77 function export_to_file($filename, $include_headers = true) { 78 $fh = fopen($filename, 'w'); 79 if (false === $fh) return false; 80 $export = $this->export($include_headers); 81 $res = fwrite($fh, $export); 82 if (false === $res) return false; 83 return fclose($fh); 84 } 85 86 /** 87 * Text to include as a comment before the start of the PO contents 88 * 89 * Doesn't need to include # in the beginning of lines, these are added automatically 90 */ 91 function set_comment_before_headers( $text ) { 92 $this->comments_before_headers = $text; 93 } 94 95 /** 96 * Formats a string in PO-style 97 * 98 * @static 99 * @param string $string the string to format 100 * @return string the poified string 101 */ 102 public static function poify($string) { 103 $quote = '"'; 104 $slash = '\\'; 105 $newline = "\n"; 106 107 $replaces = array( 108 "$slash" => "$slash$slash", 109 "$quote" => "$slash$quote", 110 "\t" => '\t', 111 ); 112 113 $string = str_replace(array_keys($replaces), array_values($replaces), $string); 114 115 $po = $quote.implode("${slash}n$quote$newline$quote", explode($newline, $string)).$quote; 116 // add empty string on first line for readbility 117 if (false !== strpos($string, $newline) && 118 (substr_count($string, $newline) > 1 || !($newline === substr($string, -strlen($newline))))) { 119 $po = "$quote$quote$newline$po"; 120 } 121 // remove empty strings 122 $po = str_replace("$newline$quote$quote", '', $po); 123 return $po; 124 } 125 126 /** 127 * Gives back the original string from a PO-formatted string 128 * 129 * @static 130 * @param string $string PO-formatted string 131 * @return string enascaped string 132 */ 133 public static function unpoify($string) { 134 $escapes = array('t' => "\t", 'n' => "\n", 'r' => "\r", '\\' => '\\'); 135 $lines = array_map('trim', explode("\n", $string)); 136 $lines = array_map(array('PO', 'trim_quotes'), $lines); 137 $unpoified = ''; 138 $previous_is_backslash = false; 139 foreach($lines as $line) { 140 preg_match_all('/./u', $line, $chars); 141 $chars = $chars[0]; 142 foreach($chars as $char) { 143 if (!$previous_is_backslash) { 144 if ('\\' == $char) 145 $previous_is_backslash = true; 146 else 147 $unpoified .= $char; 21 if ( ! class_exists( 'PO', false ) ) : 22 class PO extends Gettext_Translations { 23 24 var $comments_before_headers = ''; 25 26 /** 27 * Exports headers to a PO entry 28 * 29 * @return string msgid/msgstr PO entry for this PO file headers, doesn't contain newline at the end 30 */ 31 function export_headers() { 32 $header_string = ''; 33 foreach ( $this->headers as $header => $value ) { 34 $header_string .= "$header: $value\n"; 35 } 36 $poified = PO::poify( $header_string ); 37 if ( $this->comments_before_headers ) { 38 $before_headers = $this->prepend_each_line( rtrim( $this->comments_before_headers ) . "\n", '# ' ); 39 } else { 40 $before_headers = ''; 41 } 42 return rtrim( "{$before_headers}msgid \"\"\nmsgstr $poified" ); 43 } 44 45 /** 46 * Exports all entries to PO format 47 * 48 * @return string sequence of mgsgid/msgstr PO strings, doesn't containt newline at the end 49 */ 50 function export_entries() { 51 //TODO sorting 52 return implode( "\n\n", array_map( array( 'PO', 'export_entry' ), $this->entries ) ); 53 } 54 55 /** 56 * Exports the whole PO file as a string 57 * 58 * @param bool $include_headers whether to include the headers in the export 59 * @return string ready for inclusion in PO file string for headers and all the enrtries 60 */ 61 function export( $include_headers = true ) { 62 $res = ''; 63 if ( $include_headers ) { 64 $res .= $this->export_headers(); 65 $res .= "\n\n"; 66 } 67 $res .= $this->export_entries(); 68 return $res; 69 } 70 71 /** 72 * Same as {@link export}, but writes the result to a file 73 * 74 * @param string $filename where to write the PO string 75 * @param bool $include_headers whether to include tje headers in the export 76 * @return bool true on success, false on error 77 */ 78 function export_to_file( $filename, $include_headers = true ) { 79 $fh = fopen( $filename, 'w' ); 80 if ( false === $fh ) { 81 return false; 82 } 83 $export = $this->export( $include_headers ); 84 $res = fwrite( $fh, $export ); 85 if ( false === $res ) { 86 return false; 87 } 88 return fclose( $fh ); 89 } 90 91 /** 92 * Text to include as a comment before the start of the PO contents 93 * 94 * Doesn't need to include # in the beginning of lines, these are added automatically 95 */ 96 function set_comment_before_headers( $text ) { 97 $this->comments_before_headers = $text; 98 } 99 100 /** 101 * Formats a string in PO-style 102 * 103 * @static 104 * @param string $string the string to format 105 * @return string the poified string 106 */ 107 public static function poify( $string ) { 108 $quote = '"'; 109 $slash = '\\'; 110 $newline = "\n"; 111 112 $replaces = array( 113 "$slash" => "$slash$slash", 114 "$quote" => "$slash$quote", 115 "\t" => '\t', 116 ); 117 118 $string = str_replace( array_keys( $replaces ), array_values( $replaces ), $string ); 119 120 $po = $quote . implode( "${slash}n$quote$newline$quote", explode( $newline, $string ) ) . $quote; 121 // add empty string on first line for readbility 122 if ( false !== strpos( $string, $newline ) && 123 ( substr_count( $string, $newline ) > 1 || ! ( $newline === substr( $string, -strlen( $newline ) ) ) ) ) { 124 $po = "$quote$quote$newline$po"; 125 } 126 // remove empty strings 127 $po = str_replace( "$newline$quote$quote", '', $po ); 128 return $po; 129 } 130 131 /** 132 * Gives back the original string from a PO-formatted string 133 * 134 * @static 135 * @param string $string PO-formatted string 136 * @return string enascaped string 137 */ 138 public static function unpoify( $string ) { 139 $escapes = array( 140 't' => "\t", 141 'n' => "\n", 142 'r' => "\r", 143 '\\' => '\\', 144 ); 145 $lines = array_map( 'trim', explode( "\n", $string ) ); 146 $lines = array_map( array( 'PO', 'trim_quotes' ), $lines ); 147 $unpoified = ''; 148 $previous_is_backslash = false; 149 foreach ( $lines as $line ) { 150 preg_match_all( '/./u', $line, $chars ); 151 $chars = $chars[0]; 152 foreach ( $chars as $char ) { 153 if ( ! $previous_is_backslash ) { 154 if ( '\\' == $char ) { 155 $previous_is_backslash = true; 156 } else { 157 $unpoified .= $char; 158 } 159 } else { 160 $previous_is_backslash = false; 161 $unpoified .= isset( $escapes[ $char ] ) ? $escapes[ $char ] : $char; 162 } 163 } 164 } 165 166 // Standardise the line endings on imported content, technically PO files shouldn't contain \r 167 $unpoified = str_replace( array( "\r\n", "\r" ), "\n", $unpoified ); 168 169 return $unpoified; 170 } 171 172 /** 173 * Inserts $with in the beginning of every new line of $string and 174 * returns the modified string 175 * 176 * @static 177 * @param string $string prepend lines in this string 178 * @param string $with prepend lines with this string 179 */ 180 public static function prepend_each_line( $string, $with ) { 181 $lines = explode( "\n", $string ); 182 $append = ''; 183 if ( "\n" === substr( $string, -1 ) && '' === end( $lines ) ) { 184 // Last line might be empty because $string was terminated 185 // with a newline, remove it from the $lines array, 186 // we'll restore state by re-terminating the string at the end 187 array_pop( $lines ); 188 $append = "\n"; 189 } 190 foreach ( $lines as &$line ) { 191 $line = $with . $line; 192 } 193 unset( $line ); 194 return implode( "\n", $lines ) . $append; 195 } 196 197 /** 198 * Prepare a text as a comment -- wraps the lines and prepends # 199 * and a special character to each line 200 * 201 * @access private 202 * @param string $text the comment text 203 * @param string $char character to denote a special PO comment, 204 * like :, default is a space 205 */ 206 public static function comment_block( $text, $char = ' ' ) { 207 $text = wordwrap( $text, PO_MAX_LINE_LEN - 3 ); 208 return PO::prepend_each_line( $text, "#$char " ); 209 } 210 211 /** 212 * Builds a string from the entry for inclusion in PO file 213 * 214 * @static 215 * @param Translation_Entry $entry the entry to convert to po string (passed by reference). 216 * @return false|string PO-style formatted string for the entry or 217 * false if the entry is empty 218 */ 219 public static function export_entry( &$entry ) { 220 if ( null === $entry->singular || '' === $entry->singular ) { 221 return false; 222 } 223 $po = array(); 224 if ( ! empty( $entry->translator_comments ) ) { 225 $po[] = PO::comment_block( $entry->translator_comments ); 226 } 227 if ( ! empty( $entry->extracted_comments ) ) { 228 $po[] = PO::comment_block( $entry->extracted_comments, '.' ); 229 } 230 if ( ! empty( $entry->references ) ) { 231 $po[] = PO::comment_block( implode( ' ', $entry->references ), ':' ); 232 } 233 if ( ! empty( $entry->flags ) ) { 234 $po[] = PO::comment_block( implode( ', ', $entry->flags ), ',' ); 235 } 236 if ( $entry->context ) { 237 $po[] = 'msgctxt ' . PO::poify( $entry->context ); 238 } 239 $po[] = 'msgid ' . PO::poify( $entry->singular ); 240 if ( ! $entry->is_plural ) { 241 $translation = empty( $entry->translations ) ? '' : $entry->translations[0]; 242 $translation = PO::match_begin_and_end_newlines( $translation, $entry->singular ); 243 $po[] = 'msgstr ' . PO::poify( $translation ); 244 } else { 245 $po[] = 'msgid_plural ' . PO::poify( $entry->plural ); 246 $translations = empty( $entry->translations ) ? array( '', '' ) : $entry->translations; 247 foreach ( $translations as $i => $translation ) { 248 $translation = PO::match_begin_and_end_newlines( $translation, $entry->plural ); 249 $po[] = "msgstr[$i] " . PO::poify( $translation ); 250 } 251 } 252 return implode( "\n", $po ); 253 } 254 255 public static function match_begin_and_end_newlines( $translation, $original ) { 256 if ( '' === $translation ) { 257 return $translation; 258 } 259 260 $original_begin = "\n" === substr( $original, 0, 1 ); 261 $original_end = "\n" === substr( $original, -1 ); 262 $translation_begin = "\n" === substr( $translation, 0, 1 ); 263 $translation_end = "\n" === substr( $translation, -1 ); 264 265 if ( $original_begin ) { 266 if ( ! $translation_begin ) { 267 $translation = "\n" . $translation; 268 } 269 } elseif ( $translation_begin ) { 270 $translation = ltrim( $translation, "\n" ); 271 } 272 273 if ( $original_end ) { 274 if ( ! $translation_end ) { 275 $translation .= "\n"; 276 } 277 } elseif ( $translation_end ) { 278 $translation = rtrim( $translation, "\n" ); 279 } 280 281 return $translation; 282 } 283 284 /** 285 * @param string $filename 286 * @return boolean 287 */ 288 function import_from_file( $filename ) { 289 $f = fopen( $filename, 'r' ); 290 if ( ! $f ) { 291 return false; 292 } 293 $lineno = 0; 294 while ( true ) { 295 $res = $this->read_entry( $f, $lineno ); 296 if ( ! $res ) { 297 break; 298 } 299 if ( $res['entry']->singular == '' ) { 300 $this->set_headers( $this->make_headers( $res['entry']->translations[0] ) ); 148 301 } else { 149 $previous_is_backslash = false; 150 $unpoified .= isset($escapes[$char])? $escapes[$char] : $char; 151 } 152 } 153 } 154 155 // Standardise the line endings on imported content, technically PO files shouldn't contain \r 156 $unpoified = str_replace( array( "\r\n", "\r" ), "\n", $unpoified ); 157 158 return $unpoified; 159 } 160 161 /** 162 * Inserts $with in the beginning of every new line of $string and 163 * returns the modified string 164 * 165 * @static 166 * @param string $string prepend lines in this string 167 * @param string $with prepend lines with this string 168 */ 169 public static function prepend_each_line($string, $with) { 170 $lines = explode("\n", $string); 171 $append = ''; 172 if ("\n" === substr($string, -1) && '' === end($lines)) { 173 // Last line might be empty because $string was terminated 174 // with a newline, remove it from the $lines array, 175 // we'll restore state by re-terminating the string at the end 176 array_pop($lines); 177 $append = "\n"; 178 } 179 foreach ($lines as &$line) { 180 $line = $with . $line; 181 } 182 unset($line); 183 return implode("\n", $lines) . $append; 184 } 185 186 /** 187 * Prepare a text as a comment -- wraps the lines and prepends # 188 * and a special character to each line 189 * 190 * @access private 191 * @param string $text the comment text 192 * @param string $char character to denote a special PO comment, 193 * like :, default is a space 194 */ 195 public static function comment_block($text, $char=' ') { 196 $text = wordwrap($text, PO_MAX_LINE_LEN - 3); 197 return PO::prepend_each_line($text, "#$char "); 198 } 199 200 /** 201 * Builds a string from the entry for inclusion in PO file 202 * 203 * @static 204 * @param Translation_Entry $entry the entry to convert to po string (passed by reference). 205 * @return false|string PO-style formatted string for the entry or 206 * false if the entry is empty 207 */ 208 public static function export_entry(&$entry) { 209 if ( null === $entry->singular || '' === $entry->singular ) return false; 210 $po = array(); 211 if (!empty($entry->translator_comments)) $po[] = PO::comment_block($entry->translator_comments); 212 if (!empty($entry->extracted_comments)) $po[] = PO::comment_block($entry->extracted_comments, '.'); 213 if (!empty($entry->references)) $po[] = PO::comment_block(implode(' ', $entry->references), ':'); 214 if (!empty($entry->flags)) $po[] = PO::comment_block(implode(", ", $entry->flags), ','); 215 if ($entry->context) $po[] = 'msgctxt '.PO::poify($entry->context); 216 $po[] = 'msgid '.PO::poify($entry->singular); 217 if (!$entry->is_plural) { 218 $translation = empty($entry->translations)? '' : $entry->translations[0]; 219 $translation = PO::match_begin_and_end_newlines( $translation, $entry->singular ); 220 $po[] = 'msgstr '.PO::poify($translation); 221 } else { 222 $po[] = 'msgid_plural '.PO::poify($entry->plural); 223 $translations = empty($entry->translations)? array('', '') : $entry->translations; 224 foreach($translations as $i => $translation) { 225 $translation = PO::match_begin_and_end_newlines( $translation, $entry->plural ); 226 $po[] = "msgstr[$i] ".PO::poify($translation); 227 } 228 } 229 return implode("\n", $po); 230 } 231 232 public static function match_begin_and_end_newlines( $translation, $original ) { 233 if ( '' === $translation ) { 234 return $translation; 235 } 236 237 $original_begin = "\n" === substr( $original, 0, 1 ); 238 $original_end = "\n" === substr( $original, -1 ); 239 $translation_begin = "\n" === substr( $translation, 0, 1 ); 240 $translation_end = "\n" === substr( $translation, -1 ); 241 242 if ( $original_begin ) { 243 if ( ! $translation_begin ) { 244 $translation = "\n" . $translation; 245 } 246 } elseif ( $translation_begin ) { 247 $translation = ltrim( $translation, "\n" ); 248 } 249 250 if ( $original_end ) { 251 if ( ! $translation_end ) { 252 $translation .= "\n"; 253 } 254 } elseif ( $translation_end ) { 255 $translation = rtrim( $translation, "\n" ); 256 } 257 258 return $translation; 259 } 260 261 /** 262 * @param string $filename 263 * @return boolean 264 */ 265 function import_from_file($filename) { 266 $f = fopen($filename, 'r'); 267 if (!$f) return false; 268 $lineno = 0; 269 while (true) { 270 $res = $this->read_entry($f, $lineno); 271 if (!$res) break; 272 if ($res['entry']->singular == '') { 273 $this->set_headers($this->make_headers($res['entry']->translations[0])); 274 } else { 275 $this->add_entry($res['entry']); 276 } 277 } 278 PO::read_line($f, 'clear'); 279 if ( false === $res ) { 280 return false; 281 } 282 if ( ! $this->headers && ! $this->entries ) { 283 return false; 284 } 285 return true; 286 } 287 288 /** 289 * Helper function for read_entry 290 * @param string $context 291 * @return bool 292 */ 293 protected static function is_final($context) { 294 return ($context === 'msgstr') || ($context === 'msgstr_plural'); 295 } 296 297 /** 298 * @param resource $f 299 * @param int $lineno 300 * @return null|false|array 301 */ 302 function read_entry($f, $lineno = 0) { 303 $entry = new Translation_Entry(); 304 // where were we in the last step 305 // can be: comment, msgctxt, msgid, msgid_plural, msgstr, msgstr_plural 306 $context = ''; 307 $msgstr_index = 0; 308 while (true) { 309 $lineno++; 310 $line = PO::read_line($f); 311 if (!$line) { 312 if (feof($f)) { 313 if (self::is_final($context)) 302 $this->add_entry( $res['entry'] ); 303 } 304 } 305 PO::read_line( $f, 'clear' ); 306 if ( false === $res ) { 307 return false; 308 } 309 if ( ! $this->headers && ! $this->entries ) { 310 return false; 311 } 312 return true; 313 } 314 315 /** 316 * Helper function for read_entry 317 * 318 * @param string $context 319 * @return bool 320 */ 321 protected static function is_final( $context ) { 322 return ( $context === 'msgstr' ) || ( $context === 'msgstr_plural' ); 323 } 324 325 /** 326 * @param resource $f 327 * @param int $lineno 328 * @return null|false|array 329 */ 330 function read_entry( $f, $lineno = 0 ) { 331 $entry = new Translation_Entry(); 332 // where were we in the last step 333 // can be: comment, msgctxt, msgid, msgid_plural, msgstr, msgstr_plural 334 $context = ''; 335 $msgstr_index = 0; 336 while ( true ) { 337 $lineno++; 338 $line = PO::read_line( $f ); 339 if ( ! $line ) { 340 if ( feof( $f ) ) { 341 if ( self::is_final( $context ) ) { 342 break; 343 } elseif ( ! $context ) { // we haven't read a line and eof came 344 return null; 345 } else { 346 return false; 347 } 348 } else { 349 return false; 350 } 351 } 352 if ( $line == "\n" ) { 353 continue; 354 } 355 $line = trim( $line ); 356 if ( preg_match( '/^#/', $line, $m ) ) { 357 // the comment is the start of a new entry 358 if ( self::is_final( $context ) ) { 359 PO::read_line( $f, 'put-back' ); 360 $lineno--; 314 361 break; 315 elseif (!$context) // we haven't read a line and eof came 316 return null; 317 else 318 return false; 362 } 363 // comments have to be at the beginning 364 if ( $context && $context != 'comment' ) { 365 return false; 366 } 367 // add comment 368 $this->add_comment_to_entry( $entry, $line ); 369 } elseif ( preg_match( '/^msgctxt\s+(".*")/', $line, $m ) ) { 370 if ( self::is_final( $context ) ) { 371 PO::read_line( $f, 'put-back' ); 372 $lineno--; 373 break; 374 } 375 if ( $context && $context != 'comment' ) { 376 return false; 377 } 378 $context = 'msgctxt'; 379 $entry->context .= PO::unpoify( $m[1] ); 380 } elseif ( preg_match( '/^msgid\s+(".*")/', $line, $m ) ) { 381 if ( self::is_final( $context ) ) { 382 PO::read_line( $f, 'put-back' ); 383 $lineno--; 384 break; 385 } 386 if ( $context && $context != 'msgctxt' && $context != 'comment' ) { 387 return false; 388 } 389 $context = 'msgid'; 390 $entry->singular .= PO::unpoify( $m[1] ); 391 } elseif ( preg_match( '/^msgid_plural\s+(".*")/', $line, $m ) ) { 392 if ( $context != 'msgid' ) { 393 return false; 394 } 395 $context = 'msgid_plural'; 396 $entry->is_plural = true; 397 $entry->plural .= PO::unpoify( $m[1] ); 398 } elseif ( preg_match( '/^msgstr\s+(".*")/', $line, $m ) ) { 399 if ( $context != 'msgid' ) { 400 return false; 401 } 402 $context = 'msgstr'; 403 $entry->translations = array( PO::unpoify( $m[1] ) ); 404 } elseif ( preg_match( '/^msgstr\[(\d+)\]\s+(".*")/', $line, $m ) ) { 405 if ( $context != 'msgid_plural' && $context != 'msgstr_plural' ) { 406 return false; 407 } 408 $context = 'msgstr_plural'; 409 $msgstr_index = $m[1]; 410 $entry->translations[ $m[1] ] = PO::unpoify( $m[2] ); 411 } elseif ( preg_match( '/^".*"$/', $line ) ) { 412 $unpoified = PO::unpoify( $line ); 413 switch ( $context ) { 414 case 'msgid': 415 $entry->singular .= $unpoified; 416 break; 417 case 'msgctxt': 418 $entry->context .= $unpoified; 419 break; 420 case 'msgid_plural': 421 $entry->plural .= $unpoified; 422 break; 423 case 'msgstr': 424 $entry->translations[0] .= $unpoified; 425 break; 426 case 'msgstr_plural': 427 $entry->translations[ $msgstr_index ] .= $unpoified; 428 break; 429 default: 430 return false; 431 } 319 432 } else { 320 433 return false; 321 434 } 322 435 } 323 if ($line == "\n") continue; 324 $line = trim($line); 325 if (preg_match('/^#/', $line, $m)) { 326 // the comment is the start of a new entry 327 if (self::is_final($context)) { 328 PO::read_line($f, 'put-back'); 329 $lineno--; 436 437 $have_translations = false; 438 foreach ( $entry->translations as $t ) { 439 if ( $t || ( '0' === $t ) ) { 440 $have_translations = true; 330 441 break; 331 442 } 332 // comments have to be at the beginning 333 if ($context && $context != 'comment') { 334 return false; 335 } 336 // add comment 337 $this->add_comment_to_entry($entry, $line); 338 } elseif (preg_match('/^msgctxt\s+(".*")/', $line, $m)) { 339 if (self::is_final($context)) { 340 PO::read_line($f, 'put-back'); 341 $lineno--; 342 break; 343 } 344 if ($context && $context != 'comment') { 345 return false; 346 } 347 $context = 'msgctxt'; 348 $entry->context .= PO::unpoify($m[1]); 349 } elseif (preg_match('/^msgid\s+(".*")/', $line, $m)) { 350 if (self::is_final($context)) { 351 PO::read_line($f, 'put-back'); 352 $lineno--; 353 break; 354 } 355 if ($context && $context != 'msgctxt' && $context != 'comment') { 356 return false; 357 } 358 $context = 'msgid'; 359 $entry->singular .= PO::unpoify($m[1]); 360 } elseif (preg_match('/^msgid_plural\s+(".*")/', $line, $m)) { 361 if ($context != 'msgid') { 362 return false; 363 } 364 $context = 'msgid_plural'; 365 $entry->is_plural = true; 366 $entry->plural .= PO::unpoify($m[1]); 367 } elseif (preg_match('/^msgstr\s+(".*")/', $line, $m)) { 368 if ($context != 'msgid') { 369 return false; 370 } 371 $context = 'msgstr'; 372 $entry->translations = array(PO::unpoify($m[1])); 373 } elseif (preg_match('/^msgstr\[(\d+)\]\s+(".*")/', $line, $m)) { 374 if ($context != 'msgid_plural' && $context != 'msgstr_plural') { 375 return false; 376 } 377 $context = 'msgstr_plural'; 378 $msgstr_index = $m[1]; 379 $entry->translations[$m[1]] = PO::unpoify($m[2]); 380 } elseif (preg_match('/^".*"$/', $line)) { 381 $unpoified = PO::unpoify($line); 382 switch ($context) { 383 case 'msgid': 384 $entry->singular .= $unpoified; break; 385 case 'msgctxt': 386 $entry->context .= $unpoified; break; 387 case 'msgid_plural': 388 $entry->plural .= $unpoified; break; 389 case 'msgstr': 390 $entry->translations[0] .= $unpoified; break; 391 case 'msgstr_plural': 392 $entry->translations[$msgstr_index] .= $unpoified; break; 393 default: 394 return false; 395 } 443 } 444 if ( false === $have_translations ) { 445 $entry->translations = array(); 446 } 447 448 return array( 449 'entry' => $entry, 450 'lineno' => $lineno, 451 ); 452 } 453 454 /** 455 * @staticvar string $last_line 456 * @staticvar boolean $use_last_line 457 * 458 * @param resource $f 459 * @param string $action 460 * @return boolean 461 */ 462 function read_line( $f, $action = 'read' ) { 463 static $last_line = ''; 464 static $use_last_line = false; 465 if ( 'clear' == $action ) { 466 $last_line = ''; 467 return true; 468 } 469 if ( 'put-back' == $action ) { 470 $use_last_line = true; 471 return true; 472 } 473 $line = $use_last_line ? $last_line : fgets( $f ); 474 $line = ( "\r\n" == substr( $line, -2 ) ) ? rtrim( $line, "\r\n" ) . "\n" : $line; 475 $last_line = $line; 476 $use_last_line = false; 477 return $line; 478 } 479 480 /** 481 * @param Translation_Entry $entry 482 * @param string $po_comment_line 483 */ 484 function add_comment_to_entry( &$entry, $po_comment_line ) { 485 $first_two = substr( $po_comment_line, 0, 2 ); 486 $comment = trim( substr( $po_comment_line, 2 ) ); 487 if ( '#:' == $first_two ) { 488 $entry->references = array_merge( $entry->references, preg_split( '/\s+/', $comment ) ); 489 } elseif ( '#.' == $first_two ) { 490 $entry->extracted_comments = trim( $entry->extracted_comments . "\n" . $comment ); 491 } elseif ( '#,' == $first_two ) { 492 $entry->flags = array_merge( $entry->flags, preg_split( '/,\s*/', $comment ) ); 396 493 } else { 397 return false; 398 } 399 } 400 401 $have_translations = false; 402 foreach ( $entry->translations as $t ) { 403 if ( $t || ('0' === $t) ) { 404 $have_translations = true; 405 break; 406 } 407 } 408 if ( false === $have_translations ) { 409 $entry->translations = array(); 410 } 411 412 return array('entry' => $entry, 'lineno' => $lineno); 494 $entry->translator_comments = trim( $entry->translator_comments . "\n" . $comment ); 495 } 496 } 497 498 /** 499 * @param string $s 500 * @return sring 501 */ 502 public static function trim_quotes( $s ) { 503 if ( substr( $s, 0, 1 ) == '"' ) { 504 $s = substr( $s, 1 ); 505 } 506 if ( substr( $s, -1, 1 ) == '"' ) { 507 $s = substr( $s, 0, -1 ); 508 } 509 return $s; 510 } 413 511 } 414 415 /**416 * @staticvar string $last_line417 * @staticvar boolean $use_last_line418 *419 * @param resource $f420 * @param string $action421 * @return boolean422 */423 function read_line($f, $action = 'read') {424 static $last_line = '';425 static $use_last_line = false;426 if ('clear' == $action) {427 $last_line = '';428 return true;429 }430 if ('put-back' == $action) {431 $use_last_line = true;432 return true;433 }434 $line = $use_last_line? $last_line : fgets($f);435 $line = ( "\r\n" == substr( $line, -2 ) ) ? rtrim( $line, "\r\n" ) . "\n" : $line;436 $last_line = $line;437 $use_last_line = false;438 return $line;439 }440 441 /**442 * @param Translation_Entry $entry443 * @param string $po_comment_line444 */445 function add_comment_to_entry(&$entry, $po_comment_line) {446 $first_two = substr($po_comment_line, 0, 2);447 $comment = trim(substr($po_comment_line, 2));448 if ('#:' == $first_two) {449 $entry->references = array_merge($entry->references, preg_split('/\s+/', $comment));450 } elseif ('#.' == $first_two) {451 $entry->extracted_comments = trim($entry->extracted_comments . "\n" . $comment);452 } elseif ('#,' == $first_two) {453 $entry->flags = array_merge($entry->flags, preg_split('/,\s*/', $comment));454 } else {455 $entry->translator_comments = trim($entry->translator_comments . "\n" . $comment);456 }457 }458 459 /**460 * @param string $s461 * @return sring462 */463 public static function trim_quotes($s) {464 if ( substr($s, 0, 1) == '"') $s = substr($s, 1);465 if ( substr($s, -1, 1) == '"') $s = substr($s, 0, -1);466 return $s;467 }468 }469 512 endif;
Note: See TracChangeset
for help on using the changeset viewer.