diff --git a/tools/i18n/add-textdomain.php b/tools/i18n/add-textdomain.php new file mode 100644 index 0000000000..f030415433 --- /dev/null +++ b/tools/i18n/add-textdomain.php @@ -0,0 +1,108 @@ +funcs = array_keys( $makepot->rules ); + } + + function usage() { + $usage = "Usage: php add-textdomain.php [-i] \n\nAdds the string as a last argument to all i18n function calls in \nand prints the modified php file on standard output.\n\nOptions:\n -i Modifies the PHP file in place, instead of printing it to standard output.\n"; + fwrite(STDERR, $usage); + exit(1); + } + + function process_token($token_text, $inplace) { + if ($inplace) + $this->modified_contents .= $token_text; + else + echo $token_text; + } + + + function process_file($domain, $source_filename, $inplace) { + + $this->modified_contents = ''; + $domain = addslashes($domain); + + $source = file_get_contents($source_filename); + $tokens = token_get_all($source); + + $in_func = false; + $args_started = false; + $parens_balance = 0; + $found_domain = false; + + foreach($tokens as $token) { + $string_success = false; + if (is_array($token)) { + list($id, $text) = $token; + if (T_STRING == $id && in_array($text, $this->funcs)) { + $in_func = true; + $parens_balance = 0; + $args_started = false; + $found_domain = false; + } elseif (T_CONSTANT_ENCAPSED_STRING == $id && ("'$domain'" == $text || "\"$domain\"" == $text)) { + if ($in_func && $args_started) { + $found_domain = true; + } + } + $token = $text; + } elseif ('(' == $token){ + $args_started = true; + ++$parens_balance; + } elseif (')' == $token) { + --$parens_balance; + if ($in_func && 0 == $parens_balance) { + $token = $found_domain? ')' : ", '$domain')"; + $in_func = false; + $args_started = false; + $found_domain = false; + } + } + $this->process_token($token, $inplace); + } + + if ($inplace) { + $f = fopen($source_filename, 'w'); + fwrite($f, $this->modified_contents); + fclose($f); + } + } +} + + +// run the CLI only if the file +// wasn't included +$included_files = get_included_files(); +if ($included_files[0] == __FILE__) { + $adddomain = new AddTextdomain; + + if (!isset($argv[1]) || !isset($argv[2])) { + $adddomain->usage(); + } + + $inplace = false; + if ('-i' == $argv[1]) { + $inplace = true; + if (!isset($argv[3])) $adddomain->usage(); + array_shift($argv); + } + + $adddomain->process_file($argv[1], $argv[2], $inplace); +} + +?> diff --git a/tools/i18n/extract.php b/tools/i18n/extract.php new file mode 100644 index 0000000000..f4316a1785 --- /dev/null +++ b/tools/i18n/extract.php @@ -0,0 +1,210 @@ +rules = $rules; + } + + function extract_from_directory( $dir, $excludes = array(), $includes = array(), $prefix = '' ) { + $old_cwd = getcwd(); + chdir( $dir ); + $translations = new Translations; + $file_names = (array) scandir( '.' ); + foreach ( $file_names as $file_name ) { + if ( '.' == $file_name || '..' == $file_name ) continue; + if ( preg_match( '/\.php$/', $file_name ) && $this->does_file_name_match( $prefix . $file_name, $excludes, $includes ) ) { + $translations->merge_originals_with( $this->extract_from_file( $file_name, $prefix ) ); + } + if ( is_dir( $file_name ) ) { + $translations->merge_originals_with( $this->extract_from_directory( $file_name, $excludes, $includes, $prefix . $file_name . '/' ) ); + } + } + chdir( $old_cwd ); + return $translations; + } + + function extract_from_file( $file_name, $prefix ) { + $code = file_get_contents( $file_name ); + return $this->extract_entries( $code, $prefix . $file_name ); + } + + function does_file_name_match( $path, $excludes, $includes ) { + if ( $includes ) { + $matched_any_include = false; + foreach( $includes as $include ) { + if ( preg_match( '|^'.$include.'$|', $path ) ) { + $matched_any_include = true; + break; + } + } + if ( !$matched_any_include ) return false; + } + if ( $excludes ) { + foreach( $excludes as $exclude ) { + if ( preg_match( '|^'.$exclude.'$|', $path ) ) { + return false; + } + } + } + return true; + } + + function entry_from_call( $call, $file_name ) { + $rule = isset( $this->rules[$call['name']] )? $this->rules[$call['name']] : null; + if ( !$rule ) return null; + $entry = new Translation_Entry; + $multiple = array(); + $complete = false; + for( $i = 0; $i < count( $rule ); ++$i ) { + if ( $rule[$i] && ( !isset( $call['args'][$i] ) || !is_string( $call['args'][$i] ) || '' == $call['args'][$i] ) ) return false; + switch( $rule[$i] ) { + case 'string': + if ( $complete ) { + $multiple[] = $entry; + $entry = new Translation_Entry; + $complete = false; + } + $entry->singular = $call['args'][$i]; + $complete = true; + break; + case 'singular': + if ( $complete ) { + $multiple[] = $entry; + $entry = new Translation_Entry; + $complete = false; + } + $entry->singular = $call['args'][$i]; + $entry->is_plural = true; + break; + case 'plural': + $entry->plural = $call['args'][$i]; + $entry->is_plural = true; + $complete = true; + break; + case 'context': + $entry->context = $call['args'][$i]; + foreach( $multiple as &$single_entry ) { + $single_entry->context = $entry->context; + } + break; + } + } + if ( isset( $call['line'] ) && $call['line'] ) { + $references = array( $file_name . ':' . $call['line'] ); + $entry->references = $references; + foreach( $multiple as &$single_entry ) { + $single_entry->references = $references; + } + } + if ( isset( $call['comment'] ) && $call['comment'] ) { + $comments = rtrim( $call['comment'] ) . "\n"; + $entry->extracted_comments = $comments; + foreach( $multiple as &$single_entry ) { + $single_entry->extracted_comments = $comments; + } + } + if ( $multiple && $entry ) { + $multiple[] = $entry; + return $multiple; + } + + return $entry; + } + + function extract_entries( $code, $file_name ) { + $translations = new Translations; + $function_calls = $this->find_function_calls( array_keys( $this->rules ), $code ); + foreach( $function_calls as $call ) { + $entry = $this->entry_from_call( $call, $file_name ); + if ( is_array( $entry ) ) + foreach( $entry as $single_entry ) + $translations->add_entry_or_merge( $single_entry ); + elseif ( $entry) + $translations->add_entry_or_merge( $entry ); + } + return $translations; + } + + /** + * Finds all function calls in $code and returns an array with an associative array for each function: + * - name - name of the function + * - args - array for the function arguments. Each string literal is represented by itself, other arguments are represented by null. + * - line - line number + */ + function find_function_calls( $function_names, $code ) { + $tokens = token_get_all( $code ); + $function_calls = array(); + $latest_comment = false; + $in_func = false; + foreach( $tokens as $token ) { + $id = $text = null; + if ( is_array( $token ) ) list( $id, $text, $line ) = $token; + if ( T_WHITESPACE == $id ) continue; + if ( T_STRING == $id && in_array( $text, $function_names ) && !$in_func ) { + $in_func = true; + $paren_level = -1; + $args = array(); + $func_name = $text; + $func_line = $line; + $func_comment = $latest_comment? $latest_comment : ''; + + $just_got_into_func = true; + $latest_comment = false; + continue; + } + if ( T_COMMENT == $id ) { + $text = trim( preg_replace( '%^/\*|//%', '', preg_replace( '%\*/$%', '', $text ) ) ); + if ( 0 === stripos( $text, $this->comment_prefix ) ) { + $latest_comment = $text; + } + } + if ( !$in_func ) continue; + if ( '(' == $token ) { + $paren_level++; + if ( 0 == $paren_level ) { // start of first argument + $just_got_into_func = false; + $current_argument = null; + $current_argument_is_just_literal = true; + } + continue; + } + if ( $just_got_into_func ) { + // there wasn't a opening paren just after the function name -- this means it is not a function + $in_func = false; + $just_got_into_func = false; + } + if ( ')' == $token ) { + if ( 0 == $paren_level ) { + $in_func = false; + $args[] = $current_argument; + $call = array( 'name' => $func_name, 'args' => $args, 'line' => $func_line ); + if ( $func_comment ) $call['comment'] = $func_comment; + $function_calls[] = $call; + } + $paren_level--; + continue; + } + if ( ',' == $token && 0 == $paren_level ) { + $args[] = $current_argument; + $current_argument = null; + $current_argument_is_just_literal = true; + continue; + } + if ( T_CONSTANT_ENCAPSED_STRING == $id && $current_argument_is_just_literal ) { + // we can use eval safely, because we are sure $text is just a string literal + eval('$current_argument = '.$text.';' ); + continue; + } + $current_argument_is_just_literal = false; + $current_argument = null; + } + return $function_calls; + } +} diff --git a/tools/i18n/makepot.php b/tools/i18n/makepot.php new file mode 100644 index 0000000000..bf4b528485 --- /dev/null +++ b/tools/i18n/makepot.php @@ -0,0 +1,571 @@ + array('string'), + '__' => array('string'), + '_e' => array('string'), + '_c' => array('string'), + '_n' => array('singular', 'plural'), + '_n_noop' => array('singular', 'plural'), + '_nc' => array('singular', 'plural'), + '__ngettext' => array('singular', 'plural'), + '__ngettext_noop' => array('singular', 'plural'), + '_x' => array('string', 'context'), + '_ex' => array('string', 'context'), + '_nx' => array('singular', 'plural', null, 'context'), + '_nx_noop' => array('singular', 'plural', 'context'), + '_n_js' => array('singular', 'plural'), + '_nx_js' => array('singular', 'plural', 'context'), + 'esc_attr__' => array('string'), + 'esc_html__' => array('string'), + 'esc_attr_e' => array('string'), + 'esc_html_e' => array('string'), + 'esc_attr_x' => array('string', 'context'), + 'esc_html_x' => array('string', 'context'), + 'comments_number_link' => array('string', 'singular', 'plural'), + ); + + var $ms_files = array( 'ms-.*', '.*/ms-.*', '.*/my-.*', 'wp-activate\.php', 'wp-signup\.php', 'wp-admin/network\.php', 'wp-admin/includes/ms\.php', 'wp-admin/network/.*\.php', 'wp-admin/includes/class-wp-ms.*' ); + + var $temp_files = array(); + + var $meta = array( + 'default' => array( + 'from-code' => 'utf-8', + 'msgid-bugs-address' => 'http://wppolyglots.wordpress.com', + 'language' => 'php', + 'add-comments' => 'translators', + 'comments' => "Copyright (C) {year} {package-name}\nThis file is distributed under the same license as the {package-name} package.", + ), + 'generic' => array(), + 'wp-frontend' => array( + 'description' => 'Translation of frontend strings in WordPress {version}', + 'copyright-holder' => 'WordPress', + 'package-name' => 'WordPress', + 'package-version' => '{version}', + ), + 'wp-admin' => array( + 'description' => 'Translation of site admin strings in WordPress {version}', + 'copyright-holder' => 'WordPress', + 'package-name' => 'WordPress', + 'package-version' => '{version}', + ), + 'wp-network-admin' => array( + 'description' => 'Translation of network admin strings in WordPress {version}', + 'copyright-holder' => 'WordPress', + 'package-name' => 'WordPress', + 'package-version' => '{version}', + ), + 'wp-core' => array( + 'description' => 'Translation of WordPress {version}', + 'copyright-holder' => 'WordPress', + 'package-name' => 'WordPress', + 'package-version' => '{version}', + ), + 'wp-ms' => array( + 'description' => 'Translation of multisite strings in WordPress {version}', + 'copyright-holder' => 'WordPress', + 'package-name' => 'WordPress', + 'package-version' => '{version}', + ), + 'wp-tz' => array( + 'description' => 'Translation of timezone strings in WordPress {version}', + 'copyright-holder' => 'WordPress', + 'package-name' => 'WordPress', + 'package-version' => '{version}', + ), + 'bb' => array( + 'description' => 'Translation of bbPress', + 'copyright-holder' => 'bbPress', + 'package-name' => 'bbPress', + ), + 'wp-plugin' => array( + 'description' => 'Translation of the WordPress plugin {name} {version} by {author}', + 'msgid-bugs-address' => 'http://wordpress.org/tag/{slug}', + 'copyright-holder' => '{author}', + 'package-name' => '{name}', + 'package-version' => '{version}', + ), + 'wp-theme' => array( + 'description' => 'Translation of the WordPress theme {name} {version} by {author}', + 'msgid-bugs-address' => 'http://wordpress.org/tags/{slug}', + 'copyright-holder' => '{author}', + 'package-name' => '{name}', + 'package-version' => '{version}', + 'comments' => 'Copyright (C) {year} {author}\nThis file is distributed under the same license as the {package-name} package.', + ), + 'bp' => array( + 'description' => 'Translation of BuddyPress', + 'copyright-holder' => 'BuddyPress', + 'package-name' => 'BuddyPress', + ), + 'glotpress' => array( + 'description' => 'Translation of GlotPress', + 'copyright-holder' => 'GlotPress', + 'package-name' => 'GlotPress', + ), + 'wporg-bb-forums' => array( + 'description' => 'WordPress.org International Forums', + 'copyright-holder' => 'WordPress', + 'package-name' => 'WordPress.org International Forums', + ), + 'rosetta' => array( + 'description' => 'Rosetta (.wordpress.org locale sites)', + 'copyright-holder' => 'WordPress', + 'package-name' => 'Rosetta', + ), + ); + + function __construct($deprecated = true) { + $this->extractor = new StringExtractor( $this->rules ); + } + + function __destruct() { + foreach ( $this->temp_files as $temp_file ) + unlink( $temp_file ); + } + + function tempnam( $file ) { + $tempnam = tempnam( sys_get_temp_dir(), $file ); + $this->temp_files[] = $tempnam; + return $tempnam; + } + + function realpath_missing($path) { + return realpath(dirname($path)).DIRECTORY_SEPARATOR.basename($path); + } + + function xgettext($project, $dir, $output_file, $placeholders = array(), $excludes = array(), $includes = array()) { + $meta = array_merge( $this->meta['default'], $this->meta[$project] ); + $placeholders = array_merge( $meta, $placeholders ); + $meta['output'] = $this->realpath_missing( $output_file ); + $placeholders['year'] = date( 'Y' ); + $placeholder_keys = array_map( create_function( '$x', 'return "{".$x."}";' ), array_keys( $placeholders ) ); + $placeholder_values = array_values( $placeholders ); + foreach($meta as $key => $value) { + $meta[$key] = str_replace($placeholder_keys, $placeholder_values, $value); + } + + $originals = $this->extractor->extract_from_directory( $dir, $excludes, $includes ); + $pot = new PO; + $pot->entries = $originals->entries; + + $pot->set_header( 'Project-Id-Version', $meta['package-name'].' '.$meta['package-version'] ); + $pot->set_header( 'Report-Msgid-Bugs-To', $meta['msgid-bugs-address'] ); + $pot->set_header( 'POT-Creation-Date', gmdate( 'Y-m-d H:i:s+00:00' ) ); + $pot->set_header( 'MIME-Version', '1.0' ); + $pot->set_header( 'Content-Type', 'text/plain; charset=UTF-8' ); + $pot->set_header( 'Content-Transfer-Encoding', '8bit' ); + $pot->set_header( 'PO-Revision-Date', date( 'Y') . '-MO-DA HO:MI+ZONE' ); + $pot->set_header( 'Last-Translator', 'FULL NAME ' ); + $pot->set_header( 'Language-Team', 'LANGUAGE ' ); + $pot->set_comment_before_headers( $meta['comments'] ); + $pot->export_to_file( $output_file ); + return true; + } + + function wp_generic($dir, $args) { + $defaults = array( + 'project' => 'wp-core', + 'output' => null, + 'default_output' => 'wordpress.pot', + 'includes' => array(), + 'excludes' => array_merge( + array('wp-admin/includes/continents-cities\.php', 'wp-content/themes/twenty.*', ), + $this->ms_files + ), + 'extract_not_gettexted' => false, + 'not_gettexted_files_filter' => false, + ); + $args = array_merge( $defaults, $args ); + extract( $args ); + $placeholders = array(); + if ( $wp_version = $this->wp_version( $dir ) ) + $placeholders['version'] = $wp_version; + else + return false; + $output = is_null( $output )? $default_output : $output; + $res = $this->xgettext( $project, $dir, $output, $placeholders, $excludes, $includes ); + if ( !$res ) return false; + + if ( $extract_not_gettexted ) { + $old_dir = getcwd(); + $output = realpath( $output ); + chdir( $dir ); + $php_files = NotGettexted::list_php_files('.'); + $php_files = array_filter( $php_files, $not_gettexted_files_filter ); + $not_gettexted = new NotGettexted; + $res = $not_gettexted->command_extract( $output, $php_files ); + chdir( $old_dir ); + /* Adding non-gettexted strings can repeat some phrases */ + $output_shell = escapeshellarg( $output ); + system( "msguniq --use-first $output_shell -o $output_shell" ); + } + return $res; + } + + function wp_core($dir, $output) { + if ( file_exists( "$dir/wp-admin/user/about.php" ) ) return false; + + return $this->wp_generic( $dir, array( + 'project' => 'wp-core', 'output' => $output, + 'extract_not_gettexted' => true, + 'not_gettexted_files_filter' => array( &$this, 'is_not_ms_file' ), + ) ); + } + + function wp_frontend($dir, $output) { + if ( ! file_exists( "$dir/wp-admin/user/about.php" ) ) return false; + + return $this->wp_generic( $dir, array( + 'project' => 'wp-frontend', 'output' => $output, + 'includes' => array(), 'excludes' => array( 'wp-admin/.*', 'wp-content/themes/.*' ), + 'default_output' => 'wordpress.pot', + ) ); + } + + function wp_admin($dir, $output) { + if ( ! file_exists( "$dir/wp-admin/user/about.php" ) ) return false; + + $frontend_pot = $this->tempnam( 'frontend.pot' ); + if ( false === $frontend_pot ) return false; + + $frontend_result = $this->wp_frontend( $dir, $frontend_pot ); + if ( ! $frontend_result ) + return false; + + $result = $this->wp_generic( $dir, array( + 'project' => 'wp-admin', 'output' => $output, + 'includes' => array( 'wp-admin/.*' ), 'excludes' => array( 'wp-admin/includes/continents-cities\.php', 'wp-admin/network/.*', 'wp-admin/network.php' ), + 'default_output' => 'wordpress-admin.pot', + ) ); + + if ( ! $result ) + return false; + + $potextmeta = new PotExtMeta; + $result = $potextmeta->append( "$dir/wp-content/plugins/akismet/akismet.php", $output ); + if ( ! $result ) + return false; + $result = $potextmeta->append( "$dir/wp-content/plugins/hello.php", $output ); + if ( ! $result ) + return false; + /* Adding non-gettexted strings can repeat some phrases */ + $output_shell = escapeshellarg($output); + system("msguniq $output_shell -o $output_shell"); + + $common_pot = $this->tempnam( 'common.pot' ); + if ( ! $common_pot ) + return false; + $admin_pot = realpath( is_null( $output ) ? 'wordpress-admin.pot' : $output ); + system( "msgcat --more-than=1 --use-first $frontend_pot $admin_pot > $common_pot" ); + system( "msgcat -u --use-first $admin_pot $common_pot -o $admin_pot" ); + return true; + } + + function wp_network_admin($dir, $output) { + if ( ! file_exists( "$dir/wp-admin/user/about.php" ) ) return false; + + $frontend_pot = $this->tempnam( 'frontend.pot' ); + if ( false === $frontend_pot ) return false; + + $frontend_result = $this->wp_frontend( $dir, $frontend_pot ); + if ( ! $frontend_result ) + return false; + + $admin_pot = $this->tempnam( 'admin.pot' ); + if ( false === $admin_pot ) return false; + + $admin_result = $this->wp_admin( $dir, $admin_pot ); + if ( ! $admin_result ) + return false; + + $result = $this->wp_generic( $dir, array( + 'project' => 'wp-network-admin', 'output' => $output, + 'includes' => array( 'wp-admin/network/.*', 'wp-admin/network.php' ), 'excludes' => array(), + 'default_output' => 'wordpress-admin-network.pot', + ) ); + + if ( ! $result ) { + return false; + } + + $common_pot = $this->tempnam( 'common.pot' ); + if ( ! $common_pot ) + return false; + + $net_admin_pot = realpath( is_null( $output ) ? 'wordpress-network-admin.pot' : $output ); + system( "msgcat --more-than=1 --use-first $frontend_pot $admin_pot $net_admin_pot > $common_pot" ); + system( "msgcat -u --use-first $net_admin_pot $common_pot -o $net_admin_pot" ); + return true; + } + + function wp_ms($dir, $output) { + if ( file_exists( "$dir/wp-admin/user/about.php" ) ) return false; + if ( !is_file("$dir/wp-admin/ms-users.php") ) return false; + $core_pot = $this->tempnam( 'wordpress.pot' ); + if ( false === $core_pot ) return false; + $core_result = $this->wp_core( $dir, $core_pot ); + if ( ! $core_result ) + return false; + $ms_result = $this->wp_generic( $dir, array( + 'project' => 'wp-ms', 'output' => $output, + 'includes' => $this->ms_files, 'excludes' => array(), + 'default_output' => 'wordpress-ms.pot', + 'extract_not_gettexted' => true, + 'not_gettexted_files_filter' => array( &$this, 'is_ms_file' ), + ) ); + if ( !$ms_result ) { + return false; + } + $common_pot = $this->tempnam( 'common.pot' ); + if ( ! $common_pot ) + return false; + $ms_pot = realpath( is_null( $output )? 'wordpress-ms.pot' : $output ); + system( "msgcat --more-than=1 --use-first $core_pot $ms_pot > $common_pot" ); + system( "msgcat -u --use-first $ms_pot $common_pot -o $ms_pot" ); + return true; + } + + function wp_tz($dir, $output) { + $continents_path = 'wp-admin/includes/continents-cities.php'; + if ( !file_exists( "$dir/$continents_path" ) ) return false; + return $this->wp_generic( $dir, array( + 'project' => 'wp-tz', 'output' => $output, + 'includes' => array($continents_path), 'excludes' => array(), + 'default_output' => 'wordpress-continents-cities.pot', + ) ); + } + + function wp_version($dir) { + $version_php = $dir.'/wp-includes/version.php'; + if ( !is_readable( $version_php ) ) return false; + return preg_match( '/\$wp_version\s*=\s*\'(.*?)\';/', file_get_contents( $version_php ), $matches )? $matches[1] : false; + } + + + function mu($dir, $output) { + $placeholders = array(); + if (preg_match('/\$wpmu_version\s*=\s*\'(.*?)\';/', file_get_contents($dir.'/wp-includes/version.php'), $matches)) { + $placeholders['version'] = $matches[1]; + } + $output = is_null($output)? 'wordpress.pot' : $output; + return $this->xgettext('wp', $dir, $output, $placeholders); + } + + + function bb($dir, $output) { + $placeholders = array(); + $output = is_null($output)? 'bbpress.pot' : $output; + return $this->xgettext('bb', $dir, $output, $placeholders); + + } + + function get_first_lines($filename, $lines = 30) { + $extf = fopen($filename, 'r'); + if (!$extf) return false; + $first_lines = ''; + foreach(range(1, $lines) as $x) { + $line = fgets($extf); + if (feof($extf)) break; + if (false === $line) { + return false; + } + $first_lines .= $line; + } + return $first_lines; + } + + + function get_addon_header($header, &$source) { + if (preg_match('|'.$header.':(.*)$|mi', $source, $matches)) + return trim($matches[1]); + else + return false; + } + + function generic($dir, $output) { + $output = is_null($output)? "generic.pot" : $output; + return $this->xgettext('generic', $dir, $output, array()); + } + + function guess_plugin_slug($dir) { + if ('trunk' == basename($dir)) { + $slug = basename(dirname($dir)); + } elseif (in_array(basename(dirname($dir)), array('branches', 'tags'))) { + $slug = basename(dirname(dirname($dir))); + } else { + $slug = basename($dir); + } + return $slug; + } + + function wp_plugin($dir, $output, $slug = null) { + $placeholders = array(); + // guess plugin slug + if (is_null($slug)) { + $slug = $this->guess_plugin_slug($dir); + } + $main_file = $dir.'/'.$slug.'.php'; + $source = $this->get_first_lines($main_file, $this->max_header_lines); + + $placeholders['version'] = $this->get_addon_header('Version', $source); + $placeholders['author'] = $this->get_addon_header('Author', $source); + $placeholders['name'] = $this->get_addon_header('Plugin Name', $source); + $placeholders['slug'] = $slug; + + $output = is_null($output)? "$slug.pot" : $output; + $res = $this->xgettext('wp-plugin', $dir, $output, $placeholders); + if (!$res) return false; + $potextmeta = new PotExtMeta; + $res = $potextmeta->append($main_file, $output); + /* Adding non-gettexted strings can repeat some phrases */ + $output_shell = escapeshellarg($output); + system("msguniq $output_shell -o $output_shell"); + return $res; + } + + function wp_theme($dir, $output, $slug = null) { + $placeholders = array(); + // guess plugin slug + if (is_null($slug)) { + $slug = $this->guess_plugin_slug($dir); + } + $main_file = $dir.'/style.css'; + $source = $this->get_first_lines($main_file, $this->max_header_lines); + + $placeholders['version'] = $this->get_addon_header('Version', $source); + $placeholders['author'] = $this->get_addon_header('Author', $source); + $placeholders['name'] = $this->get_addon_header('Theme Name', $source); + $placeholders['slug'] = $slug; + + $license = $this->get_addon_header( 'License', $source ); + if ( $license ) + $this->meta['wp-theme']['comments'] = "Copyright (C) {year} {author}\nThis file is distributed under the {$license}."; + else + $this->meta['wp-theme']['comments'] = "Copyright (C) {year} {author}\nThis file is distributed under the same license as the {package-name} package."; + + $output = is_null($output)? "$slug.pot" : $output; + $res = $this->xgettext('wp-theme', $dir, $output, $placeholders); + if (! $res ) + return false; + $potextmeta = new PotExtMeta; + $res = $potextmeta->append( $main_file, $output, array( 'Theme Name', 'Theme URI', 'Description', 'Author', 'Author URI' ) ); + if ( ! $res ) + return false; + // If we're dealing with a pre-3.4 default theme, don't extract page templates before 3.4. + $extract_templates = ! in_array( $slug, array( 'twentyten', 'twentyeleven', 'default', 'classic' ) ); + if ( ! $extract_templates ) { + $wp_dir = dirname( dirname( dirname( $dir ) ) ); + $extract_templates = file_exists( "$wp_dir/wp-admin/user/about.php" ) || ! file_exists( "$wp_dir/wp-load.php" ); + } + if ( $extract_templates ) { + $res = $potextmeta->append( $dir, $output, array( 'Template Name' ) ); + if ( ! $res ) + return false; + $files = scandir( $dir ); + foreach ( $files as $file ) { + if ( '.' == $file[0] || 'CVS' == $file ) + continue; + if ( is_dir( $dir . '/' . $file ) ) { + $res = $potextmeta->append( $dir . '/' . $file, $output, array( 'Template Name' ) ); + if ( ! $res ) + return false; + } + } + } + /* Adding non-gettexted strings can repeat some phrases */ + $output_shell = escapeshellarg($output); + system("msguniq $output_shell -o $output_shell"); + return $res; + } + + function bp($dir, $output) { + $output = is_null($output)? "buddypress.pot" : $output; + return $this->xgettext('bp', $dir, $output, array(), array('bp-forums/bbpress/.*')); + } + + function glotpress( $dir, $output ) { + $output = is_null( $output ) ? "glotpress.pot" : $output; + return $this->xgettext( 'glotpress', $dir, $output ); + } + + function wporg_bb_forums( $dir, $output ) { + $output = is_null( $output ) ? 'wporg.pot' : $output; + return $this->xgettext( 'wporg-bb-forums', $dir, $output, array(), array( + 'bb-plugins/elfakismet/.*', + 'bb-plugins/support-forum/.*', + ) ); + } + + function rosetta( $dir, $output ) { + $output = is_null( $output )? 'rosetta.pot' : $output; + return $this->xgettext( 'rosetta', $dir, $output, array(), array(), array( + 'mu-plugins/rosetta.*\.php', + 'mu-plugins/rosetta/[^/]+\.php', + 'mu-plugins/rosetta/tmpl/.*\.php', + 'themes/rosetta/.*\.php', + ) ); + } + + function is_ms_file( $file_name ) { + $is_ms_file = false; + $prefix = substr( $file_name, 0, 2 ) === './'? '\./' : ''; + foreach( $this->ms_files as $ms_file ) + if ( preg_match( '|^'.$prefix.$ms_file.'$|', $file_name ) ) { + $is_ms_file = true; + break; + } + return $is_ms_file; + } + + function is_not_ms_file( $file_name ) { + return !$this->is_ms_file( $file_name ); + } +} + +// run the CLI only if the file +// wasn't included +$included_files = get_included_files(); +if ($included_files[0] == __FILE__) { + $makepot = new MakePOT; + if ((3 == count($argv) || 4 == count($argv)) && in_array($method = str_replace('-', '_', $argv[1]), get_class_methods($makepot))) { + $res = call_user_func(array(&$makepot, $method), realpath($argv[2]), isset($argv[3])? $argv[3] : null); + if (false === $res) { + fwrite(STDERR, "Couldn't generate POT file!\n"); + } + } else { + $usage = "Usage: php makepot.php PROJECT DIRECTORY [OUTPUT]\n\n"; + $usage .= "Generate POT file from the files in DIRECTORY [OUTPUT]\n"; + $usage .= "Available projects: ".implode(', ', $makepot->projects)."\n"; + fwrite(STDERR, $usage); + exit(1); + } +} diff --git a/tools/i18n/not-gettexted.php b/tools/i18n/not-gettexted.php new file mode 100644 index 0000000000..c196e487b1 --- /dev/null +++ b/tools/i18n/not-gettexted.php @@ -0,0 +1,234 @@ + 'command_extract', 'replace' => 'command_replace' ); + + + function logmsg() { + $args = func_get_args(); + if ($this->enable_logging) error_log(implode(' ', $args)); + } + + function stderr($msg, $nl=true) { + fwrite(STDERR, $msg.($nl? "\n" : "")); + } + + function cli_die($msg) { + $this->stderr($msg); + exit(1); + } + + function unchanged_token($token, $s='') { + return is_array($token)? $token[1] : $token; + } + + function ignore_token($token, $s='') { + return ''; + } + + 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 (is_dir($full_item)) + $files += array_merge($files, NotGettexted::list_php_files($full_item, $files)); + } + return $files; + } + + + function make_string_aggregator($global_array_name, $filename) { + $a = $global_array_name; + return create_function('$string, $comment_id, $line_number', 'global $'.$a.'; $'.$a.'[] = array($string, $comment_id, '.var_export($filename, true).', $line_number);'); + } + + function make_mo_replacer($global_mo_name) { + $m = $global_mo_name; + return create_function('$token, $string', 'global $'.$m.'; return var_export($'.$m.'->translate($string), true);'); + } + + 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; + } + } else if (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; + } + } else if (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; + } + + + 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; + } + + 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; + } + + 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'); + } + + 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 ($included_files[0] == __FILE__) { + error_reporting(E_ALL); + $not_gettexted = new NotGettexted; + $not_gettexted->cli(); +} + +?> diff --git a/tools/i18n/pot-ext-meta.php b/tools/i18n/pot-ext-meta.php new file mode 100644 index 0000000000..48efcc5af1 --- /dev/null +++ b/tools/i18n/pot-ext-meta.php @@ -0,0 +1,76 @@ +headers as $header) { + $string = MakePOT::get_addon_header($header, $source); + if (!$string) continue; + $args = array( + 'singular' => $string, + 'extracted_comments' => $header.' of the plugin/theme', + ); + $entry = new Translation_Entry($args); + $pot .= "\n".PO::export_entry($entry)."\n"; + } + return $pot; + } + + function append( $ext_filename, $pot_filename, $headers = null ) { + if ( $headers ) + $this->headers = (array) $headers; + if ( is_dir( $ext_filename ) ) { + $pot = implode('', array_map(array(&$this, 'load_from_file'), glob("$ext_filename/*.php"))); + } else { + $pot = $this->load_from_file($ext_filename); + } + $potf = '-' == $pot_filename? STDOUT : fopen($pot_filename, 'a'); + if (!$potf) return false; + fwrite($potf, $pot); + if ('-' != $pot_filename) fclose($potf); + return true; + } +} + +$included_files = get_included_files(); +if ($included_files[0] == __FILE__) { + ini_set('display_errors', 1); + $potextmeta = new PotExtMeta; + if (!isset($argv[1])) { + $potextmeta->usage(); + } + $potextmeta->append( $argv[1], isset( $argv[2] ) ? $argv[2] : '-', isset( $argv[3] ) ? $argv[3] : null ); +} + +?> diff --git a/tools/i18n/t/ExtractTest.php b/tools/i18n/t/ExtractTest.php new file mode 100644 index 0000000000..c1deea35c0 --- /dev/null +++ b/tools/i18n/t/ExtractTest.php @@ -0,0 +1,160 @@ +extractor = new StringExtractor; + $this->extractor->rules = array( + '__' => array('string'), + ); + } + + function test_with_just_a_string() { + $expected = new Translation_Entry( array( 'singular' => 'baba', 'references' => array('baba.php:1') ) ); + $result = $this->extractor->extract_entries('', 'baba.php' ); + $this->assertEquals( $expected, $result->entries['baba'] ); + } + + function test_entry_from_call_simple() { + $entry = $this->extractor->entry_from_call( array( 'name' => '__', 'args' => array('baba') ), 'baba.php' ); + $this->assertEquals( $entry, new Translation_Entry( array( 'singular' => 'baba' ) ) ); + } + + function test_entry_from_call_nonexisting_function() { + $entry = $this->extractor->entry_from_call( array( 'name' => 'f', 'args' => array('baba') ), 'baba.php' ); + $this->assertEquals( $entry, null ); + } + + function test_entry_from_call_too_few_args() { + $entry = $this->extractor->entry_from_call( array( 'name' => '__', 'args' => array() ), 'baba.php' ); + $this->assertEquals( $entry, null ); + } + + function test_entry_from_call_non_expected_null_arg() { + $this->extractor->rules = array( '_nx' => array( 'singular', 'plural', 'context' ) ); + $entry = $this->extractor->entry_from_call( array( 'name' => '_nx', 'args' => array('%s baba', null, 'noun') ), 'baba.php' ); + $this->assertEquals( $entry, null ); + } + + function test_entry_from_call_more_args_should_be_ok() { + $this->extractor->rules = array( '__' => array('string') ); + $entry = $this->extractor->entry_from_call( array( 'name' => '__', 'args' => array('baba', 5, 'pijo', null) ), 'baba.php' ); + $this->assertEquals( $entry, new Translation_Entry( array( 'singular' => 'baba' ) ) ); + } + + + function test_entry_from_call_context() { + $this->extractor->rules = array( '_x' => array( 'string', 'context' ) ); + $entry = $this->extractor->entry_from_call( array( 'name' => '_x', 'args' => array('baba', 'noun') ), 'baba.php' ); + $this->assertEquals( $entry, new Translation_Entry( array( 'singular' => 'baba', 'context' => 'noun' ) ) ); + } + + function test_entry_from_call_plural() { + $this->extractor->rules = array( '_n' => array( 'singular', 'plural' ) ); + $entry = $this->extractor->entry_from_call( array( 'name' => '_n', 'args' => array('%s baba', '%s babas') ), 'baba.php' ); + $this->assertEquals( $entry, new Translation_Entry( array( 'singular' => '%s baba', 'plural' => '%s babas' ) ) ); + } + + function test_entry_from_call_plural_and_context() { + $this->extractor->rules = array( '_nx' => array( 'singular', 'plural', 'context' ) ); + $entry = $this->extractor->entry_from_call( array( 'name' => '_nx', 'args' => array('%s baba', '%s babas', 'noun') ), 'baba.php' ); + $this->assertEquals( $entry, new Translation_Entry( array( 'singular' => '%s baba', 'plural' => '%s babas', 'context' => 'noun' ) ) ); + } + + function test_entry_from_call_extracted_comment() { + $entry = $this->extractor->entry_from_call( array( 'name' => '__', 'args' => array('baba'), 'comment' => 'translators: give me back my pants!' ), 'baba.php' ); + $this->assertEquals( $entry, new Translation_Entry( array( 'singular' => 'baba', 'extracted_comments' => "translators: give me back my pants!\n" ) ) ); + } + + function test_entry_from_call_line_number() { + $entry = $this->extractor->entry_from_call( array( 'name' => '__', 'args' => array('baba'), 'line' => 10 ), 'baba.php' ); + $this->assertEquals( $entry, new Translation_Entry( array( 'singular' => 'baba', 'references' => array('baba.php:10') ) ) ); + } + + function test_entry_from_call_zero() { + $entry = $this->extractor->entry_from_call( array( 'name' => '__', 'args' => array('0') ), 'baba.php' ); + $this->assertEquals( $entry, new Translation_Entry( array( 'singular' => '0' ) ) ); + } + + function test_entry_from_call_multiple() { + $this->extractor->rules = array( 'c' => array( 'string', 'singular', 'plural' ) ); + $entries = $this->extractor->entry_from_call( array( 'name' => 'c', 'args' => array('baba', 'dyado', 'dyados') ), 'baba.php' ); + $this->assertEquals( array( + new Translation_Entry( array( 'singular' => 'baba' ) ), new Translation_Entry( array( 'singular' => 'dyado', 'plural' => 'dyados' ) ) ), $entries ); + } + + function test_entry_from_call_multiple_first_plural_then_two_strings() { + $this->extractor->rules = array( 'c' => array( 'singular', 'plural', null, 'string', 'string' ) ); + $entries = $this->extractor->entry_from_call( array( 'name' => 'c', 'args' => array('dyado', 'dyados', 'baba', 'foo', 'bar') ), 'baba.php' ); + $this->assertEquals( array( + new Translation_Entry( array( 'singular' => 'dyado', 'plural' => 'dyados' ) ), + new Translation_Entry( array( 'singular' => 'foo' ) ), + new Translation_Entry( array( 'singular' => 'bar' ) ) ), $entries ); + } + + function test_find_function_calls_one_arg_literal() { + $this->assertEquals( array( array( 'name' => '__', 'args' => array( 'baba' ), 'line' => 1 ) ), $this->extractor->find_function_calls( array('__'), '' ) ); + } + + function test_find_function_calls_one_arg_zero() { + $this->assertEquals( array( array( 'name' => '__', 'args' => array( '0' ), 'line' => 1 ) ), $this->extractor->find_function_calls( array('__'), '' ) ); + } + + function test_find_function_calls_one_arg_non_literal() { + $this->assertEquals( array( array( 'name' => '__', 'args' => array( null ), 'line' => 1 ) ), $this->extractor->find_function_calls( array('__'), '' ) ); + } + + function test_find_function_calls_shouldnt_be_mistaken_by_a_class() { + $this->assertEquals( array(), $this->extractor->find_function_calls( array('__'), 'assertEquals( array( array( 'name' => 'f', 'args' => array( null, "baba" ), 'line' => 1 ) ), $this->extractor->find_function_calls( array('f'), 'assertEquals( array( array( 'name' => 'f', 'args' => array( null, "baba", null ), 'line' => 1 ) ), $this->extractor->find_function_calls( array('f'), 'assertEquals( array( array( 'name' => 'f', 'args' => array( null ), 'line' => 1 ) ), $this->extractor->find_function_calls( array('f'), 'assertEquals( array( array( 'name' => 'f', 'args' => array( null ), 'line' => 1 ) ), $this->extractor->find_function_calls( array('f'), 'assertEquals( array( array( 'name' => 'f', 'args' => array( "baba", null ), 'line' => 1 ) ), $this->extractor->find_function_calls( array('f'), 'assertEquals( array( array( 'name' => 'f', 'args' => array( null, "baba" ), 'line' => 1 ) ), $this->extractor->find_function_calls( array('f'), 'assertEquals( + array( array( 'name' => 'f', 'args' => array( 'baba' ), 'line' => 1, 'comment' => 'translators: let your ears fly!' ) ), + $this->extractor->find_function_calls( array('f'), 'assertEquals( + array( array( 'name' => 'f', 'args' => array( 'baba' ), 'line' => 1, 'comment' => 'translators: let your ears fly!' ) ), + $this->extractor->find_function_calls( array('f'), 'assertEquals( + array( array( 'name' => 'f', 'args' => array( 'baba' ), 'line' => 1, 'comment' => 'translators: let your ears fly!' ) ), + $this->extractor->find_function_calls( array('f'), 'assertEquals( + array( array( 'name' => 'f', 'args' => array( 'baba' ), 'line' => 1, 'comment' => 'Translators: let your ears fly!' ) ), + $this->extractor->find_function_calls( array('f'), 'ng = new NotGettexted; + } + + function test_make_string_aggregator() { + global $baba; + $f = $this->ng->make_string_aggregator( 'baba', 'baba.php' ); + call_user_func( $f, 'x', 'y', 'z' ); + call_user_func( $f, 'a', 'b', 'c' ); + $this->assertEquals( array( array( 'x', 'y', 'baba.php', 'z'), array( 'a', 'b', 'baba.php', 'c' ) ), $baba ); + } + + function test_walk() { + $code = ' +'; + $tokens = token_get_all($code); + $this->assertEquals( '', $this->ng->walk_tokens( $tokens, array(&$this->ng, 'ignore_token'), array(&$this->ng, 'ignore_token') ) ); + $this->assertEquals( '"yes"\'We died %d times!\'', $this->ng->walk_tokens( $tokens, array(&$this->ng, 'unchanged_token'), array(&$this->ng, 'ignore_token') ) ); + $this->assertEquals( $code, $this->ng->walk_tokens( $tokens, array(&$this->ng, 'unchanged_token'), array(&$this->ng, 'unchanged_token') ) ); + $this->assertEquals( $code, $this->ng->walk_tokens( $tokens, array(&$this->ng, 'unchanged_token'), array(&$this->ng, 'unchanged_token') ) ); + } + + function test_replace() { + # copy to a new file, so that we don't corrupt the old one + copy( 'data/not-gettexted-0.php', 'data/not-gettexted-0-work.php' ); + $this->ng->command_replace( 'data/not-gettexted-0.mo', 'data/not-gettexted-0-work.php' ); + $this->assertEquals( file_get_contents( 'data/not-gettexted-0-result.php' ), file_get_contents( 'data/not-gettexted-0-work.php' ) ); + unlink( 'data/not-gettexted-0-work.php' ); + } +} \ No newline at end of file diff --git a/tools/i18n/t/data/not-gettexted-0-result.php b/tools/i18n/t/data/not-gettexted-0-result.php new file mode 100644 index 0000000000..c233bfcd86 --- /dev/null +++ b/tools/i18n/t/data/not-gettexted-0-result.php @@ -0,0 +1,24 @@ +wp-config.php file. I need this before we can get started. Need more help? We got it. You can create a wp-config.php file through a web interface, but this doesn\'t work for all server setups. The safest way is to manually create the file.

