From 1b1138b6aa7c6545236bf028bda38340612b3016 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 14 Feb 2026 22:33:11 +0000 Subject: [PATCH 1/7] Initial plan From ac758cf400587ff8df41bf9b4bc72db278dd8f8f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 14 Feb 2026 22:38:15 +0000 Subject: [PATCH 2/7] Extract validation logic into StringAuditTrait and use in all make-* commands Co-authored-by: swissspidy <841956+swissspidy@users.noreply.github.com> --- src/MakeJsonCommand.php | 4 + src/MakeMoCommand.php | 4 + src/MakePhpCommand.php | 4 + src/MakePotCommand.php | 130 +------------------------ src/StringAuditTrait.php | 201 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 216 insertions(+), 127 deletions(-) create mode 100644 src/StringAuditTrait.php diff --git a/src/MakeJsonCommand.php b/src/MakeJsonCommand.php index b52c9a29..07ae20bf 100644 --- a/src/MakeJsonCommand.php +++ b/src/MakeJsonCommand.php @@ -13,6 +13,7 @@ use SplFileInfo; class MakeJsonCommand extends WP_CLI_Command { + use StringAuditTrait; /** * Options passed to json_encode(). * @@ -220,6 +221,9 @@ protected function make_json( $source_file, $destination, $map, $domain, $extens PoExtractor::fromFile( $source_file, $translations ); + // Audit strings for potential issues. + $this->perform_string_audit( $translations ); + $base_file_name = basename( $source_file, '.po' ); $domain = ( ! empty( $domain ) ) ? $domain : $translations->getDomain(); diff --git a/src/MakeMoCommand.php b/src/MakeMoCommand.php index 89bc9865..9c2b56a6 100644 --- a/src/MakeMoCommand.php +++ b/src/MakeMoCommand.php @@ -13,6 +13,7 @@ class MakeMoCommand extends WP_CLI_Command { use JsStringFilterTrait; + use StringAuditTrait; /** * Create MO files from PO files. @@ -92,6 +93,9 @@ public function __invoke( $args, $assoc_args ) { $translations = Translations::fromPoFile( $file->getPathname() ); + // Audit strings for potential issues. + $this->perform_string_audit( $translations ); + // Remove JS-only strings from MO files to keep them small. $this->remove_js_only_strings( $translations ); diff --git a/src/MakePhpCommand.php b/src/MakePhpCommand.php index adecee2c..83c4a99d 100644 --- a/src/MakePhpCommand.php +++ b/src/MakePhpCommand.php @@ -13,6 +13,7 @@ class MakePhpCommand extends WP_CLI_Command { use JsStringFilterTrait; + use StringAuditTrait; /** * Create PHP files from PO files. @@ -86,6 +87,9 @@ public function __invoke( $args, $assoc_args ) { $translations = Translations::fromPoFile( $file->getPathname() ); + // Audit strings for potential issues. + $this->perform_string_audit( $translations ); + // Remove JS-only strings from PHP files to keep them small. $this->remove_js_only_strings( $translations ); diff --git a/src/MakePotCommand.php b/src/MakePotCommand.php index 3d14245d..0e5b39e1 100644 --- a/src/MakePotCommand.php +++ b/src/MakePotCommand.php @@ -14,6 +14,7 @@ use IteratorIterator; class MakePotCommand extends WP_CLI_Command { + use StringAuditTrait; /** * @var string */ @@ -780,133 +781,8 @@ protected function extract_strings() { * @param Translations $translations Translations object. */ protected function audit_strings( $translations ) { - foreach ( $translations as $translation ) { - /** @var Translation $translation */ - - $references = $translation->getReferences(); - - // 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() && - preg_match( self::SPRINTF_PLACEHOLDER_REGEX, $translation->getOriginal(), $placeholders ) >= 1 - ) { - $message = sprintf( - 'The string "%1$s" contains placeholders but has no "translators:" comment to clarify their meaning. %2$s', - $translation->getOriginal(), - $location - ); - WP_CLI::warning( $message ); - } - - // Check 2: Flag strings with different translator comments. - if ( $translation->hasExtractedComments() ) { - $comments = $translation->getExtractedComments(); - - // Remove plugin header information from comments. - $comments = array_filter( - $comments, - function ( $comment ) { - /** @var ParsedComment|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 ) ) { - return null; - } - } - - return $comment; - } - ); - - $unique_comments = array(); - - // Remove duplicate comments. - $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 ) ) { - return null; - } - - $unique_comments[] = ( $comment instanceof ParsedComment ? $comment->getComment() : $comment ); - - return $comment; - } - ); - - $comments_count = count( $comments ); - - if ( $comments_count > 1 ) { - $message = sprintf( - "The string \"%1\$s\" has %2\$d different translator comments. %3\$s\n%4\$s", - $translation->getOriginal(), - $comments_count, - $location, - implode( "\n", $unique_comments ) - ); - WP_CLI::warning( $message ); - } - } - - $non_placeholder_content = trim( preg_replace( '`^([\'"])(.*)\1$`Ds', '$2', $translation->getOriginal() ) ); - $non_placeholder_content = preg_replace( self::SPRINTF_PLACEHOLDER_REGEX, '', $non_placeholder_content ); - - // Check 3: Flag empty strings without any translatable content. - if ( '' === $non_placeholder_content ) { - $message = sprintf( - 'Found string without translatable content. %s', - $location - ); - WP_CLI::warning( $message ); - } - - // Check 4: Flag strings with multiple unordered placeholders (%s %s %s vs. %1$s %2$s %3$s). - $unordered_matches_count = preg_match_all( self::UNORDERED_SPRINTF_PLACEHOLDER_REGEX, $translation->getOriginal(), $unordered_matches ); - $unordered_matches = $unordered_matches[0]; - - if ( $unordered_matches_count >= 2 ) { - $message = sprintf( - 'Multiple placeholders should be ordered. %s', - $location - ); - WP_CLI::warning( $message ); - } - - if ( $translation->hasPlural() ) { - preg_match_all( self::SPRINTF_PLACEHOLDER_REGEX, $translation->getOriginal(), $single_placeholders ); - $single_placeholders = $single_placeholders[0]; - - preg_match_all( self::SPRINTF_PLACEHOLDER_REGEX, $translation->getPlural(), $plural_placeholders ); - $plural_placeholders = $plural_placeholders[0]; - - // see https://developer.wordpress.org/plugins/internationalization/how-to-internationalize-your-plugin/#plurals - if ( count( $single_placeholders ) < count( $plural_placeholders ) ) { - // Check 5: Flag things like _n( 'One comment', '%s Comments' ) - $message = sprintf( - 'Missing singular placeholder, needed for some languages. See https://developer.wordpress.org/plugins/internationalization/how-to-internationalize-your-plugin/#plurals %s', - $location - ); - WP_CLI::warning( $message ); - } else { - // Reordering is fine, but mismatched placeholders is probably wrong. - sort( $single_placeholders ); - sort( $plural_placeholders ); - - // Check 6: Flag things like _n( '%s Comment (%d)', '%s Comments (%s)' ) - if ( $single_placeholders !== $plural_placeholders ) { - $message = sprintf( - 'Mismatched placeholders for singular and plural string. %s', - $location - ); - WP_CLI::warning( $message ); - } - } - } - } + // Call the trait's perform_string_audit method with the appropriate file headers. + $this->perform_string_audit( $translations, $this->get_file_headers( $this->project_type ) ); } /** diff --git a/src/StringAuditTrait.php b/src/StringAuditTrait.php new file mode 100644 index 00000000..b8a376ed --- /dev/null +++ b/src/StringAuditTrait.php @@ -0,0 +1,201 @@ +getReferences(); + + // 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() && + preg_match( self::SPRINTF_PLACEHOLDER_REGEX, $translation->getOriginal(), $placeholders ) >= 1 + ) { + $message = sprintf( + 'The string "%1$s" contains placeholders but has no "translators:" comment to clarify their meaning. %2$s', + $translation->getOriginal(), + $location + ); + WP_CLI::warning( $message ); + } + + // Check 2: Flag strings with different translator comments. + if ( $translation->hasExtractedComments() ) { + $comments = $translation->getExtractedComments(); + + // Remove plugin/theme header information from comments. + $comments = array_filter( + $comments, + function ( $comment ) use ( $file_headers ) { + /** @var \Gettext\Comments\ParsedComment|string $comment */ + /** @var string $file_header */ + foreach ( $file_headers as $file_header ) { + if ( 0 === strpos( ( $comment instanceof \Gettext\Comments\ParsedComment ? $comment->getComment() : $comment ), $file_header ) ) { + return null; + } + } + + return $comment; + } + ); + + $unique_comments = array(); + + // Remove duplicate comments. + $comments = array_filter( + $comments, + function ( $comment ) use ( &$unique_comments ) { + /** @var \Gettext\Comments\ParsedComment|string $comment */ + if ( in_array( ( $comment instanceof \Gettext\Comments\ParsedComment ? $comment->getComment() : $comment ), $unique_comments, true ) ) { + return null; + } + + $unique_comments[] = ( $comment instanceof \Gettext\Comments\ParsedComment ? $comment->getComment() : $comment ); + + return $comment; + } + ); + + $comments_count = count( $comments ); + + if ( $comments_count > 1 ) { + $message = sprintf( + "The string \"%1\$s\" has %2\$d different translator comments. %3\$s\n%4\$s", + $translation->getOriginal(), + $comments_count, + $location, + implode( "\n", $unique_comments ) + ); + WP_CLI::warning( $message ); + } + } + + $non_placeholder_content = trim( preg_replace( '`^([\'"])(.*)\1$`Ds', '$2', $translation->getOriginal() ) ); + $non_placeholder_content = preg_replace( self::SPRINTF_PLACEHOLDER_REGEX, '', $non_placeholder_content ); + + // Check 3: Flag empty strings without any translatable content. + if ( '' === $non_placeholder_content ) { + $message = sprintf( + 'Found string without translatable content. %s', + $location + ); + WP_CLI::warning( $message ); + } + + // Check 4: Flag strings with multiple unordered placeholders (%s %s %s vs. %1$s %2$s %3$s). + $unordered_matches_count = preg_match_all( self::UNORDERED_SPRINTF_PLACEHOLDER_REGEX, $translation->getOriginal(), $unordered_matches ); + $unordered_matches = $unordered_matches[0]; + + if ( $unordered_matches_count >= 2 ) { + $message = sprintf( + 'Multiple placeholders should be ordered. %s', + $location + ); + WP_CLI::warning( $message ); + } + + if ( $translation->hasPlural() ) { + preg_match_all( self::SPRINTF_PLACEHOLDER_REGEX, $translation->getOriginal(), $single_placeholders ); + $single_placeholders = $single_placeholders[0]; + + preg_match_all( self::SPRINTF_PLACEHOLDER_REGEX, $translation->getPlural(), $plural_placeholders ); + $plural_placeholders = $plural_placeholders[0]; + + // see https://developer.wordpress.org/plugins/internationalization/how-to-internationalize-your-plugin/#plurals + if ( count( $single_placeholders ) < count( $plural_placeholders ) ) { + // Check 5: Flag things like _n( 'One comment', '%s Comments' ) + $message = sprintf( + 'Missing singular placeholder, needed for some languages. See https://developer.wordpress.org/plugins/internationalization/how-to-internationalize-your-plugin/#plurals %s', + $location + ); + WP_CLI::warning( $message ); + } else { + // Reordering is fine, but mismatched placeholders is probably wrong. + sort( $single_placeholders ); + sort( $plural_placeholders ); + + // Check 6: Flag things like _n( '%s Comment (%d)', '%s Comments (%s)' ) + if ( $single_placeholders !== $plural_placeholders ) { + $message = sprintf( + 'Mismatched placeholders for singular and plural string. %s', + $location + ); + WP_CLI::warning( $message ); + } + } + } + } + } +} From 7acb9fdfe0a97ebca129547d615ca3ffe098a120 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 14 Feb 2026 22:41:36 +0000 Subject: [PATCH 3/7] Add validation tests for make-mo, make-php, and make-json commands Co-authored-by: swissspidy <841956+swissspidy@users.noreply.github.com> --- features/makejson.feature | 48 ++++++++++++++++++++ features/makemo.feature | 48 ++++++++++++++++++++ features/makephp.feature | 48 ++++++++++++++++++++ src/MakeJsonCommand.php | 1 + src/MakePotCommand.php | 1 + src/StringAuditTrait.php | 94 ++++++++++++++++++--------------------- 6 files changed, 189 insertions(+), 51 deletions(-) diff --git a/features/makejson.feature b/features/makejson.feature index f5314ef5..007e9b8d 100644 --- a/features/makejson.feature +++ b/features/makejson.feature @@ -1089,3 +1089,51 @@ Feature: Split PO files into JSON files. """ "source":"app.min.admin.js" """ + + Scenario: Prints validation warnings for strings with placeholders + Given an empty foo-plugin directory + And a foo-plugin/foo-plugin-de_DE.po file: + """ + # Copyright (C) 2018 Foo Plugin + # This file is distributed under the same license as the Foo Plugin package. + msgid "" + msgstr "" + "Project-Id-Version: Foo Plugin\n" + "Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/foo-plugin\n" + "Last-Translator: FULL NAME \n" + "Language-Team: LANGUAGE \n" + "Language: de_DE\n" + "MIME-Version: 1.0\n" + "Content-Type: text/plain; charset=UTF-8\n" + "Content-Transfer-Encoding: 8bit\n" + "POT-Creation-Date: 2018-05-02T22:06:24+00:00\n" + "PO-Revision-Date: 2018-05-02T22:06:24+00:00\n" + "X-Domain: foo-plugin\n" + "Plural-Forms: nplurals=2; plural=(n != 1);\n" + + #: foo-plugin.js:10 + msgid "Hello, %s" + msgstr "Hallo, %s" + + #: foo-plugin.js:15 + msgid "One Comment" + msgid_plural "%s Comments" + msgstr[0] "Ein Kommentar" + msgstr[1] "%s Kommentare" + """ + + When I try `wp i18n make-json foo-plugin` + Then STDOUT should contain: + """ + Success: Created 1 file. + """ + And STDERR should contain: + """ + Warning: The string "Hello, %s" contains placeholders but has no "translators:" comment to clarify their meaning. (foo-plugin.js:10) + """ + And STDERR should contain: + """ + Warning: Missing singular placeholder, needed for some languages. See https://developer.wordpress.org/plugins/internationalization/how-to-internationalize-your-plugin/#plurals (foo-plugin.js:15) + """ + And the return code should be 0 + diff --git a/features/makemo.feature b/features/makemo.feature index ae0e2585..9f96a98c 100644 --- a/features/makemo.feature +++ b/features/makemo.feature @@ -254,3 +254,51 @@ Feature: Generate MO files from PO files JS Only Translation """ + Scenario: Prints validation warnings for strings with placeholders + Given an empty foo-plugin directory + And a foo-plugin/foo-plugin-de_DE.po file: + """ + # Copyright (C) 2018 Foo Plugin + # This file is distributed under the same license as the Foo Plugin package. + msgid "" + msgstr "" + "Project-Id-Version: Foo Plugin\n" + "Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/foo-plugin\n" + "Last-Translator: FULL NAME \n" + "Language-Team: LANGUAGE \n" + "Language: de_DE\n" + "MIME-Version: 1.0\n" + "Content-Type: text/plain; charset=UTF-8\n" + "Content-Transfer-Encoding: 8bit\n" + "POT-Creation-Date: 2018-05-02T22:06:24+00:00\n" + "PO-Revision-Date: 2018-05-02T22:06:24+00:00\n" + "X-Domain: foo-plugin\n" + "Plural-Forms: nplurals=2; plural=(n != 1);\n" + + #: foo-plugin.php:10 + msgid "Hello, %s" + msgstr "Hallo, %s" + + #: foo-plugin.php:15 + msgid "One Comment" + msgid_plural "%s Comments" + msgstr[0] "Ein Kommentar" + msgstr[1] "%s Kommentare" + """ + + When I try `wp i18n make-mo foo-plugin` + Then STDOUT should contain: + """ + Success: Created 1 file. + """ + And STDERR should contain: + """ + Warning: The string "Hello, %s" contains placeholders but has no "translators:" comment to clarify their meaning. (foo-plugin.php:10) + """ + And STDERR should contain: + """ + Warning: Missing singular placeholder, needed for some languages. See https://developer.wordpress.org/plugins/internationalization/how-to-internationalize-your-plugin/#plurals (foo-plugin.php:15) + """ + And the return code should be 0 + And the foo-plugin/foo-plugin-de_DE.mo file should exist + diff --git a/features/makephp.feature b/features/makephp.feature index 66f12711..cd011190 100644 --- a/features/makephp.feature +++ b/features/makephp.feature @@ -364,3 +364,51 @@ Feature: Generate PHP files from PO files JS Only Translation """ + Scenario: Prints validation warnings for strings with placeholders + Given an empty foo-plugin directory + And a foo-plugin/foo-plugin-de_DE.po file: + """ + # Copyright (C) 2018 Foo Plugin + # This file is distributed under the same license as the Foo Plugin package. + msgid "" + msgstr "" + "Project-Id-Version: Foo Plugin\n" + "Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/foo-plugin\n" + "Last-Translator: FULL NAME \n" + "Language-Team: LANGUAGE \n" + "Language: de_DE\n" + "MIME-Version: 1.0\n" + "Content-Type: text/plain; charset=UTF-8\n" + "Content-Transfer-Encoding: 8bit\n" + "POT-Creation-Date: 2018-05-02T22:06:24+00:00\n" + "PO-Revision-Date: 2018-05-02T22:06:24+00:00\n" + "X-Domain: foo-plugin\n" + "Plural-Forms: nplurals=2; plural=(n != 1);\n" + + #: foo-plugin.php:10 + msgid "Hello, %s" + msgstr "Hallo, %s" + + #: foo-plugin.php:15 + msgid "One Comment" + msgid_plural "%s Comments" + msgstr[0] "Ein Kommentar" + msgstr[1] "%s Kommentare" + """ + + When I try `wp i18n make-php foo-plugin` + Then STDOUT should contain: + """ + Success: Created 1 file. + """ + And STDERR should contain: + """ + Warning: The string "Hello, %s" contains placeholders but has no "translators:" comment to clarify their meaning. (foo-plugin.php:10) + """ + And STDERR should contain: + """ + Warning: Missing singular placeholder, needed for some languages. See https://developer.wordpress.org/plugins/internationalization/how-to-internationalize-your-plugin/#plurals (foo-plugin.php:15) + """ + And the return code should be 0 + And the foo-plugin/foo-plugin-de_DE.l10n.php file should exist + diff --git a/src/MakeJsonCommand.php b/src/MakeJsonCommand.php index 07ae20bf..c4f6b74e 100644 --- a/src/MakeJsonCommand.php +++ b/src/MakeJsonCommand.php @@ -14,6 +14,7 @@ class MakeJsonCommand extends WP_CLI_Command { use StringAuditTrait; + /** * Options passed to json_encode(). * diff --git a/src/MakePotCommand.php b/src/MakePotCommand.php index 0e5b39e1..884c5ab3 100644 --- a/src/MakePotCommand.php +++ b/src/MakePotCommand.php @@ -15,6 +15,7 @@ class MakePotCommand extends WP_CLI_Command { use StringAuditTrait; + /** * @var string */ diff --git a/src/StringAuditTrait.php b/src/StringAuditTrait.php index b8a376ed..866e3a4f 100644 --- a/src/StringAuditTrait.php +++ b/src/StringAuditTrait.php @@ -7,15 +7,46 @@ trait StringAuditTrait { /** - * Regular expression to match sprintf placeholders. + * Audits translation strings for potential issues with placeholders. + * + * Performs validation checks on strings including: + * - Flags strings with placeholders that lack translator comments + * - Flags strings with inconsistent translator comments + * - Flags empty strings with no translatable content + * - Flags multiple unordered placeholders + * - Flags missing singular placeholders in plural translations + * - Flags mismatched placeholders between singular/plural forms * - * @see MakePotCommand::SPRINTF_PLACEHOLDER_REGEX + * @param Translations $translations The translations to audit. + * @param array $file_headers Optional. File headers to filter out from comments. Default empty array. */ - const SPRINTF_PLACEHOLDER_REGEX = '/(?: - (?hasExtractedComments() && - preg_match( self::SPRINTF_PLACEHOLDER_REGEX, $translation->getOriginal(), $placeholders ) >= 1 + preg_match( $sprintf_placeholder_regex, $translation->getOriginal(), $placeholders ) >= 1 ) { $message = sprintf( 'The string "%1$s" contains placeholders but has no "translators:" comment to clarify their meaning. %2$s', @@ -143,7 +135,7 @@ function ( $comment ) use ( &$unique_comments ) { } $non_placeholder_content = trim( preg_replace( '`^([\'"])(.*)\1$`Ds', '$2', $translation->getOriginal() ) ); - $non_placeholder_content = preg_replace( self::SPRINTF_PLACEHOLDER_REGEX, '', $non_placeholder_content ); + $non_placeholder_content = preg_replace( $sprintf_placeholder_regex, '', $non_placeholder_content ); // Check 3: Flag empty strings without any translatable content. if ( '' === $non_placeholder_content ) { @@ -155,7 +147,7 @@ function ( $comment ) use ( &$unique_comments ) { } // Check 4: Flag strings with multiple unordered placeholders (%s %s %s vs. %1$s %2$s %3$s). - $unordered_matches_count = preg_match_all( self::UNORDERED_SPRINTF_PLACEHOLDER_REGEX, $translation->getOriginal(), $unordered_matches ); + $unordered_matches_count = preg_match_all( $unordered_sprintf_placeholder_regex, $translation->getOriginal(), $unordered_matches ); $unordered_matches = $unordered_matches[0]; if ( $unordered_matches_count >= 2 ) { @@ -167,10 +159,10 @@ function ( $comment ) use ( &$unique_comments ) { } if ( $translation->hasPlural() ) { - preg_match_all( self::SPRINTF_PLACEHOLDER_REGEX, $translation->getOriginal(), $single_placeholders ); + preg_match_all( $sprintf_placeholder_regex, $translation->getOriginal(), $single_placeholders ); $single_placeholders = $single_placeholders[0]; - preg_match_all( self::SPRINTF_PLACEHOLDER_REGEX, $translation->getPlural(), $plural_placeholders ); + preg_match_all( $sprintf_placeholder_regex, $translation->getPlural(), $plural_placeholders ); $plural_placeholders = $plural_placeholders[0]; // see https://developer.wordpress.org/plugins/internationalization/how-to-internationalize-your-plugin/#plurals From 6ef98883158c82af8ab9f9df96745ef3a2485c1e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 14 Feb 2026 22:43:50 +0000 Subject: [PATCH 4/7] Fix array_filter callbacks to return false instead of null Co-authored-by: swissspidy <841956+swissspidy@users.noreply.github.com> --- src/StringAuditTrait.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/StringAuditTrait.php b/src/StringAuditTrait.php index 866e3a4f..78658460 100644 --- a/src/StringAuditTrait.php +++ b/src/StringAuditTrait.php @@ -95,7 +95,7 @@ function ( $comment ) use ( $file_headers ) { /** @var string $file_header */ foreach ( $file_headers as $file_header ) { if ( 0 === strpos( ( $comment instanceof \Gettext\Comments\ParsedComment ? $comment->getComment() : $comment ), $file_header ) ) { - return null; + return false; } } @@ -111,7 +111,7 @@ function ( $comment ) use ( $file_headers ) { function ( $comment ) use ( &$unique_comments ) { /** @var \Gettext\Comments\ParsedComment|string $comment */ if ( in_array( ( $comment instanceof \Gettext\Comments\ParsedComment ? $comment->getComment() : $comment ), $unique_comments, true ) ) { - return null; + return false; } $unique_comments[] = ( $comment instanceof \Gettext\Comments\ParsedComment ? $comment->getComment() : $comment ); From 8a861ea77280b55d47732a21e00637a6d5132622 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 15 Feb 2026 12:14:08 +0000 Subject: [PATCH 5/7] Fix incorrect ParsedComment namespace causing strpos() warning Co-authored-by: swissspidy <841956+swissspidy@users.noreply.github.com> --- src/StringAuditTrait.php | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/StringAuditTrait.php b/src/StringAuditTrait.php index 78658460..b9cf94e7 100644 --- a/src/StringAuditTrait.php +++ b/src/StringAuditTrait.php @@ -2,7 +2,9 @@ namespace WP_CLI\I18n; +use Gettext\Translation; use Gettext\Translations; +use Gettext\Utils\ParsedComment; use WP_CLI; trait StringAuditTrait { @@ -63,7 +65,7 @@ protected function perform_string_audit( Translations $translations, array $file )/x'; foreach ( $translations as $translation ) { - /** @var \Gettext\Translation $translation */ + /** @var Translation $translation */ $references = $translation->getReferences(); @@ -91,10 +93,10 @@ protected function perform_string_audit( Translations $translations, array $file $comments = array_filter( $comments, function ( $comment ) use ( $file_headers ) { - /** @var \Gettext\Comments\ParsedComment|string $comment */ + /** @var ParsedComment|string $comment */ /** @var string $file_header */ foreach ( $file_headers as $file_header ) { - if ( 0 === strpos( ( $comment instanceof \Gettext\Comments\ParsedComment ? $comment->getComment() : $comment ), $file_header ) ) { + if ( 0 === strpos( ( $comment instanceof ParsedComment ? $comment->getComment() : $comment ), $file_header ) ) { return false; } } @@ -109,12 +111,12 @@ function ( $comment ) use ( $file_headers ) { $comments = array_filter( $comments, function ( $comment ) use ( &$unique_comments ) { - /** @var \Gettext\Comments\ParsedComment|string $comment */ - if ( in_array( ( $comment instanceof \Gettext\Comments\ParsedComment ? $comment->getComment() : $comment ), $unique_comments, true ) ) { + /** @var ParsedComment|string $comment */ + if ( in_array( ( $comment instanceof ParsedComment ? $comment->getComment() : $comment ), $unique_comments, true ) ) { return false; } - $unique_comments[] = ( $comment instanceof \Gettext\Comments\ParsedComment ? $comment->getComment() : $comment ); + $unique_comments[] = ( $comment instanceof ParsedComment ? $comment->getComment() : $comment ); return $comment; } From 2a662c6a94f45d49af8a660bad6da01fa3659224 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 15 Feb 2026 17:22:21 +0000 Subject: [PATCH 6/7] Update makephp test to expect validation warnings in STDERR Co-authored-by: swissspidy <841956+swissspidy@users.noreply.github.com> --- features/makephp.feature | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/features/makephp.feature b/features/makephp.feature index cd011190..69894eb5 100644 --- a/features/makephp.feature +++ b/features/makephp.feature @@ -150,7 +150,10 @@ Feature: Generate PHP files from PO files Success: Created 1 file. """ And the return code should be 0 - And STDERR should be empty + And STDERR should contain: + """ + Warning: + """ And the foo-plugin/foo-plugin-de_DE.l10n.php file should contain: """ Date: Sun, 15 Feb 2026 17:23:56 +0000 Subject: [PATCH 7/7] Change make-php tests to use "try" instead of "run" to allow warnings Co-authored-by: swissspidy <841956+swissspidy@users.noreply.github.com> --- features/makephp.feature | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/features/makephp.feature b/features/makephp.feature index 69894eb5..7e256411 100644 --- a/features/makephp.feature +++ b/features/makephp.feature @@ -144,7 +144,7 @@ Feature: Generate PHP files from PO files msgstr "[%s] Export personenbezogener Daten" """ - When I run `wp i18n make-php foo-plugin` + When I try `wp i18n make-php foo-plugin` Then STDOUT should contain: """ Success: Created 1 file. @@ -197,7 +197,7 @@ Feature: Generate PHP files from PO files msgstr[1] "Sie haben %d neue Nachrichten" """ - When I run `wp i18n make-php foo-plugin` + When I try `wp i18n make-php foo-plugin` Then STDOUT should contain: """ Success: Created 1 file. @@ -252,7 +252,7 @@ Feature: Generate PHP files from PO files msgstr[1] "" """ - When I run `wp i18n make-php foo-plugin` + When I try `wp i18n make-php foo-plugin` Then STDOUT should contain: """ Success: Created 1 file.