252 lines
7.9 KiB
PHP
252 lines
7.9 KiB
PHP
<?php
|
|
/**
|
|
* Console application, which extracts or replaces strings for
|
|
* translation, which cannot be gettexted
|
|
*
|
|
* @package wordpress-i18n
|
|
* @subpackage tools
|
|
*/
|
|
// see: http://php.net/tokenizer
|
|
if ( ! defined( 'T_ML_COMMENT' ) ) {
|
|
define( 'T_ML_COMMENT', T_COMMENT );
|
|
} else {
|
|
define( 'T_DOC_COMMENT', T_ML_COMMENT );
|
|
}
|
|
|
|
$pomo = dirname( __FILE__ ) . '/pomo';
|
|
require_once( "$pomo/po.php" );
|
|
require_once( "$pomo/mo.php" );
|
|
|
|
class NotGettexted {
|
|
public $enable_logging = false;
|
|
|
|
public $STAGE_OUTSIDE = 0;
|
|
public $STAGE_START_COMMENT = 1;
|
|
public $STAGE_WHITESPACE_BEFORE = 2;
|
|
public $STAGE_STRING = 3;
|
|
public $STAGE_WHITESPACE_AFTER = 4;
|
|
public $STAGE_END_COMMENT = 4;
|
|
|
|
public $commands = array( 'extract' => 'command_extract', 'replace' => 'command_replace' );
|
|
|
|
public function logmsg() {
|
|
$args = func_get_args();
|
|
if ( $this->enable_logging ) {
|
|
error_log( implode( ' ', $args ) );
|
|
}
|
|
}
|
|
|
|
public function stderr( $msg, $nl=true ) {
|
|
fwrite( STDERR, $msg . ( $nl ? "\n" : "" ) );
|
|
}
|
|
|
|
public function cli_die( $msg ) {
|
|
$this->stderr( $msg );
|
|
exit( 1 );
|
|
}
|
|
|
|
public function unchanged_token( $token, $s = '' ) {
|
|
return is_array( $token )? $token[1] : $token;
|
|
}
|
|
|
|
public function ignore_token( $token, $s = '' ) {
|
|
return '';
|
|
}
|
|
|
|
public function list_php_files( $dir ) {
|
|
$files = array();
|
|
$items = scandir( $dir );
|
|
foreach ( (array) $items as $item ) {
|
|
$full_item = $dir . '/' . $item;
|
|
if ( '.' == $item || '..' == $item ) {
|
|
continue;
|
|
}
|
|
if ( '.php' == substr( $item, -4 ) ) {
|
|
$files[] = $full_item;
|
|
}
|
|
if ( '.html' == substr( $item, -5 ) ) {
|
|
$files[] = $full_item;
|
|
}
|
|
if ( is_dir( $full_item ) ) {
|
|
$files += array_merge( $files, NotGettexted::list_php_files( $full_item, $files ) );
|
|
}
|
|
}
|
|
return $files;
|
|
}
|
|
|
|
public function make_string_aggregator( $global_array_name, $filename ) {
|
|
$a = $global_array_name;
|
|
return function ($string, $comment_id, $line_number) use ($a, $filename) {
|
|
global ${$a};
|
|
${$a}[] = array($string, $comment_id, var_export( $filename, true ), $line_number);
|
|
};
|
|
}
|
|
|
|
public function make_mo_replacer( $global_mo_name ) {
|
|
$m = $global_mo_name;
|
|
return function ($token, $string) use ($m) {
|
|
global ${$m};
|
|
return var_export(${$m}->translate($string), true);
|
|
};
|
|
}
|
|
|
|
public function walk_tokens( &$tokens, $string_action, $other_action, $register_action = null ) {
|
|
|
|
$current_comment_id = '';
|
|
$current_string = '';
|
|
$current_string_line = 0;
|
|
|
|
$result = '';
|
|
$line = 1;
|
|
|
|
foreach( $tokens as $token ) {
|
|
if ( is_array( $token ) ) {
|
|
list( $id, $text ) = $token;
|
|
$line += substr_count( $text, "\n" );
|
|
if ( ( T_ML_COMMENT == $id || T_COMMENT == $id ) && preg_match( '|/\*\s*(/?WP_I18N_[a-z_]+)\s*\*/|i', $text, $matches ) ) {
|
|
if ( $this->STAGE_OUTSIDE == $stage ) {
|
|
$stage = $this->STAGE_START_COMMENT;
|
|
$current_comment_id = $matches[1];
|
|
$this->logmsg( 'start comment', $current_comment_id );
|
|
$result .= call_user_func( $other_action, $token );
|
|
continue;
|
|
}
|
|
if ( $this->STAGE_START_COMMENT <= $stage && $stage <= $this->STAGE_WHITESPACE_AFTER && '/' . $current_comment_id == $matches[1] ) {
|
|
$stage = $this->STAGE_END_COMMENT;
|
|
$this->logmsg( 'end comment', $current_comment_id );
|
|
$result .= call_user_func( $other_action, $token );
|
|
if ( ! is_null( $register_action ) ) {
|
|
call_user_func( $register_action, $current_string, $current_comment_id, $current_string_line );
|
|
}
|
|
continue;
|
|
}
|
|
} elseif ( T_CONSTANT_ENCAPSED_STRING == $id ) {
|
|
if ( $this->STAGE_START_COMMENT <= $stage && $stage < $this->STAGE_WHITESPACE_AFTER ) {
|
|
eval( '$current_string=' . $text . ';' );
|
|
$this->logmsg( 'string', $current_string );
|
|
$current_string_line = $line;
|
|
$result .= call_user_func( $string_action, $token, $current_string );
|
|
continue;
|
|
}
|
|
} elseif ( T_WHITESPACE == $id ) {
|
|
if ( $this->STAGE_START_COMMENT <= $stage && $stage < $this->STAGE_STRING ) {
|
|
$stage = $this->STAGE_WHITESPACE_BEFORE;
|
|
$this->logmsg( 'whitespace before' );
|
|
$result .= call_user_func( $other_action, $token );
|
|
continue;
|
|
}
|
|
if ( $this->STAGE_STRING < $stage && $stage < $this->STAGE_END_COMMENT ) {
|
|
$stage = $this->STAGE_WHITESPACE_AFTER;
|
|
$this->logmsg( 'whitespace after' );
|
|
$result .= call_user_func( $other_action, $token );
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
$result .= call_user_func( $other_action, $token );
|
|
$stage = $this->STAGE_OUTSIDE;
|
|
$current_comment_id = '';
|
|
$current_string = '';
|
|
$current_string_line = 0;
|
|
}
|
|
return $result;
|
|
}
|
|
|
|
public function command_extract() {
|
|
$args = func_get_args();
|
|
$pot_filename = $args[0];
|
|
if ( isset( $args[1] ) && is_array( $args[1] ) ) {
|
|
$filenames = $args[1];
|
|
} else {
|
|
$filenames = array_slice($args, 1);
|
|
}
|
|
|
|
$global_name = '__entries_' . mt_rand( 1, 1000 );
|
|
$GLOBALS[ $global_name ] = array();
|
|
|
|
foreach( $filenames as $filename ) {
|
|
$tokens = token_get_all( file_get_contents( $filename ) );
|
|
$aggregator = $this->make_string_aggregator( $global_name, $filename );
|
|
$this->walk_tokens( $tokens, array( $this, 'ignore_token' ), array( $this, 'ignore_token' ), $aggregator );
|
|
}
|
|
|
|
$potf = '-' == $pot_filename ? STDOUT : @fopen( $pot_filename, 'a' );
|
|
if ( false === $potf ) {
|
|
$this->cli_die( "Couldn't open pot file: $pot_filename" );
|
|
}
|
|
|
|
foreach( $GLOBALS[ $global_name ] as $item ) {
|
|
@list( $string, $comment_id, $filename, $line_number ) = $item;
|
|
$filename = isset( $filename ) ? preg_replace( '|^\./|', '', $filename ) : '';
|
|
$ref_line_number = isset( $line_number ) ? ":$line_number" : '';
|
|
$args = array(
|
|
'singular' => $string,
|
|
'extracted_comments' => "Not gettexted string $comment_id",
|
|
'references' => array( "$filename$ref_line_number" ),
|
|
);
|
|
$entry = new Translation_Entry( $args );
|
|
fwrite( $potf, "\n" . PO::export_entry( $entry ) . "\n");
|
|
}
|
|
if ( '-' != $pot_filename ) {
|
|
fclose($potf);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
public function command_replace() {
|
|
$args = func_get_args();
|
|
$mo_filename = $args[0];
|
|
if ( isset( $args[1] ) && is_array( $args[1] ) ) {
|
|
$filenames = $args[1];
|
|
} else {
|
|
$filenames = array_slice( $args, 1 );
|
|
}
|
|
|
|
$global_name = '__mo_' . mt_rand( 1, 1000 );
|
|
$GLOBALS[ $global_name ] = new MO();
|
|
$replacer = $this->make_mo_replacer( $global_name );
|
|
|
|
$res = $GLOBALS[ $global_name ]->import_from_file( $mo_filename );
|
|
if ( false === $res ) {
|
|
$this->cli_die("Couldn't read MO file '$mo_filename'!");
|
|
}
|
|
foreach( $filenames as $filename ) {
|
|
$source = file_get_contents( $filename );
|
|
if ( strlen( $source ) > 150000 ) {
|
|
continue;
|
|
}
|
|
$tokens = token_get_all( $source );
|
|
$new_file = $this->walk_tokens( $tokens, $replacer, array( $this, 'unchanged_token' ) );
|
|
$f = fopen( $filename, 'w' );
|
|
fwrite( $f, $new_file );
|
|
fclose( $f );
|
|
}
|
|
return true;
|
|
}
|
|
|
|
public function usage() {
|
|
$this->stderr( 'php i18n-comments.php COMMAND OUTPUTFILE INPUTFILES' );
|
|
$this->stderr( 'Extracts and replaces strings, which cannot be gettexted' );
|
|
$this->stderr( 'Commands:' );
|
|
$this->stderr( ' extract POTFILE PHPFILES appends the strings to POTFILE' );
|
|
$this->stderr( ' replace MOFILE PHPFILES replaces strings in PHPFILES with translations from MOFILE' );
|
|
}
|
|
|
|
public function cli() {
|
|
global $argv, $commands;
|
|
if ( count( $argv ) < 4 || ! in_array( $argv[1], array_keys( $this->commands ) ) ) {
|
|
$this->usage();
|
|
exit( 1 );
|
|
}
|
|
call_user_func_array( array( $this, $this->commands[ $argv[1] ] ), array_slice( $argv, 2 ) );
|
|
}
|
|
}
|
|
|
|
// Run the CLI only if the file wasn't included.
|
|
$included_files = get_included_files();
|
|
if ( __FILE__ == $included_files[0] ) {
|
|
error_reporting( E_ALL );
|
|
$not_gettexted = new NotGettexted;
|
|
$not_gettexted->cli();
|
|
}
|