Create a Configuration File' /*/WP_I18N_CONFIG*/, $path.'setup-config.php'), /*WP_I18N_ERROR*/ 'Translation: WordPress › Error' /*/WP_I18N_ERROR*/); +} + +$wp_did_header = true; + +require_once( dirname(__FILE__) . '/wp-config.php'); + +wp(); + +require_once(ABSPATH . WPINC . '/template-loader.php'); + +endif; + +?> diff --git a/tools/i18n/t/data/not-gettexted-0-work.php b/tools/i18n/t/data/not-gettexted-0-work.php new file mode 100644 index 0000000000..c233bfcd86 --- /dev/null +++ b/tools/i18n/t/data/not-gettexted-0-work.php @@ -0,0 +1,24 @@ +wp-config.php file. I need this before we can get started. Need more help? We got it. You can create a wp-config.php file through a web interface, but this doesn\'t work for all server setups. The safest way is to manually create the file.

Create a Configuration File' /*/WP_I18N_CONFIG*/, $path.'setup-config.php'), /*WP_I18N_ERROR*/ 'Translation: WordPress › Error' /*/WP_I18N_ERROR*/); +} + +$wp_did_header = true; + +require_once( dirname(__FILE__) . '/wp-config.php'); + +wp(); + +require_once(ABSPATH . WPINC . '/template-loader.php'); + +endif; + +?> diff --git a/tools/i18n/t/data/not-gettexted-0.mo b/tools/i18n/t/data/not-gettexted-0.mo new file mode 100644 index 0000000000..4042fb05bb Binary files /dev/null and b/tools/i18n/t/data/not-gettexted-0.mo differ diff --git a/tools/i18n/t/data/not-gettexted-0.php b/tools/i18n/t/data/not-gettexted-0.php new file mode 100644 index 0000000000..4faaccd865 --- /dev/null +++ b/tools/i18n/t/data/not-gettexted-0.php @@ -0,0 +1,24 @@ +wp-config.php file. I need this before we can get started. Need more help? We got it. You can create a wp-config.php file through a web interface, but this doesn't work for all server setups. The safest way is to manually create the file.

