Skip to content

Commit

Permalink
Merge branch 'master' into 53-generator
Browse files Browse the repository at this point in the history
  • Loading branch information
schlessera authored Jul 31, 2018
2 parents f1b6e32 + 1a15b15 commit b4dcde8
Show file tree
Hide file tree
Showing 6 changed files with 202 additions and 229 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@ Scans PHP and JavaScript files, as well as theme stylesheets for translatable st
By default, the following files and folders are ignored: node_modules, .git, .svn, .CVS, .hg, vendor.
Leading and trailing slashes are ignored, i.e. `/my/directory/` is the same as `my/directory`.

[--headers=<headers>]
Array in JSON format of custom headers which will be added to the POT file. Defaults to empty array.

[--skip-js]
Skips JavaScript string extraction. Useful when this is done in another build step, e.g. through Babel.

Expand Down
22 changes: 22 additions & 0 deletions features/makepot.feature
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,28 @@ Feature: Generate a POT file of a WordPress plugin
"Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/hello-world\n"
"""

Scenario: Sets custom Report-Msgid-Bugs-To
When I run `wp scaffold plugin hello-world`

When I run `wp i18n make-pot wp-content/plugins/hello-world wp-content/plugins/hello-world/languages/hello-world.pot --headers='{"Report-Msgid-Bugs-To":"https://github.com/hello-world/hello-world/"}'`
And the wp-content/plugins/hello-world/languages/hello-world.pot file should contain:
"""
"Report-Msgid-Bugs-To: https://github.com/hello-world/hello-world/\n"
"""
And the wp-content/plugins/hello-world/languages/hello-world.pot file should not contain:
"""
"Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/hello-world\n"
"""

Scenario: Sets custom header
When I run `wp scaffold plugin hello-world`

When I run `wp i18n make-pot wp-content/plugins/hello-world wp-content/plugins/hello-world/languages/hello-world.pot --headers='{"X-Poedit-Basepath":".."}'`
And the wp-content/plugins/hello-world/languages/hello-world.pot file should contain:
"""
"X-Poedit-Basepath: ..\n"
"""

Scenario: Sets the last translator and the language team
When I run `wp scaffold plugin hello-world`

Expand Down
138 changes: 138 additions & 0 deletions src/IterableCodeExtractor.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
<?php

namespace WP_CLI\I18n;

use Gettext\Translation;
use Gettext\Translations;
use RecursiveCallbackFilterIterator;
use RecursiveDirectoryIterator;
use RecursiveIteratorIterator;
use DirectoryIterator;
use WP_CLI;

trait IterableCodeExtractor {

private static $dir = '';

/**
* Extract the translations from a file.
*
* @param array|string $file A path of a file or files
* @param Translations $translations The translations instance to append the new translations.
* @param array $options {
* Optional. An array of options passed down to static::fromString()
*
* @type bool $wpExtractTemplates Extract 'Template Name' headers in theme files. Default 'false'.
* }
* @return null
*/
public static function fromFile( $file, Translations $translations, array $options = [] ) {
foreach ( static::getFiles( $file ) as $f ) {
// Make sure a relative file path is added as a comment.
$options['file'] = ltrim( str_replace( static::$dir, '', $f ), '/' );

$string = file_get_contents( $f );

if ( ! $string ) {
WP_CLI::debug(
sprintf(
'Could not load file %1s',
$f
)
);

continue;
}

if ( ! empty ( $options['wpExtractTemplates'] ) ) {
$headers = MakePotCommand::get_file_data_from_string( $string, [ 'Template Name' => 'Template Name' ] );

if ( ! empty( $headers[ 'Template Name'])) {
$translation = new Translation( '', $headers[ 'Template Name'] );
$translation->addExtractedComment('Template Name of the theme' );

$translations[] = $translation;
}
}

static::fromString( $string, $translations, $options );
}
}

/**
* Extract the translations from a file.
*
* @param string $dir Root path to start the recursive traversal in.
* @param Translations $translations The translations instance to append the new translations.
* @param array $options {
* Optional. An array of options passed down to static::fromString()
*
* @type bool $wpExtractTemplates Extract 'Template Name' headers in theme files. Default 'false'.
* @type array $exclude A list of path to exclude. Default [].
* @type array $extensions A list of extensions to process. Default [].
* }
* @return null
*/
public static function fromDirectory( $dir, Translations $translations, array $options = [] ) {
static::$dir = $dir;

$files = static::getFilesFromDirectory( $dir, isset( $options['exclude'] ) ? $options['exclude'] : [], $options['extensions'] );

if ( ! empty( $files ) ) {
static::fromFile( $files, $translations, $options );
}

static::$dir = '';
}

/**
* Recursively gets all PHP files within a directory.
*
* @param string $dir A path of a directory.
* @param array $exclude List of files and directories to skip.
* @param array $extensions List of filename extensions to process.
*
* @return array File list.
*/
public static function getFilesFromDirectory( $dir, array $exclude = [], $extensions = [] ) {
$filtered_files = [];

$files = new RecursiveIteratorIterator(
new RecursiveCallbackFilterIterator(
new RecursiveDirectoryIterator( $dir, RecursiveDirectoryIterator::SKIP_DOTS ),
function ( $file, $key, $iterator ) use ( $exclude, $extensions ) {
/** @var DirectoryIterator $file */
if ( in_array( $file->getBasename(), $exclude, true ) ) {
return false;
}

// Check for more complex paths, e.g. /some/sub/folder.
foreach( $exclude as $path_or_file ) {
if ( false !== mb_ereg( preg_quote( '/' . $path_or_file ) . '$', $file->getPathname() ) ) {
return false;
}
}

/** @var RecursiveCallbackFilterIterator $iterator */
if ( $iterator->hasChildren() ) {
return true;
}

return ( $file->isFile() && in_array( $file->getExtension(), $extensions, TRUE ) );
}
),
RecursiveIteratorIterator::CHILD_FIRST
);

foreach ( $files as $file ) {
/** @var DirectoryIterator $file */
if ( ! $file->isFile() || ! in_array( $file->getExtension(), $extensions, TRUE ) ) {
continue;
}

$filtered_files[] = $file->getPathname();
}

return $filtered_files;
}
}
134 changes: 18 additions & 116 deletions src/JsCodeExtractor.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,10 @@
use Gettext\Extractors\JsCode;
use Gettext\Translations;
use Peast\Syntax\Exception as PeastException;
use RecursiveCallbackFilterIterator;
use RecursiveDirectoryIterator;
use RecursiveIteratorIterator;
use DirectoryIterator;
use WP_CLI;

final class JsCodeExtractor extends JsCode {
private static $dir = '';
use IterableCodeExtractor;

public static $options = [
'extractComments' => [ 'translators', 'Translators' ],
Expand All @@ -29,117 +25,23 @@ final class JsCodeExtractor extends JsCode {
* {@inheritdoc}
*/
public static function fromString( $string, Translations $translations, array $options = [] ) {
$options += static::$options;

$functions = new JsFunctionsScanner( $string );

$functions->enableCommentsExtraction( $options['extractComments'] );
$functions->saveGettextFunctions( $translations, $options );
}

/**
* {@inheritdoc}
*/
public static function fromFile( $file, Translations $translations, array $options = [] ) {
foreach ( static::getFiles( $file ) as $f ) {
// Make sure a relative file path is added as a comment.
$options['file'] = ltrim( str_replace( static::$dir, '', $f ), '/' );

$string = file_get_contents( $f );

if ( ! $string ) {
WP_CLI::debug(
sprintf(
'Could not load file %1s',
$f
)
);

continue;
}

try {
static::fromString( $string, $translations, $options );
} catch ( PeastException $e ) {
WP_CLI::debug(
sprintf(
'Could not parse file %1$s: %2$s (line %3$d, column %4$d)',
$options['file'],
$e->getMessage(),
$e->getPosition()->getLine(),
$e->getPosition()->getColumn()
)
);
}
try {
$options += static::$options;

$functions = new JsFunctionsScanner( $string );

$functions->enableCommentsExtraction( $options['extractComments'] );
$functions->saveGettextFunctions( $translations, $options );
} catch ( PeastException $e ) {
WP_CLI::debug(
sprintf(
'Could not parse file %1$s: %2$s (line %3$d, column %4$d)',
$options['file'],
$e->getMessage(),
$e->getPosition()->getLine(),
$e->getPosition()->getColumn()
)
);
}
}

/**
* Recursively extracts the translations from a directory.
*
* @param string $dir Root path to start the recursive traversal in.
* @param Translations $translations The translations instance to append the new translations.
* @param array $options
*/
public static function fromDirectory( $dir, Translations $translations, array $options = [] ) {
static::$dir = $dir;

$files = static::getFilesFromDirectory( $dir, isset( $options['exclude'] ) ? $options['exclude'] : [] );

if ( ! empty( $files ) ) {
static::fromFile( $files, $translations, $options );
}

static::$dir = '';
}

/**
* Recursively gets all PHP files within a directory.
*
* @param string $dir A path of a directory.
* @param array $exclude List of files and directories to skip.
*
* @return array File list.
*/
private static function getFilesFromDirectory( $dir, array $exclude = [] ) {
$filtered_files = [];

$files = new RecursiveIteratorIterator(
new RecursiveCallbackFilterIterator(
new RecursiveDirectoryIterator( $dir, RecursiveDirectoryIterator::SKIP_DOTS ),
function ( $file, $key, $iterator ) use ( $exclude ) {
/** @var DirectoryIterator $file */
if ( in_array( $file->getBasename(), $exclude, true ) ) {
return false;
}

// Check for more complex paths, e.g. /some/sub/folder.
foreach( $exclude as $path_or_file ) {
if ( false !== mb_ereg( preg_quote( '/' . $path_or_file ) . '$', $file->getPathname() ) ) {
return false;
}
}

/** @var RecursiveCallbackFilterIterator $iterator */
if ( $iterator->hasChildren() ) {
return true;
}

return ( $file->isFile() && 'js' === $file->getExtension() );
}
),
RecursiveIteratorIterator::CHILD_FIRST
);

foreach ( $files as $file ) {
/** @var DirectoryIterator $file */
if ( ! $file->isFile() || 'js' !== $file->getExtension() ) {
continue;
}

$filtered_files[] = $file->getPathname();
}

return $filtered_files;
}
}
23 changes: 20 additions & 3 deletions src/MakePotCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,11 @@ class MakePotCommand extends WP_CLI_Command {
*/
protected $skip_js = false;

/**
* @var array
*/
protected $headers = [];

/**
* @var string
*/
Expand Down Expand Up @@ -91,6 +96,9 @@ class MakePotCommand extends WP_CLI_Command {
* By default, the following files and folders are ignored: node_modules, .git, .svn, .CVS, .hg, vendor.
* Leading and trailing slashes are ignored, i.e. `/my/directory/` is the same as `my/directory`.
*
* [--headers=<headers>]
* : Array in JSON format of custom headers which will be added to the POT file. Defaults to empty array.
*
* [--skip-js]
* : Skips JavaScript string extraction. Useful when this is done in another build step, e.g. through Babel.
*
Expand All @@ -102,9 +110,12 @@ class MakePotCommand extends WP_CLI_Command {
* @when before_wp_load
*/
public function __invoke( $args, $assoc_args ) {
$this->source = realpath( $args[0] );
$this->slug = Utils\get_flag_value( $assoc_args, 'slug', Utils\basename( $this->source ) );
$this->skip_js = Utils\get_flag_value( $assoc_args, 'skip-js', $this->skip_js );
$array_arguments = array( 'headers' );
$assoc_args = \WP_CLI\Utils\parse_shell_arrays( $assoc_args, $array_arguments );
$this->source = realpath( $args[0] );
$this->slug = Utils\get_flag_value( $assoc_args, 'slug', Utils\basename( $this->source ) );
$this->skip_js = Utils\get_flag_value( $assoc_args, 'skip-js', $this->skip_js );
$this->headers = Utils\get_flag_value( $assoc_args, 'headers', $this->headers );

$ignore_domain = Utils\get_flag_value( $assoc_args, 'ignore-domain', false );

Expand Down Expand Up @@ -326,6 +337,7 @@ protected function makepot() {
// Extract 'Template Name' headers in theme files.
'wpExtractTemplates' => isset( $file_data['Theme Name'] ),
'exclude' => $this->exclude,
'extensions' => [ 'php' ],
] );

if ( ! $this->skip_js ) {
Expand All @@ -334,6 +346,7 @@ protected function makepot() {
$this->translations,
[
'exclude' => $this->exclude,
'extensions' => [ 'js' ],
]
);
}
Expand Down Expand Up @@ -414,6 +427,10 @@ protected function set_default_headers() {
$this->translations->setHeader( 'Last-Translator', 'FULL NAME <EMAIL@ADDRESS>' );
$this->translations->setHeader( 'Language-Team', 'LANGUAGE <[email protected]>' );
$this->translations->setHeader( 'X-Generator', 'WP-CLI ' . WP_CLI_VERSION );

foreach( $this->headers as $key => $value ) {
$this->translations->setHeader( $key, $value );
}
}

/**
Expand Down
Loading

0 comments on commit b4dcde8

Please sign in to comment.