diff --git a/composer.json b/composer.json index 78900734..749dab74 100644 --- a/composer.json +++ b/composer.json @@ -11,8 +11,11 @@ } ], "require": { - "eftec/bladeone": "3.52", - "gettext/gettext": "^4.8", + "eftec/bladeone": "^4.11", + "gettext/gettext": "^5.7", + "gettext/js-scanner": "^1.1", + "gettext/php-scanner": "^v1.3.1", + "gettext/translator": "^1.2", "mck89/peast": "^1.13.11", "wp-cli/wp-cli": "^2.12" }, diff --git a/src/BladeCodeExtractor.php b/src/BladeCodeExtractor.php index 482c68c8..a90477e6 100644 --- a/src/BladeCodeExtractor.php +++ b/src/BladeCodeExtractor.php @@ -42,8 +42,6 @@ final class BladeCodeExtractor extends BladeGettextExtractor { ], ]; - protected static $functionsScannerClass = 'WP_CLI\I18n\PhpFunctionsScanner'; - /** * {@inheritdoc} */ diff --git a/src/BladeGettextExtractor.php b/src/BladeGettextExtractor.php index d698967e..6065d9f0 100644 --- a/src/BladeGettextExtractor.php +++ b/src/BladeGettextExtractor.php @@ -4,14 +4,10 @@ use eftec\bladeone\BladeOne; -// Modified Gettext Blade extractor that -// uses the up-to-date BladeOne standalone Blade engine, -// correctly supports fromStringMultiple. - /** * Class to get gettext strings from blade.php files returning arrays. */ -class BladeGettextExtractor extends \Gettext\Extractors\PhpCode { +class BladeGettextExtractor extends PhpCodeExtractor { /** * Prepares a Blade compiler/engine and returns it. diff --git a/src/IterableCodeExtractor.php b/src/IterableCodeExtractor.php index 51b3e53b..811ca594 100644 --- a/src/IterableCodeExtractor.php +++ b/src/IterableCodeExtractor.php @@ -70,10 +70,10 @@ public static function fromFile( $file_or_files, Translations $translations, arr $headers = FileDataExtractor::get_file_data_from_string( $text, [ 'Template Name' => 'Template Name' ] ); if ( ! empty( $headers['Template Name'] ) ) { - $translation = new Translation( '', $headers['Template Name'] ); - $translation->addExtractedComment( 'Template Name of the theme' ); + $translation = Translation::create( '', $headers['Template Name'] ); + $translation->getComments()->add( 'Template Name of the theme' ); - $translations[] = $translation; + $translations->add( $translation ); } } @@ -88,17 +88,17 @@ public static function fromFile( $file_or_files, Translations $translations, arr ); if ( ! empty( $headers['Title'] ) ) { - $translation = new Translation( 'Pattern title', $headers['Title'] ); - $translation->addReference( $options['file'] ); + $translation = Translation::create( 'Pattern title', $headers['Title'] ); + $translation->getReferences()->add( $options['file'] ); - $translations[] = $translation; + $translations->add( $translation ); } if ( ! empty( $headers['Description'] ) ) { - $translation = new Translation( 'Pattern description', $headers['Description'] ); - $translation->addReference( $options['file'] ); + $translation = Translation::create( 'Pattern description', $headers['Description'] ); + $translation->getReferences()->add( $options['file'] ); - $translations[] = $translation; + $translations->add( $translation ); } } @@ -339,4 +339,37 @@ protected static function file_get_extension_multi( $file ) { protected static function trim_leading_slash( $path ) { return ltrim( $path, '/' ); } + + /** + * Formerly part php-gettext 4.x. + */ + protected static function getFiles( $file ) { + if ( empty( $file ) ) { + throw new InvalidArgumentException( 'There is not any file defined' ); + } + + if ( is_string( $file ) ) { + if ( ! is_file( $file ) ) { + throw new InvalidArgumentException( "'$file' is not a valid file" ); + } + + if ( ! is_readable( $file ) ) { + throw new InvalidArgumentException( "'$file' is not a readable file" ); + } + + return [ $file ]; + } + + if ( is_array( $file ) ) { + $files = []; + + foreach ( $file as $f ) { + $files = array_merge( $files, self::getFiles( $f ) ); + } + + return $files; + } + + throw new InvalidArgumentException( 'The first argument must be string or array' ); + } } diff --git a/src/JedGenerator.php b/src/JedGenerator.php index 7665a422..590f8458 100644 --- a/src/JedGenerator.php +++ b/src/JedGenerator.php @@ -2,7 +2,7 @@ namespace WP_CLI\I18n; -use Gettext\Generators\Jed; +use Gettext\Generator\Generator; use Gettext\Translation; use Gettext\Translations; @@ -11,72 +11,83 @@ * * Adds some more meta data to JED translation files than the default generator. */ -class JedGenerator extends Jed { +class JedGenerator extends Generator { /** - * {@parentDoc}. + * Options passed to json_encode(). + * + * @var int JSON options. */ - public static function toString( Translations $translations, array $options = [] ) { - $options += static::$options; - $domain = $translations->getDomain() ?: 'messages'; - $messages = static::buildMessages( $translations ); - - $configuration = [ - '' => [ - 'domain' => $domain, - 'lang' => $translations->getLanguage() ?: 'en', - 'plural-forms' => $translations->getHeader( 'Plural-Forms' ) ?: 'nplurals=2; plural=(n != 1);', - ], - ]; - - $data = [ - 'translation-revision-date' => $translations->getHeader( 'PO-Revision-Date' ), - 'generator' => 'WP-CLI/' . WP_CLI_VERSION, - 'source' => $options['source'], - 'domain' => $domain, - 'locale_data' => [ - $domain => $configuration + $messages, - ], - ]; - - return json_encode( $data, $options['json'] ); - } + protected $json_options = 0; /** - * Generates an array with all translations. + * Source file. * - * @param Translations $translations + * @var string Source file. + */ + protected $source = ''; + + /** + * Constructor. * - * @return array + * @param int $json_options Options passed to json_encode(). + * @param string $source Source file. */ - public static function buildMessages( Translations $translations ) { - $plural_forms = $translations->getPluralForms(); - $number_of_plurals = is_array( $plural_forms ) ? ( $plural_forms[0] - 1 ) : null; - $messages = []; - $context_glue = chr( 4 ); + public function __construct( int $json_options, string $source ) { + $this->json_options = $json_options; + $this->source = $source; + } - foreach ( $translations as $translation ) { - /** @var Translation $translation */ + public function generateString( Translations $translations ): string { + $array = $this->generateArray( $translations ); + + return json_encode( $array, $this->json_options ); + } - if ( $translation->isDisabled() ) { + public function generateArray( Translations $translations ): array { + $pluralForm = $translations->getHeaders()->getPluralForm(); + $pluralSize = is_array( $pluralForm ) ? ( $pluralForm[0] - 1 ) : null; + $messages = []; + + foreach ( $translations as $translation ) { + if ( ! $translation->getTranslation() || $translation->isDisabled() ) { continue; } - $key = $translation->getOriginal(); + $context = $translation->getContext() ?: ''; + $original = $translation->getOriginal(); - if ( $translation->hasContext() ) { - $key = $translation->getContext() . $context_glue . $key; + if ( ! isset( $messages[ $context ] ) ) { + $messages[ $context ] = []; } - if ( $translation->hasPluralTranslations( true ) ) { - $message = $translation->getPluralTranslations( $number_of_plurals ); - array_unshift( $message, $translation->getTranslation() ); + if ( self::hasPluralTranslations( $translation ) ) { + $messages[ $context ][ $original ] = $translation->getPluralTranslations( $pluralSize ); + array_unshift( $messages[ $context ][ $original ], $translation->getTranslation() ); } else { - $message = [ $translation->getTranslation() ]; + $messages[ $context ][ $original ] = $translation->getTranslation(); } - - $messages[ $key ] = $message; } - return $messages; + $configuration = [ + '' => [ + 'domain' => $translations->getDomain(), + 'lang' => $translations->getLanguage() ?: 'en', + 'plural-forms' => $translations->getHeaders()->getPluralForm() ?: 'nplurals=2; plural=(n != 1);', + ], + ]; + + return [ + 'translation-revision-date' => $translations->getHeaders()->get( 'PO-Revision-Date' ), + 'generator' => 'WP-CLI/' . WP_CLI_VERSION, + 'source' => $this->source, + 'domain' => $translations->getDomain(), + 'locale_data' => [ + $translations->getDomain() => $configuration + $messages, + ], + ]; + } + + private static function hasPluralTranslations( Translation $translation ): bool { + return implode( '', $translation->getPluralTranslations() ) !== ''; } } diff --git a/src/JsCodeExtractor.php b/src/JsCodeExtractor.php index 4c03ee5a..8dfe4233 100644 --- a/src/JsCodeExtractor.php +++ b/src/JsCodeExtractor.php @@ -3,27 +3,27 @@ namespace WP_CLI\I18n; use Exception; -use Gettext\Extractors\JsCode; +use Gettext\Scanner\JsScanner; use Gettext\Translations; use Peast\Syntax\Exception as PeastException; use WP_CLI; -final class JsCodeExtractor extends JsCode { +final class JsCodeExtractor { use IterableCodeExtractor; + protected $functions = [ + '__' => 'text_domain', + '_x' => 'text_context_domain', + '_n' => 'single_plural_number_domain', + '_nx' => 'single_plural_number_context_domain', + ]; + public static $options = [ 'extractComments' => [ 'translators', 'Translators' ], 'constants' => [], - 'functions' => [ - '__' => 'text_domain', - '_x' => 'text_context_domain', - '_n' => 'single_plural_number_domain', - '_nx' => 'single_plural_number_context_domain', - ], + 'functions' => [], ]; - protected static $functionsScannerClass = 'WP_CLI\I18n\JsFunctionsScanner'; - /** * @inheritdoc */ @@ -31,7 +31,12 @@ public static function fromString( $text, Translations $translations, array $opt WP_CLI::debug( "Parsing file {$options['file']}", 'make-pot' ); try { - self::fromStringMultiple( $text, [ $translations ], $options ); + $options += self::$options; + + $scanner = new JsScanner( $translations ); + $scanner->setFunctions( self::$options['functions'] ); + $scanner->extractCommentsStartingWith( $options['extractComments'] ); + $scanner->scanString( $text, $options['file'] ); } catch ( PeastException $exception ) { WP_CLI::debug( sprintf( @@ -54,16 +59,4 @@ public static function fromString( $text, Translations $translations, array $opt ); } } - - /** - * @inheritDoc - */ - public static function fromStringMultiple( $text, array $translations, array $options = [] ) { - $options += self::$options; - - /** @var JsFunctionsScanner $functions */ - $functions = new self::$functionsScannerClass( $text ); - $functions->enableCommentsExtraction( $options['extractComments'] ); - $functions->saveGettextFunctions( $translations, $options ); - } } diff --git a/src/JsFunctionsScanner.php b/src/JsFunctionsScanner.php index bf4b8049..44136426 100644 --- a/src/JsFunctionsScanner.php +++ b/src/JsFunctionsScanner.php @@ -178,7 +178,7 @@ function ( $node ) use ( &$translations, $options, &$all_comments ) { $translation = $translations->insert( $context, $original, $plural ); if ( $add_reference ) { - $translation->addReference( $file, $line ); + $translation->getReferences()->add( $file, $line ); } if ( @@ -203,7 +203,7 @@ function ( $node ) use ( &$translations, $options, &$all_comments ) { $prefixes = array_filter( (array) $this->extract_comments ); if ( $parsed_comment->checkPrefixes( $prefixes ) ) { - $translation->addExtractedComment( $parsed_comment->getComment() ); + $translation->getComments()->add( $parsed_comment->getComment() ); $this->comments_cache[] = $comment; } diff --git a/src/JsonSchemaExtractor.php b/src/JsonSchemaExtractor.php index 63ae0f57..81744493 100644 --- a/src/JsonSchemaExtractor.php +++ b/src/JsonSchemaExtractor.php @@ -2,12 +2,12 @@ namespace WP_CLI\I18n; -use Gettext\Extractors\Extractor; +use Gettext\Translation; use Gettext\Translations; use WP_CLI; use WP_CLI\Utils; -class JsonSchemaExtractor extends Extractor { +class JsonSchemaExtractor { use IterableCodeExtractor; /** @@ -127,12 +127,14 @@ private static function extract_strings_using_i18n_schema( Translations $transla } if ( is_string( $i18n_schema ) && is_string( $settings ) ) { - $translation = $translations->insert( $i18n_schema, $settings ); + $translation = Translation::create( $i18n_schema, $settings ); if ( $file ) { - $translation->addReference( $file ); + $translation->getReferences()->add( $file ); } + $translations->add( $translation ); + return; } diff --git a/src/MakeJsonCommand.php b/src/MakeJsonCommand.php index 164ba06e..638c465e 100644 --- a/src/MakeJsonCommand.php +++ b/src/MakeJsonCommand.php @@ -2,8 +2,9 @@ namespace WP_CLI\I18n; -use Gettext\Extractors\Po as PoExtractor; -use Gettext\Generators\Po as PoGenerator; +use Gettext\Generator\MoGenerator; +use Gettext\Loader\PoLoader; +use Gettext\Generator\PoGenerator; use Gettext\Translation; use Gettext\Translations; use WP_CLI; @@ -143,8 +144,8 @@ function ( $extension ) { $file_basename = basename( $file->getFilename(), '.po' ); $destination_file = "{$destination}/{$file_basename}.mo"; - $translations = Translations::fromPoFile( $file->getPathname() ); - if ( ! $translations->toMoFile( $destination_file ) ) { + $translations = ( new PoLoader() )->loadFile( $file->getPathname() ); + if ( ! ( new MoGenerator() )->generateFile( $translations, $destination_file ) ) { WP_CLI::warning( "Could not create file {$destination_file}" ); } } @@ -241,13 +242,12 @@ static function ( $value ) { * @return array List of created JSON files. */ protected function make_json( $source_file, $destination, $map, $domain, $extensions ) { - /** @var Translations[] $mapping */ - $mapping = []; - $translations = new Translations(); - $result = []; - $extensions = array_merge( [ 'js' ], $extensions ); + /** @var array $mapping */ + $mapping = []; + $result = []; + $extensions = array_merge( [ 'js' ], $extensions ); - PoExtractor::fromFile( $source_file, $translations ); + $translations = ( new PoLoader() )->loadFile( $source_file ); $base_file_name = basename( $source_file, '.po' ); @@ -270,31 +270,31 @@ static function ( $reference ) use ( $extensions ) { ? preg_replace( "/.min.{$extension}$/", ".{$extension}", $file ) : null; }, - $this->reference_map( $translation->getReferences(), $map ) + $this->reference_map( $translation->getReferences()->toArray(), $map ) ); $sources = array_unique( array_filter( $sources ) ); foreach ( $sources as $source ) { if ( ! isset( $mapping[ $source ] ) ) { - $mapping[ $source ] = new Translations(); + $mapping[ $source ] = Translations::create(); // phpcs:ignore Squiz.PHP.CommentedOutCode.Found -- Provide code that is meant to be used once the bug is fixed. // See https://core.trac.wordpress.org/ticket/45441 // $mapping[ $source ]->setDomain( $translations->getDomain() ); - $mapping[ $source ]->setHeader( 'Language', $translations->getLanguage() ); - $mapping[ $source ]->setHeader( 'PO-Revision-Date', $translations->getHeader( 'PO-Revision-Date' ) ); + $mapping[ $source ]->getHeaders()->set( 'Language', $translations->getLanguage() ); + $mapping[ $source ]->getHeaders()->set( 'PO-Revision-Date', $translations->getHeaders()->get( 'PO-Revision-Date' ) ); - $plural_forms = $translations->getPluralForms(); + $plural_forms = $translations->getHeaders()->getPluralForm(); if ( $plural_forms ) { list( $count, $rule ) = $plural_forms; - $mapping[ $source ]->setPluralForms( $count, $rule ); + $mapping[ $source ]->getHeaders()->setPluralForm( $count, $rule ); } } - $mapping[ $source ][] = $translation; + $mapping[ $source ]->add( $translation ); } } @@ -306,9 +306,9 @@ static function ( $reference ) use ( $extensions ) { /** * Takes the references and applies map, if given * - * @param array $references translation references - * @param array|null $map file mapping - * @return array mapped references + * @param array $references Translation references. + * @param array|null $map File mapping. + * @return array Mapped references. */ protected function reference_map( $references, $map ) { if ( is_null( $map ) ) { @@ -365,13 +365,9 @@ protected function build_json_files( $mapping, $base_file_name, $destination ) { $hash = md5( $file ); $destination_file = "{$destination}/{$base_file_name}-{$hash}.json"; - $success = JedGenerator::toFile( + $success = ( new JedGenerator( $this->json_options, $file ) )->generateFile( $translations, - $destination_file, - [ - 'json' => $this->json_options, - 'source' => $file, - ] + $destination_file ); if ( ! $success ) { @@ -393,19 +389,18 @@ protected function build_json_files( $mapping, $base_file_name, $destination ) { * @return bool True on success, false otherwise. */ protected function remove_js_strings_from_po_file( $source_file ) { - /** @var Translations[] $mapping */ - $translations = new Translations(); + $translations = ( new PoLoader() )->loadFile( $source_file ); - PoExtractor::fromFile( $source_file, $translations ); - - foreach ( $translations->getArrayCopy() as $translation ) { + foreach ( $translations->getTranslations() as $translation ) { /** @var Translation $translation */ - if ( ! $translation->hasReferences() ) { + $references = $translation->getReferences(); + + if ( 0 === $references->count() ) { continue; } - foreach ( $translation->getReferences() as $reference ) { + foreach ( $references->toArray() as $reference ) { $file = $reference[0]; if ( substr( $file, - 3 ) !== '.js' ) { @@ -416,6 +411,6 @@ protected function remove_js_strings_from_po_file( $source_file ) { unset( $translations[ $translation->getId() ] ); } - return PoGenerator::toFile( $translations, $source_file ); + return ( new PoGenerator() )->generateFile( $translations, $source_file ); } } diff --git a/src/MakeMoCommand.php b/src/MakeMoCommand.php index a13eeabc..b410b300 100644 --- a/src/MakeMoCommand.php +++ b/src/MakeMoCommand.php @@ -3,7 +3,8 @@ namespace WP_CLI\I18n; use DirectoryIterator; -use Gettext\Translations; +use Gettext\Loader\PoLoader; +use Gettext\Generator\PoGenerator; use IteratorIterator; use SplFileInfo; use WP_CLI; @@ -87,8 +88,9 @@ public function __invoke( $args, $assoc_args ) { } $destination_file = "{$destination}/{$file_name}"; - $translations = Translations::fromPoFile( $file->getPathname() ); - if ( ! $translations->toMoFile( $destination_file ) ) { + $translations = ( new PoLoader() )->loadFile( $file->getPathname() ); + + if ( ! ( new PoGenerator() )->generateFile( $translations, $destination_file ) ) { WP_CLI::warning( sprintf( 'Could not create file %s', $destination_file ) ); continue; } diff --git a/src/MakePhpCommand.php b/src/MakePhpCommand.php index 3c3b559c..1b02152b 100644 --- a/src/MakePhpCommand.php +++ b/src/MakePhpCommand.php @@ -3,6 +3,7 @@ namespace WP_CLI\I18n; use DirectoryIterator; +use Gettext\Loader\PoLoader; use Gettext\Translations; use IteratorIterator; use SplFileInfo; @@ -72,8 +73,9 @@ public function __invoke( $args, $assoc_args ) { $file_basename = basename( $file->getFilename(), '.po' ); $destination_file = "{$destination}/{$file_basename}.l10n.php"; - $translations = Translations::fromPoFile( $file->getPathname() ); - if ( ! PhpArrayGenerator::toFile( $translations, $destination_file ) ) { + $translations = ( new PoLoader() )->loadFile( $file->getPathname() ); + + if ( ! ( new PhpArrayGenerator() )->generateFile( $translations, $destination_file ) ) { WP_CLI::warning( sprintf( 'Could not create file %s', $destination_file ) ); continue; } diff --git a/src/MakePotCommand.php b/src/MakePotCommand.php index 8688096e..b28a2fcc 100644 --- a/src/MakePotCommand.php +++ b/src/MakePotCommand.php @@ -2,7 +2,7 @@ namespace WP_CLI\I18n; -use Gettext\Extractors\Po; +use Gettext\Generator\PoGenerator; use Gettext\Merge; use Gettext\Translation; use Gettext\Translations; @@ -11,6 +11,7 @@ use WP_CLI_Command; use WP_CLI\Utils; use DirectoryIterator; +use Gettext\Headers; use IteratorIterator; class MakePotCommand extends WP_CLI_Command { @@ -303,7 +304,7 @@ public function __invoke( $args, $assoc_args ) { WP_CLI::debug( sprintf( 'Extracted %d strings', $translations_count ), 'make-pot' ); } - if ( ! PotGenerator::toFile( $translations, $this->destination ) ) { + if ( ! ( new PoGenerator() )->generateFile( $translations, $this->destination ) ) { WP_CLI::error( 'Could not generate a POT file.' ); } @@ -427,7 +428,7 @@ function ( $file ) { WP_CLI::debug( sprintf( 'Ignoring any string already existing in: %s', $file ), 'make-pot' ); - $this->exceptions[ $file ] = new Translations(); + $this->exceptions[ $file ] = Translations::create(); Po::fromFile( $file, $this->exceptions[ $file ] ); } } @@ -587,24 +588,24 @@ protected function get_file_headers( $type ) { * @return Translations A Translation set. */ protected function extract_strings() { - $translations = new Translations(); + $translations = Translations::create(); // Add existing strings first but don't keep headers. if ( ! empty( $this->merge ) ) { - $existing_translations = new Translations(); + $existing_translations = Translations::create(); Po::fromFile( $this->merge, $existing_translations ); - $translations->mergeWith( $existing_translations, Merge::ADD | Merge::REMOVE ); + $translations->mergeWith( $existing_translations, Merge::TRANSLATIONS_OURS | Merge::HEADERS_OURS ); } - PotGenerator::setCommentBeforeHeaders( $this->get_file_comment() ); + $translations->setDescription( $this->get_file_comment() ); $this->set_default_headers( $translations ); // POT files have no Language header. - $translations->deleteHeader( Translations::HEADER_LANGUAGE ); + $translations->getHeaders()->delete( Headers::HEADER_LANGUAGE ); // Only relevant for PO files, not POT files. - $translations->setHeader( 'PO-Revision-Date', 'YEAR-MO-DA HO:MI+ZONE' ); + $translations->getHeaders()->set( 'PO-Revision-Date', 'YEAR-MO-DA HO:MI+ZONE' ); if ( $this->domain ) { $translations->setDomain( $this->domain ); @@ -620,21 +621,21 @@ protected function extract_strings() { continue; } - $translation = new Translation( '', $data ); + $translation = Translation::create( '', $data ); if ( $is_theme ) { - $translation->addExtractedComment( sprintf( '%s of the theme', $header ) ); + $translation->getComments()->add( sprintf( '%s of the theme', $header ) ); } else { - $translation->addExtractedComment( sprintf( '%s of the plugin', $header ) ); + $translation->getComments()->add( sprintf( '%s of the plugin', $header ) ); } if ( $this->main_file_path && $this->location ) { - $translation->addReference( + $translation->getReferences()->add( ltrim( str_replace( Utils\normalize_path( "$this->source/" ), '', Utils\normalize_path( $this->main_file_path ) ), '/' ) ); } - $translations[] = $translation; + $translations->add( $translation ); } try { @@ -744,7 +745,7 @@ protected function extract_strings() { foreach ( $this->exceptions as $file => $exception_translations ) { /** @var Translation $exception_translation */ foreach ( $exception_translations as $exception_translation ) { - if ( ! $translations->find( $exception_translation ) ) { + if ( ! $translations->find( $exception_translation->getContext(), $exception_translation->getOriginal() ) ) { continue; } @@ -757,7 +758,7 @@ protected function extract_strings() { } if ( $this->subtract_and_merge ) { - PotGenerator::toFile( $exception_translations, $file ); + ( new PoGenerator() )->generateFile( $exception_translations, $file ); } } @@ -779,14 +780,21 @@ protected function audit_strings( $translations ) { foreach ( $translations as $translation ) { /** @var Translation $translation */ - $references = $translation->getReferences(); + $references = $translation->getReferences()->toArray(); + + $location = ''; + + // There might not be any file references. + foreach( $references as $file => $lines ) { + if ( count( $lines ) > 0 ) { + $location = "$file:$lines[0]"; + } + } - // File headers don't have any file references. - $location = $translation->hasReferences() ? '(' . implode( ':', $references[0] ) . ')' : ''; // Check 1: Flag strings with placeholders that should have translator comments. if ( - ! $translation->hasExtractedComments() && + $translation->getComments()->count() === 0 && preg_match( self::SPRINTF_PLACEHOLDER_REGEX, $translation->getOriginal(), $placeholders ) >= 1 ) { $message = sprintf( @@ -798,17 +806,17 @@ protected function audit_strings( $translations ) { } // Check 2: Flag strings with different translator comments. - if ( $translation->hasExtractedComments() ) { - $comments = $translation->getExtractedComments(); + if ( $translation->getComments()->count() > 0 ) { + $comments = $translation->getExtractedComments()->toArray(); // Remove plugin header information from comments. $comments = array_filter( $comments, function ( $comment ) { - /** @var ParsedComment|string $comment */ + /** @var string $comment */ /** @var string $file_header */ foreach ( $this->get_file_headers( $this->project_type ) as $file_header ) { - if ( 0 === strpos( ( $comment instanceof ParsedComment ? $comment->getComment() : $comment ), $file_header ) ) { + if ( 0 === strpos( $comment, $file_header ) ) { return null; } } @@ -872,7 +880,7 @@ function ( $comment ) use ( &$unique_comments ) { WP_CLI::warning( $message ); } - if ( $translation->hasPlural() ) { + if ( count( $translation->getPluralTranslations() ) > 0 ) { preg_match_all( self::SPRINTF_PLACEHOLDER_REGEX, $translation->getOriginal(), $single_placeholders ); $single_placeholders = $single_placeholders[0]; @@ -985,19 +993,19 @@ protected function set_default_headers( $translations ) { } if ( null !== $name ) { - $translations->setHeader( 'Project-Id-Version', $name . ( $version ? ' ' . $version : '' ) ); + $translations->getHeaders()->set( 'Project-Id-Version', $name . ( $version ? ' ' . $version : '' ) ); } if ( null !== $bugs_address ) { - $translations->setHeader( 'Report-Msgid-Bugs-To', $bugs_address ); + $translations->getHeaders()->set( 'Report-Msgid-Bugs-To', $bugs_address ); } - $translations->setHeader( 'Last-Translator', 'FULL NAME ' ); - $translations->setHeader( 'Language-Team', 'LANGUAGE ' ); - $translations->setHeader( 'X-Generator', 'WP-CLI ' . WP_CLI_VERSION ); + $translations->getHeaders()->set( 'Last-Translator', 'FULL NAME ' ); + $translations->getHeaders()->set( 'Language-Team', 'LANGUAGE ' ); + $translations->getHeaders()->set( 'X-Generator', 'WP-CLI ' . WP_CLI_VERSION ); foreach ( $this->headers as $key => $value ) { - $translations->setHeader( $key, $value ); + $translations->getHeaders()->set( $key, $value ); } } diff --git a/src/MapCodeExtractor.php b/src/MapCodeExtractor.php index d3b8c67a..af6730a6 100644 --- a/src/MapCodeExtractor.php +++ b/src/MapCodeExtractor.php @@ -2,12 +2,11 @@ namespace WP_CLI\I18n; -use Gettext\Extractors\JsCode; use Gettext\Translations; use Peast\Syntax\Exception as PeastException; use WP_CLI; -final class MapCodeExtractor extends JsCode { +final class MapCodeExtractor { use IterableCodeExtractor; public static $options = [ diff --git a/src/PhpArrayGenerator.php b/src/PhpArrayGenerator.php index eadb4d2b..8f7c5cce 100644 --- a/src/PhpArrayGenerator.php +++ b/src/PhpArrayGenerator.php @@ -2,7 +2,7 @@ namespace WP_CLI\I18n; -use Gettext\Generators\PhpArray; +use Gettext\Generator\Generator; use Gettext\Translation; use Gettext\Translations; @@ -11,16 +11,16 @@ * * Returns output in the form WordPress uses. */ -class PhpArrayGenerator extends PhpArray { +class PhpArrayGenerator extends Generator { public static $options = [ 'includeHeaders' => false, ]; /** - * {@inheritdoc} + * Generates the final string representation of the translations. */ - public static function toString( Translations $translations, array $options = [] ) { - $array = static::generate( $translations, $options ); + public function generateString( Translations $translations ): string { + $array = static::generate( $translations, static::$options ); return ' $translations->getDomain(), - 'plural-forms' => $translations->getHeader( 'Plural-Forms' ), + 'plural-forms' => $translations->getHeaders()->getPluralForm(), ]; $language = $translations->getLanguage(); @@ -78,7 +78,7 @@ protected static function toArray( Translations $translations, $include_headers, * @var Translation $translation */ foreach ( $translations as $translation ) { - if ( $translation->isDisabled() || ! $translation->hasTranslation() ) { + if ( $translation->isDisabled() || null === $translation->getTranslation() ) { continue; } @@ -87,7 +87,7 @@ protected static function toArray( Translations $translations, $include_headers, $key = $context ? $context . "\4" . $original : $original; - if ( $translation->hasPluralTranslations() ) { + if ( count( $translation->getPluralTranslations() ) > 0 ) { $msg_translations = $translation->getPluralTranslations(); array_unshift( $msg_translations, $translation->getTranslation() ); $messages[ $key ] = implode( "\0", $msg_translations ); diff --git a/src/PhpCodeExtractor.php b/src/PhpCodeExtractor.php index 6014848e..f8e0e495 100644 --- a/src/PhpCodeExtractor.php +++ b/src/PhpCodeExtractor.php @@ -3,11 +3,11 @@ namespace WP_CLI\I18n; use Exception; -use Gettext\Extractors\PhpCode; +use Gettext\Scanner\PhpScanner; use Gettext\Translations; use WP_CLI; -final class PhpCodeExtractor extends PhpCode { +class PhpCodeExtractor { use IterableCodeExtractor; public static $options = [ @@ -43,16 +43,20 @@ final class PhpCodeExtractor extends PhpCode { ], ]; - protected static $functionsScannerClass = 'WP_CLI\I18n\PhpFunctionsScanner'; - /** * {@inheritdoc} */ public static function fromString( $text, Translations $translations, array $options = [] ) { + $options = static::$options + $options; + WP_CLI::debug( "Parsing file {$options['file']}", 'make-pot' ); try { - self::fromStringMultiple( $text, [ $translations ], $options ); + $scanner = new PhpScanner( $translations ); + $scanner->setFunctions( array_keys( self::$options['functions'] ) ); + $scanner->extractCommentsStartingWith( ...$options['extractComments'] ); + $scanner->scanString( $text, $options['file'] ); + } catch ( Exception $exception ) { WP_CLI::debug( sprintf( diff --git a/src/PhpFunctionsScanner.php b/src/PhpFunctionsScanner.php index 033ba2d0..376da360 100644 --- a/src/PhpFunctionsScanner.php +++ b/src/PhpFunctionsScanner.php @@ -74,7 +74,7 @@ public function saveGettextFunctions( $translations, array $options ) { $translation = $translations->insert( $context, $original, $plural ); if ( $add_reference ) { - $translation = $translation->addReference( $file, $line ); + $translation = $translation->getReferences()->add( $file, $line ); } if ( @@ -86,7 +86,7 @@ public function saveGettextFunctions( $translations, array $options ) { if ( isset( $function[3] ) ) { foreach ( $function[3] as $extracted_comment ) { - $translation = $translation->addExtractedComment( $extracted_comment ); + $translation = $translation->getComments()->add( $extracted_comment ); } } } diff --git a/src/PotGenerator.php b/src/PotGenerator.php deleted file mode 100644 index ed2f9a57..00000000 --- a/src/PotGenerator.php +++ /dev/null @@ -1,147 +0,0 @@ -getPluralForms(); - $plural_size = is_array( $plural_form ) ? ( $plural_form[0] - 1 ) : 1; - - foreach ( $translations->getHeaders() as $name => $value ) { - $lines[] = sprintf( '"%s: %s\\n"', $name, $value ); - } - - $lines[] = ''; - - foreach ( $translations as $translation ) { - /** @var \Gettext\Translation $translation */ - if ( $translation->hasComments() ) { - foreach ( $translation->getComments() as $comment ) { - $lines[] = '# ' . $comment; - } - } - - if ( $translation->hasExtractedComments() ) { - $unique_comments = array(); - - /** @var ParsedComment|string $comment */ - foreach ( $translation->getExtractedComments() as $comment ) { - $comment = ( $comment instanceof ParsedComment ? $comment->getComment() : $comment ); - if ( ! in_array( $comment, $unique_comments, true ) ) { - $lines[] = '#. ' . $comment; - $unique_comments[] = $comment; - } - } - } - - foreach ( $translation->getReferences() as $reference ) { - $lines[] = '#: ' . $reference[0] . ( null !== $reference[1] ? ':' . $reference[1] : '' ); - } - - if ( $translation->hasFlags() ) { - $lines[] = '#, ' . implode( ',', $translation->getFlags() ); - } - - $prefix = $translation->isDisabled() ? '#~ ' : ''; - - if ( $translation->hasContext() ) { - $lines[] = $prefix . 'msgctxt ' . self::convertString( $translation->getContext() ); - } - - self::addLines( $lines, $prefix . 'msgid', $translation->getOriginal() ); - - if ( $translation->hasPlural() ) { - self::addLines( $lines, $prefix . 'msgid_plural', $translation->getPlural() ); - - for ( $i = 0; $i <= $plural_size; $i++ ) { - self::addLines( $lines, $prefix . 'msgstr[' . $i . ']', '' ); - } - } else { - self::addLines( $lines, $prefix . 'msgstr', $translation->getTranslation() ); - } - - $lines[] = ''; - } - - return implode( "\n", $lines ); - } - - /** - * Escapes and adds double quotes to a string. - * - * @param string $text Multiline string. - * - * @return string[] - */ - protected static function multilineQuote( $text ) { - $lines = explode( "\n", $text ); - $last = count( $lines ) - 1; - - foreach ( $lines as $k => $line ) { - if ( $k === $last ) { - $lines[ $k ] = self::convertString( $line ); - } else { - $lines[ $k ] = self::convertString( $line . "\n" ); - } - } - - return $lines; - } - - /** - * Add one or more lines depending whether the string is multiline or not. - * - * @param array &$lines Array lines should be added to. - * @param string $name Name of the line, e.g. msgstr or msgid_plural. - * @param string $value The line to add. - */ - protected static function addLines( array &$lines, $name, $value ) { - $newlines = self::multilineQuote( $value ); - - if ( count( $newlines ) === 1 ) { - $lines[] = $name . ' ' . $newlines[0]; - } else { - $lines[] = $name . ' ""'; - - foreach ( $newlines as $line ) { - $lines[] = $line; - } - } - } -} diff --git a/src/UpdatePoCommand.php b/src/UpdatePoCommand.php index 5af6b996..08e237c0 100644 --- a/src/UpdatePoCommand.php +++ b/src/UpdatePoCommand.php @@ -3,9 +3,9 @@ namespace WP_CLI\I18n; use DirectoryIterator; -use Gettext\Extractors\Po; +use Gettext\Generator\PoGenerator; +use Gettext\Loader\PoLoader; use Gettext\Merge; -use Gettext\Translations; use IteratorIterator; use SplFileInfo; use WP_CLI; @@ -67,7 +67,7 @@ public function __invoke( $args, $assoc_args ) { $files = new IteratorIterator( new DirectoryIterator( $destination ) ); } - $pot_translations = Translations::fromPoFile( $source ); + $pot_translations = ( new PoLoader() )->loadFile( $source ); $result_count = 0; /** @var DirectoryIterator $file */ @@ -81,13 +81,13 @@ public function __invoke( $args, $assoc_args ) { continue; } - $po_translations = Translations::fromPoFile( $file->getPathname() ); + $po_translations = ( new PoLoader() )->loadFile( $file->getPathname() ); $po_translations->mergeWith( $pot_translations, - Merge::ADD | Merge::REMOVE | Merge::COMMENTS_THEIRS | Merge::EXTRACTED_COMMENTS_THEIRS | Merge::REFERENCES_THEIRS | Merge::DOMAIN_OVERRIDE + Merge::TRANSLATIONS_OURS | Merge::COMMENTS_THEIRS | Merge::EXTRACTED_COMMENTS_THEIRS | Merge::REFERENCES_THEIRS | Merge::HEADERS_OURS ); - if ( ! $po_translations->toPoFile( $file->getPathname() ) ) { + if ( ! ( new PoGenerator() )->generateFile( $po_translations, $file->getPathname() ) ) { WP_CLI::warning( sprintf( 'Could not update file %s', $file->getPathname() ) ); continue; } diff --git a/tests/PotGeneratorTest.php b/tests/PotGeneratorTest.php deleted file mode 100644 index 2e7ff389..00000000 --- a/tests/PotGeneratorTest.php +++ /dev/null @@ -1,25 +0,0 @@ -assertStringContainsString( 'msgid "%d cat"', $result ); - $this->assertStringContainsString( 'msgid_plural "%d cats"', $result ); - $this->assertStringContainsString( 'msgstr[0] ""', $result ); - $this->assertStringContainsString( 'msgstr[1] ""', $result ); - } -}