Create a Configuration File" /*/WP_I18N_CONFIG*/, $path.'setup-config.php'), /*WP_I18N_ERROR*/ "WordPress › Error" /*/WP_I18N_ERROR*/); +} + +$wp_did_header = true; + +require_once( dirname(__FILE__) . '/wp-config.php'); + +wp(); + +require_once(ABSPATH . WPINC . '/template-loader.php'); + +endif; + +?> diff --git a/tools/i18n/t/data/not-gettexted-0.po b/tools/i18n/t/data/not-gettexted-0.po new file mode 100644 index 0000000000..606ac7e5c3 --- /dev/null +++ b/tools/i18n/t/data/not-gettexted-0.po @@ -0,0 +1,21 @@ +msgid "" +msgstr "" +"Project-Id-Version: \n" +"POT-Creation-Date: \n" +"PO-Revision-Date: 2008-04-22 19:23+0200\n" +"Last-Translator: \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=iso-8859-1\n" +"Content-Transfer-Encoding: 8bit\n" + +#. Not gettexted string WP_I18N_CONFIG +#: not-gettext-0.php:11 +msgid " There doesn't seem to be a wp-config.php file. I need this before we can get started. Need more help? We got it. You can create a wp-config.php file through a web interface, but this doesn't work for all server setups. The safest way is to manually create the file.

Create a Configuration File" +msgstr "Translation: There doesn't seem to be a wp-config.php file. I need this before we can get started. Need more help? We got it. You can create a wp-config.php file through a web interface, but this doesn't work for all server setups. The safest way is to manually create the file.

Create a Configuration File" + +#. Not gettexted string WP_I18N_ERROR +#: not-gettext-0.php:11 +msgid "WordPress › Error" +msgstr "Translation: WordPress › Error" +