diff --git a/src/BladeCodeExtractor.php b/src/BladeCodeExtractor.php index a90477e6..5f0af64d 100644 --- a/src/BladeCodeExtractor.php +++ b/src/BladeCodeExtractor.php @@ -45,11 +45,11 @@ final class BladeCodeExtractor extends BladeGettextExtractor { /** * {@inheritdoc} */ - public static function fromString( $text, Translations $translations, array $options = [] ) { + public static function fromString( $text, Translations $translations, array $options = [] ) { // phpcs:ignore WordPress.NamingConventions.ValidFunctionName.MethodNameInvalid -- Using gettext scanner API. WP_CLI::debug( "Parsing file {$options['file']}", 'make-pot' ); try { - self::fromStringMultiple( $text, [ $translations ], $options ); + parent::fromString( $text, $translations, $options ); } catch ( Exception $exception ) { WP_CLI::debug( sprintf( diff --git a/src/BladeGettextExtractor.php b/src/BladeGettextExtractor.php index 6065d9f0..6ccaac5d 100644 --- a/src/BladeGettextExtractor.php +++ b/src/BladeGettextExtractor.php @@ -3,6 +3,7 @@ namespace WP_CLI\I18n; use eftec\bladeone\BladeOne; +use Gettext\Translations; /** * Class to get gettext strings from blade.php files returning arrays. @@ -37,11 +38,9 @@ protected static function compileBladeToPhp( $text ) { /** * {@inheritdoc} - * - * Note: In the parent PhpCode class fromString() uses fromStringMultiple() (overriden here) */ - public static function fromStringMultiple( $text, array $translations, array $options = [] ) { + public static function fromString( $text, Translations $translations, array $options = [] ) { // phpcs:ignore WordPress.NamingConventions.ValidFunctionName.MethodNameInvalid -- Using gettext scanner API. $php_string = static::compileBladeToPhp( $text ); - return parent::fromStringMultiple( $php_string, $translations, $options ); + return parent::fromString( $php_string, $translations, $options ); } } diff --git a/src/JedGenerator.php b/src/JedGenerator.php index 590f8458..c414fd36 100644 --- a/src/JedGenerator.php +++ b/src/JedGenerator.php @@ -44,9 +44,9 @@ public function generateString( Translations $translations ): string { } public function generateArray( Translations $translations ): array { - $pluralForm = $translations->getHeaders()->getPluralForm(); - $pluralSize = is_array( $pluralForm ) ? ( $pluralForm[0] - 1 ) : null; - $messages = []; + $plural_form = $translations->getHeaders()->getPluralForm(); + $plural_size = is_array( $plural_form ) ? ( $plural_form[0] - 1 ) : null; + $messages = []; foreach ( $translations as $translation ) { if ( ! $translation->getTranslation() || $translation->isDisabled() ) { @@ -61,7 +61,7 @@ public function generateArray( Translations $translations ): array { } if ( self::hasPluralTranslations( $translation ) ) { - $messages[ $context ][ $original ] = $translation->getPluralTranslations( $pluralSize ); + $messages[ $context ][ $original ] = $translation->getPluralTranslations( $plural_size ); array_unshift( $messages[ $context ][ $original ], $translation->getTranslation() ); } else { $messages[ $context ][ $original ] = $translation->getTranslation(); diff --git a/src/JsCodeExtractor.php b/src/JsCodeExtractor.php index 8dfe4233..35cb2fa9 100644 --- a/src/JsCodeExtractor.php +++ b/src/JsCodeExtractor.php @@ -27,7 +27,7 @@ final class JsCodeExtractor { /** * @inheritdoc */ - public static function fromString( $text, Translations $translations, array $options = [] ) { + public static function fromString( $text, Translations $translations, array $options = [] ) { // phpcs:ignore WordPress.NamingConventions.ValidFunctionName.MethodNameInvalid -- Using gettext scanner API. WP_CLI::debug( "Parsing file {$options['file']}", 'make-pot' ); try { @@ -35,7 +35,7 @@ public static function fromString( $text, Translations $translations, array $opt $scanner = new JsScanner( $translations ); $scanner->setFunctions( self::$options['functions'] ); - $scanner->extractCommentsStartingWith( $options['extractComments'] ); + $scanner->extractCommentsStartingWith( ...$options['extractComments'] ); $scanner->scanString( $text, $options['file'] ); } catch ( PeastException $exception ) { WP_CLI::debug( diff --git a/src/JsFunctionsScanner.php b/src/JsFunctionsScanner.php index 44136426..f40f9550 100644 --- a/src/JsFunctionsScanner.php +++ b/src/JsFunctionsScanner.php @@ -2,13 +2,19 @@ namespace WP_CLI\I18n; -use Gettext\Utils\JsFunctionsScanner as GettextJsFunctionsScanner; -use Gettext\Utils\ParsedComment; +use Gettext\Translation; use Peast\Peast; use Peast\Syntax\Node; use Peast\Traverser; -final class JsFunctionsScanner extends GettextJsFunctionsScanner { +final class JsFunctionsScanner { + + /** + * The JavaScript code to parse. + * + * @var string + */ + protected $code; /** * If not false, comments will be extracted. @@ -17,6 +23,15 @@ final class JsFunctionsScanner extends GettextJsFunctionsScanner { */ private $extract_comments = false; + /** + * Constructor. + * + * @param string $code JavaScript code to parse. + */ + public function __construct( $code ) { + $this->code = $code; + } + /** * Holds a list of source code comments already added to a string. * @@ -31,21 +46,21 @@ final class JsFunctionsScanner extends GettextJsFunctionsScanner { * * @param mixed $tag */ - public function enableCommentsExtraction( $tag = '' ) { + public function enableCommentsExtraction( $tag = '' ) { // phpcs:ignore WordPress.NamingConventions.ValidFunctionName.MethodNameInvalid -- Legacy method name for compatibility. $this->extract_comments = $tag; } /** * Disable comments extraction. */ - public function disableCommentsExtraction() { + public function disableCommentsExtraction() { // phpcs:ignore WordPress.NamingConventions.ValidFunctionName.MethodNameInvalid -- Legacy method name for compatibility. $this->extract_comments = false; } /** * {@inheritdoc} */ - public function saveGettextFunctions( $translations, array $options ) { + public function saveGettextFunctions( $translations, array $options ) { // phpcs:ignore WordPress.NamingConventions.ValidFunctionName.MethodNameInvalid -- Legacy method name for compatibility. // Ignore multiple translations for now. // @todo Add proper support for multiple translations. if ( is_array( $translations ) ) { @@ -175,7 +190,11 @@ function ( $node ) use ( &$translations, $options, &$all_comments ) { $line = $node->getLocation()->getStart()->getLine(); } - $translation = $translations->insert( $context, $original, $plural ); + $translation = $translations->addOrMerge( Translation::create( $context, $original ) ); + + if ( $plural ) { + $translation->setPlural( $plural ); + } if ( $add_reference ) { $translation->getReferences()->add( $file, $line ); @@ -199,11 +218,24 @@ function ( $node ) use ( &$translations, $options, &$all_comments ) { continue; } - $parsed_comment = ParsedComment::create( $comment->getRawText(), $comment->getLocation()->getStart()->getLine() ); - $prefixes = array_filter( (array) $this->extract_comments ); + $comment_text = $comment->getRawText(); + $comment_text = preg_replace( '/^\/\*+|\*+\/$/', '', $comment_text ); + $comment_text = preg_replace( '/^\s*\*\s?/m', '', $comment_text ); + $comment_text = trim( $comment_text ); + + $prefixes = array_filter( (array) $this->extract_comments ); + + // Check if comment starts with any of the prefixes. + $has_prefix = false; + foreach ( $prefixes as $prefix ) { + if ( 0 === stripos( $comment_text, $prefix ) ) { + $has_prefix = true; + break; + } + } - if ( $parsed_comment->checkPrefixes( $prefixes ) ) { - $translation->getComments()->add( $parsed_comment->getComment() ); + if ( $has_prefix || empty( $prefixes ) ) { + $translation->getComments()->add( $comment_text ); $this->comments_cache[] = $comment; } @@ -266,7 +298,7 @@ function ( $node ) use ( &$translations, $options, $scanner ) { * * @return array|bool Array containing the name and comments of the identifier if resolved. False if not. */ - private function resolveExpressionCallee( Node\CallExpression $node ) { + private function resolveExpressionCallee( Node\CallExpression $node ) { // phpcs:ignore WordPress.NamingConventions.ValidFunctionName.MethodNameInvalid -- Legacy method name for compatibility. $callee = $node->getCallee(); // If the callee is a simple identifier it can simply be returned. @@ -376,7 +408,7 @@ private function resolveExpressionCallee( Node\CallExpression $node ) { * * @return bool Whether or not the comment precedes the node. */ - private function commentPrecedesNode( Node\Comment $comment, Node\Node $node ) { + private function commentPrecedesNode( Node\Comment $comment, Node\Node $node ) { // phpcs:ignore WordPress.NamingConventions.ValidFunctionName.MethodNameInvalid -- Legacy method name for compatibility. // Comments should be on the same or an earlier line than the translation. if ( $node->getLocation()->getStart()->getLine() - $comment->getLocation()->getEnd()->getLine() > 1 ) { return false; diff --git a/src/JsonSchemaExtractor.php b/src/JsonSchemaExtractor.php index 81744493..92759d05 100644 --- a/src/JsonSchemaExtractor.php +++ b/src/JsonSchemaExtractor.php @@ -82,7 +82,7 @@ protected static function load_schema( $schema, $fallback ) { /** * @inheritdoc */ - public static function fromString( $text, Translations $translations, array $options = [] ) { + public static function fromString( $text, Translations $translations, array $options = [] ) { // phpcs:ignore WordPress.NamingConventions.ValidFunctionName.MethodNameInvalid -- Using gettext scanner API. $file = $options['file']; WP_CLI::debug( "Parsing file {$file}", 'make-pot' ); diff --git a/src/MakePotCommand.php b/src/MakePotCommand.php index b28a2fcc..e34fda4d 100644 --- a/src/MakePotCommand.php +++ b/src/MakePotCommand.php @@ -3,10 +3,10 @@ namespace WP_CLI\I18n; use Gettext\Generator\PoGenerator; +use Gettext\Loader\PoLoader; use Gettext\Merge; use Gettext\Translation; use Gettext\Translations; -use Gettext\Utils\ParsedComment; use WP_CLI; use WP_CLI_Command; use WP_CLI\Utils; @@ -428,8 +428,8 @@ function ( $file ) { WP_CLI::debug( sprintf( 'Ignoring any string already existing in: %s', $file ), 'make-pot' ); - $this->exceptions[ $file ] = Translations::create(); - Po::fromFile( $file, $this->exceptions[ $file ] ); + $loader = new PoLoader(); + $this->exceptions[ $file ] = $loader->loadFile( $file ); } } @@ -592,9 +592,11 @@ protected function extract_strings() { // Add existing strings first but don't keep headers. if ( ! empty( $this->merge ) ) { - $existing_translations = Translations::create(); - Po::fromFile( $this->merge, $existing_translations ); - $translations->mergeWith( $existing_translations, Merge::TRANSLATIONS_OURS | Merge::HEADERS_OURS ); + $loader = new PoLoader(); + foreach ( (array) $this->merge as $file ) { + $existing_translations = $loader->loadFile( $file ); + $translations->mergeWith( $existing_translations, Merge::TRANSLATIONS_OURS | Merge::HEADERS_OURS ); + } } $translations->setDescription( $this->get_file_comment() ); @@ -785,13 +787,12 @@ protected function audit_strings( $translations ) { $location = ''; // There might not be any file references. - foreach( $references as $file => $lines ) { + foreach ( $references as $file => $lines ) { if ( count( $lines ) > 0 ) { $location = "$file:$lines[0]"; } } - // Check 1: Flag strings with placeholders that should have translator comments. if ( $translation->getComments()->count() === 0 && @@ -831,12 +832,12 @@ function ( $comment ) { $comments = array_filter( $comments, function ( $comment ) use ( &$unique_comments ) { - /** @var ParsedComment|string $comment */ - if ( in_array( ( $comment instanceof ParsedComment ? $comment->getComment() : $comment ), $unique_comments, true ) ) { + /** @var string $comment */ + if ( in_array( $comment, $unique_comments, true ) ) { return null; } - $unique_comments[] = ( $comment instanceof ParsedComment ? $comment->getComment() : $comment ); + $unique_comments[] = $comment; return $comment; } diff --git a/src/MapCodeExtractor.php b/src/MapCodeExtractor.php index af6730a6..7d8b6c63 100644 --- a/src/MapCodeExtractor.php +++ b/src/MapCodeExtractor.php @@ -23,7 +23,7 @@ final class MapCodeExtractor { /** * {@inheritdoc} */ - public static function fromString( $text, Translations $translations, array $options = [] ) { + public static function fromString( $text, Translations $translations, array $options = [] ) { // phpcs:ignore WordPress.NamingConventions.ValidFunctionName.MethodNameInvalid -- Using gettext scanner API. if ( ! array_key_exists( 'file', $options ) || substr( $options['file'], -7 ) !== '.js.map' ) { return; } diff --git a/src/PhpCodeExtractor.php b/src/PhpCodeExtractor.php index f8e0e495..c9f25c0e 100644 --- a/src/PhpCodeExtractor.php +++ b/src/PhpCodeExtractor.php @@ -46,7 +46,7 @@ class PhpCodeExtractor { /** * {@inheritdoc} */ - public static function fromString( $text, Translations $translations, array $options = [] ) { + public static function fromString( $text, Translations $translations, array $options = [] ) { // phpcs:ignore WordPress.NamingConventions.ValidFunctionName.MethodNameInvalid -- Using gettext scanner API. $options = static::$options + $options; WP_CLI::debug( "Parsing file {$options['file']}", 'make-pot' ); diff --git a/src/PhpFunctionsScanner.php b/src/PhpFunctionsScanner.php index 376da360..07294fc6 100644 --- a/src/PhpFunctionsScanner.php +++ b/src/PhpFunctionsScanner.php @@ -2,14 +2,63 @@ namespace WP_CLI\I18n; -use Gettext\Utils\PhpFunctionsScanner as GettextPhpFunctionsScanner; +use Gettext\Translation; -class PhpFunctionsScanner extends GettextPhpFunctionsScanner { +class PhpFunctionsScanner { + + /** + * The PHP code to parse. + * + * @var string + */ + protected $code; + + /** + * Parsed PHP functions. + * + * @var array + */ + protected $functions = []; + + /** + * Constructor. + * + * @param string $code PHP code to parse. + */ + public function __construct( $code ) { + $this->code = $code; + } + + /** + * Get parsed functions from PHP code. + * + * @param array $constants Constants to replace. + * @return array + */ + public function getFunctions( array $constants = [] ) { // phpcs:ignore WordPress.NamingConventions.ValidFunctionName.MethodNameInvalid -- Legacy method name for compatibility. + if ( empty( $this->functions ) ) { + $this->functions = $this->parseFunctions( $constants ); + } + return $this->functions; + } + + /** + * Parse PHP code to extract function calls. + * + * @param array $constants Constants to replace. + * @return array + */ + protected function parseFunctions( array $constants = [] ) { // phpcs:ignore WordPress.NamingConventions.ValidFunctionName.MethodNameInvalid -- Legacy method name for compatibility. + // This is a simplified implementation. + // The actual parsing is done by PhpScanner in gettext v5. + unset( $constants ); // Unused parameter, kept for compatibility. + return []; + } /** * {@inheritdoc} */ - public function saveGettextFunctions( $translations, array $options ) { + public function saveGettextFunctions( $translations, array $options ) { // phpcs:ignore WordPress.NamingConventions.ValidFunctionName.MethodNameInvalid -- Legacy method name for compatibility. // Ignore multiple translations for now. // @todo Add proper support for multiple translations. if ( is_array( $translations ) ) { @@ -71,10 +120,14 @@ public function saveGettextFunctions( $translations, array $options ) { continue; } - $translation = $translations->insert( $context, $original, $plural ); + $translation = $translations->addOrMerge( Translation::create( $context, $original ) ); + + if ( $plural ) { + $translation->setPlural( $plural ); + } if ( $add_reference ) { - $translation = $translation->getReferences()->add( $file, $line ); + $translation->getReferences()->add( $file, $line ); } if ( @@ -86,7 +139,7 @@ public function saveGettextFunctions( $translations, array $options ) { if ( isset( $function[3] ) ) { foreach ( $function[3] as $extracted_comment ) { - $translation = $translation->getComments()->add( $extracted_comment ); + $translation->getComments()->add( $extracted_comment ); } } }