From d130e94c182957469b7464ae12805e686343ff5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Amieiro?= <1667814+amieiro@users.noreply.github.com> Date: Mon, 4 Nov 2024 11:26:31 +0100 Subject: [PATCH 1/9] Add the check for the leading and trailing spaces --- WordPress/Sniffs/WP/I18nSniff.php | 44 +++++++++++++++++++++ WordPress/Tests/WP/I18nUnitTest.1.inc | 23 +++++++++++ WordPress/Tests/WP/I18nUnitTest.1.inc.fixed | 23 +++++++++++ WordPress/Tests/WP/I18nUnitTest.php | 22 +++++++++++ 4 files changed, 112 insertions(+) diff --git a/WordPress/Sniffs/WP/I18nSniff.php b/WordPress/Sniffs/WP/I18nSniff.php index 33158ed08e..b740495386 100644 --- a/WordPress/Sniffs/WP/I18nSniff.php +++ b/WordPress/Sniffs/WP/I18nSniff.php @@ -380,6 +380,7 @@ public function process_parameters( $stackPtr, $group_name, $matched_content, $p $has_content = $this->check_string_has_translatable_content( $matched_content, $param_name, $param_info ); if ( true === $has_content ) { $this->check_string_has_no_html_wrapper( $matched_content, $param_name, $param_info ); + $this->check_string_has_no_leading_trailing_spaces( $matched_content, $param_name, $param_info ); } } } @@ -804,6 +805,49 @@ private function check_string_has_no_html_wrapper( $matched_content, $param_name } } + /** + * Check if a translatable string has leading or trailing spaces. + * + * @since 3.2.0 + * + * @param string $matched_content The token content (function name) which was matched + * in lowercase. + * @param string $param_name The name of the parameter being examined. + * @param array|false $param_info Parameter info array for an individual parameter, + * as received from the PassedParameters class. + * + * @return void + */ + private function check_string_has_no_leading_trailing_spaces( $matched_content, $param_name, $param_info ) { + // Strip surrounding quotes. + $content_without_quotes = TextStrings::stripQuotes( $param_info['clean'] ); + $first_non_empty = $this->phpcsFile->findNext( Tokens::$emptyTokens, $param_info['start'], ( $param_info['end'] + 1 ), true ); + + if ( ltrim( $content_without_quotes ) !== $content_without_quotes ) { + $this->phpcsFile->addError( + 'Translatable string should not have leading spaces. Found: %s', + $first_non_empty, + 'NoLeadingTrailingSpaces', + array( $param_info['clean'] ) + ); + } + if ( rtrim( $content_without_quotes ) !== $content_without_quotes ) { + $this->phpcsFile->addError( + 'Translatable string should not have trailing spaces. Found: %s', + $first_non_empty, + 'NoLeadingTrailingSpaces', + array( $param_info['clean'] ) + ); + } + + if ('cadena' == $param_info['raw']) { + fwrite(STDERR, "\n" . '$matched_content: ' . print_r($matched_content, TRUE)); + fwrite(STDERR, "\n" . '$param_name: ' . print_r($param_name, TRUE)); + fwrite(STDERR, "\n" . '$param_info: ' . print_r($param_info, TRUE)); + exit(1); + } + } + /** * Check for inconsistencies in the placeholders between single and plural form of the translatable text string. * diff --git a/WordPress/Tests/WP/I18nUnitTest.1.inc b/WordPress/Tests/WP/I18nUnitTest.1.inc index 3abf8ec3ba..92b5cfff04 100644 --- a/WordPress/Tests/WP/I18nUnitTest.1.inc +++ b/WordPress/Tests/WP/I18nUnitTest.1.inc @@ -328,4 +328,27 @@ MyNamespace\__( 'foo', 'my-slug' ); // Ok. namespace\esc_html_e( 'foo', '' ); // The sniff should start flagging this once it can resolve relative namespaces. namespace\Sub\translate( 'foo', 'my-slug' ); // Ok. +__( ' string', 'my-slug' ); // Bad: leading space. +_e( 'string ', 'my-slug' ); // Bad: trailing space. +_x( ' string ', 'context', 'my-slug' ); // Bad: leading and trailing spaces. +_ex( ' string', 'context', 'my-slug' ); // Bad: leading spaces. +_n( ' There is %1$d monkey in the %2$s', 'In the %2$s there are %1$d monkeys', $number, 'my-slug' ); // Bad: leading space. +_n( 'There is %1$d monkey in the %2$s', 'In the %2$s there are %1$d monkeys ', $number, 'my-slug' ); // Bad: trailing space. +_n( ' There is %1$d monkey in the %2$s', 'In the %2$s there are %1$d monkeys ', $number, 'my-slug' ); // Bad: leading and trailing spaces. +_nx( ' I have %d cat.', 'I have %d cats.', $number, 'Not really.', 'my-slug' ); // Bad: leading spaces. +_nx( 'I have %d cat.', 'I have %d cats. ', $number, 'Not really.', 'my-slug' ); // Bad: trailing spaces. +_nx( ' I have %d cat.', 'I have %d cats. ', $number, 'Not really.', 'my-slug' ); // Bad: leading and trailing spaces. +_n_noop( ' I have %d cat.', 'I have %d cats.', 'my-slug' ); // Bad: leading space. +_n_noop( 'I have %d cat.', 'I have %d cats. ', 'my-slug' ); // Bad: trailing space. +_n_noop( ' I have %d cat.', 'I have %d cats. ', 'my-slug' ); // Bad: leading and trailing spaces. +_nx_noop( ' I have %d cat.', 'I have %d cats.', 'Not really.', 'my-slug' ); // Bad: leading spaces. +_nx_noop( 'I have %d cat.', 'I have %d cats. ', 'Not really.', 'my-slug' ); // Bad: trailing spaces. +_nx_noop( ' I have %d cat.', 'I have %d cats. ', 'Not really.', 'my-slug' ); // Bad: leading and trailing spaces. +esc_html__( ' string', 'my-slug' ); // Bad: leading space. +esc_html_e( 'string ', 'my-slug' ); // Bad: trailing space. +esc_html_x( ' string ', 'context', 'my-slug' ); // Bad: leading and trailing spaces. +esc_attr__( ' string', 'my-slug' ); // Bad: leading space. +esc_attr_e( 'string ', 'my-slug' ); // Bad: trailing space. +esc_attr_x( ' string ', 'context', 'my-slug' ); // Bad: leading and trailing spaces. + // phpcs:enable WordPress.WP.I18n.MissingTranslatorsComment diff --git a/WordPress/Tests/WP/I18nUnitTest.1.inc.fixed b/WordPress/Tests/WP/I18nUnitTest.1.inc.fixed index ae6358abbf..29735b8259 100644 --- a/WordPress/Tests/WP/I18nUnitTest.1.inc.fixed +++ b/WordPress/Tests/WP/I18nUnitTest.1.inc.fixed @@ -328,4 +328,27 @@ MyNamespace\__( 'foo', 'my-slug' ); // Ok. namespace\esc_html_e( 'foo', '' ); // The sniff should start flagging this once it can resolve relative namespaces. namespace\Sub\translate( 'foo', 'my-slug' ); // Ok. +__( ' string', 'my-slug' ); // Bad: leading space. +_e( 'string ', 'my-slug' ); // Bad: trailing space. +_x( ' string ', 'context', 'my-slug' ); // Bad: leading and trailing spaces. +_ex( ' string', 'context', 'my-slug' ); // Bad: leading spaces. +_n( ' There is %1$d monkey in the %2$s', 'In the %2$s there are %1$d monkeys', $number, 'my-slug' ); // Bad: leading space. +_n( 'There is %1$d monkey in the %2$s', 'In the %2$s there are %1$d monkeys ', $number, 'my-slug' ); // Bad: trailing space. +_n( ' There is %1$d monkey in the %2$s', 'In the %2$s there are %1$d monkeys ', $number, 'my-slug' ); // Bad: leading and trailing spaces. +_nx( ' I have %d cat.', 'I have %d cats.', $number, 'Not really.', 'my-slug' ); // Bad: leading spaces. +_nx( 'I have %d cat.', 'I have %d cats. ', $number, 'Not really.', 'my-slug' ); // Bad: trailing spaces. +_nx( ' I have %d cat.', 'I have %d cats. ', $number, 'Not really.', 'my-slug' ); // Bad: leading and trailing spaces. +_n_noop( ' I have %d cat.', 'I have %d cats.', 'my-slug' ); // Bad: leading space. +_n_noop( 'I have %d cat.', 'I have %d cats. ', 'my-slug' ); // Bad: trailing space. +_n_noop( ' I have %d cat.', 'I have %d cats. ', 'my-slug' ); // Bad: leading and trailing spaces. +_nx_noop( ' I have %d cat.', 'I have %d cats.', 'Not really.', 'my-slug' ); // Bad: leading spaces. +_nx_noop( 'I have %d cat.', 'I have %d cats. ', 'Not really.', 'my-slug' ); // Bad: trailing spaces. +_nx_noop( ' I have %d cat.', 'I have %d cats. ', 'Not really.', 'my-slug' ); // Bad: leading and trailing spaces. +esc_html__( ' string', 'my-slug' ); // Bad: leading space. +esc_html_e( 'string ', 'my-slug' ); // Bad: trailing space. +esc_html_x( ' string ', 'context', 'my-slug' ); // Bad: leading and trailing spaces. +esc_attr__( ' string', 'my-slug' ); // Bad: leading space. +esc_attr_e( 'string ', 'my-slug' ); // Bad: trailing space. +esc_attr_x( ' string ', 'context', 'my-slug' ); // Bad: leading and trailing spaces. + // phpcs:enable WordPress.WP.I18n.MissingTranslatorsComment diff --git a/WordPress/Tests/WP/I18nUnitTest.php b/WordPress/Tests/WP/I18nUnitTest.php index 20e7accaab..807bb97ac8 100644 --- a/WordPress/Tests/WP/I18nUnitTest.php +++ b/WordPress/Tests/WP/I18nUnitTest.php @@ -149,6 +149,28 @@ public function getErrorList( $testFile = '' ) { 315 => 1, 318 => 1, 323 => 1, + 331 => 1, + 332 => 1, + 333 => 2, + 334 => 1, + 335 => 1, + 336 => 1, + 337 => 2, + 338 => 1, + 339 => 1, + 340 => 2, + 341 => 1, + 342 => 1, + 343 => 2, + 344 => 1, + 345 => 1, + 346 => 2, + 347 => 1, + 348 => 1, + 349 => 2, + 350 => 1, + 351 => 1, + 352 => 2, ); case 'I18nUnitTest.2.inc': From 258b3a51af7ea94b0bfd90055069adeb05d398fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Amieiro?= <1667814+amieiro@users.noreply.github.com> Date: Mon, 4 Nov 2024 11:33:27 +0100 Subject: [PATCH 2/9] Remove a debug code --- WordPress/Sniffs/WP/I18nSniff.php | 7 ------- 1 file changed, 7 deletions(-) diff --git a/WordPress/Sniffs/WP/I18nSniff.php b/WordPress/Sniffs/WP/I18nSniff.php index b740495386..9e24d0787d 100644 --- a/WordPress/Sniffs/WP/I18nSniff.php +++ b/WordPress/Sniffs/WP/I18nSniff.php @@ -839,13 +839,6 @@ private function check_string_has_no_leading_trailing_spaces( $matched_content, array( $param_info['clean'] ) ); } - - if ('cadena' == $param_info['raw']) { - fwrite(STDERR, "\n" . '$matched_content: ' . print_r($matched_content, TRUE)); - fwrite(STDERR, "\n" . '$param_name: ' . print_r($param_name, TRUE)); - fwrite(STDERR, "\n" . '$param_info: ' . print_r($param_info, TRUE)); - exit(1); - } } /** From c33ae936de80e5f16bc4b390f34ea17113336d3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Amieiro?= <1667814+amieiro@users.noreply.github.com> Date: Mon, 4 Nov 2024 11:50:33 +0100 Subject: [PATCH 3/9] Lint --- WordPress/Sniffs/WP/I18nSniff.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WordPress/Sniffs/WP/I18nSniff.php b/WordPress/Sniffs/WP/I18nSniff.php index 9e24d0787d..82499c2ec6 100644 --- a/WordPress/Sniffs/WP/I18nSniff.php +++ b/WordPress/Sniffs/WP/I18nSniff.php @@ -821,7 +821,7 @@ private function check_string_has_no_html_wrapper( $matched_content, $param_name private function check_string_has_no_leading_trailing_spaces( $matched_content, $param_name, $param_info ) { // Strip surrounding quotes. $content_without_quotes = TextStrings::stripQuotes( $param_info['clean'] ); - $first_non_empty = $this->phpcsFile->findNext( Tokens::$emptyTokens, $param_info['start'], ( $param_info['end'] + 1 ), true ); + $first_non_empty = $this->phpcsFile->findNext( Tokens::$emptyTokens, $param_info['start'], ( $param_info['end'] + 1 ), true ); if ( ltrim( $content_without_quotes ) !== $content_without_quotes ) { $this->phpcsFile->addError( From 2fab295c62d1b9ef66d2d4147e3ec6ce27e67492 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Amieiro?= <1667814+amieiro@users.noreply.github.com> Date: Tue, 10 Jun 2025 19:10:05 +0200 Subject: [PATCH 4/9] Add tabs to the checks --- WordPress/Tests/WP/I18nUnitTest.1.inc | 5 +++++ WordPress/Tests/WP/I18nUnitTest.1.inc.fixed | 5 +++++ WordPress/Tests/WP/I18nUnitTest.php | 5 +++++ 3 files changed, 15 insertions(+) diff --git a/WordPress/Tests/WP/I18nUnitTest.1.inc b/WordPress/Tests/WP/I18nUnitTest.1.inc index 92b5cfff04..ca571f5b39 100644 --- a/WordPress/Tests/WP/I18nUnitTest.1.inc +++ b/WordPress/Tests/WP/I18nUnitTest.1.inc @@ -350,5 +350,10 @@ esc_html_x( ' string ', 'context', 'my-slug' ); // Bad: leading and trailing spa esc_attr__( ' string', 'my-slug' ); // Bad: leading space. esc_attr_e( 'string ', 'my-slug' ); // Bad: trailing space. esc_attr_x( ' string ', 'context', 'my-slug' ); // Bad: leading and trailing spaces. +__( ' string', 'my-slug' ); // Bad: leading tab. +__( 'string ', 'my-slug' ); // Bad: trailing tab. +__( ' string ', 'my-slug' ); // Bad: leading and trailing tabs. +__( ' string', 'my-slug' ); // Bad: two leading tabs. +__( 'string ', 'my-slug' ); // Bad: two trailing tabs. // phpcs:enable WordPress.WP.I18n.MissingTranslatorsComment diff --git a/WordPress/Tests/WP/I18nUnitTest.1.inc.fixed b/WordPress/Tests/WP/I18nUnitTest.1.inc.fixed index 29735b8259..ce2cd730ad 100644 --- a/WordPress/Tests/WP/I18nUnitTest.1.inc.fixed +++ b/WordPress/Tests/WP/I18nUnitTest.1.inc.fixed @@ -350,5 +350,10 @@ esc_html_x( ' string ', 'context', 'my-slug' ); // Bad: leading and trailing spa esc_attr__( ' string', 'my-slug' ); // Bad: leading space. esc_attr_e( 'string ', 'my-slug' ); // Bad: trailing space. esc_attr_x( ' string ', 'context', 'my-slug' ); // Bad: leading and trailing spaces. +__( ' string', 'my-slug' ); // Bad: leading tab. +__( 'string ', 'my-slug' ); // Bad: trailing tab. +__( ' string ', 'my-slug' ); // Bad: leading and trailing tabs. +__( ' string', 'my-slug' ); // Bad: two leading tabs. +__( 'string ', 'my-slug' ); // Bad: two trailing tabs. // phpcs:enable WordPress.WP.I18n.MissingTranslatorsComment diff --git a/WordPress/Tests/WP/I18nUnitTest.php b/WordPress/Tests/WP/I18nUnitTest.php index 807bb97ac8..9d7186d5cb 100644 --- a/WordPress/Tests/WP/I18nUnitTest.php +++ b/WordPress/Tests/WP/I18nUnitTest.php @@ -171,6 +171,11 @@ public function getErrorList( $testFile = '' ) { 350 => 1, 351 => 1, 352 => 2, + 353 => 1, + 354 => 1, + 355 => 2, + 356 => 1, + 357 => 1, ); case 'I18nUnitTest.2.inc': From 409f1dda67eaca9205a09b6ab25f5434d32d8316 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Amieiro?= <1667814+amieiro@users.noreply.github.com> Date: Tue, 10 Jun 2025 19:35:44 +0200 Subject: [PATCH 5/9] Add new lines to the checks --- WordPress/Sniffs/WP/I18nSniff.php | 4 ++-- WordPress/Tests/WP/I18nUnitTest.1.inc | 13 +++++++++++++ WordPress/Tests/WP/I18nUnitTest.1.inc.fixed | 13 +++++++++++++ WordPress/Tests/WP/I18nUnitTest.php | 5 +++++ 4 files changed, 33 insertions(+), 2 deletions(-) diff --git a/WordPress/Sniffs/WP/I18nSniff.php b/WordPress/Sniffs/WP/I18nSniff.php index 82499c2ec6..c21de0f285 100644 --- a/WordPress/Sniffs/WP/I18nSniff.php +++ b/WordPress/Sniffs/WP/I18nSniff.php @@ -825,7 +825,7 @@ private function check_string_has_no_leading_trailing_spaces( $matched_content, if ( ltrim( $content_without_quotes ) !== $content_without_quotes ) { $this->phpcsFile->addError( - 'Translatable string should not have leading spaces. Found: %s', + 'Translatable string should not have leading spaces, tabs, or new lines. Found: %s', $first_non_empty, 'NoLeadingTrailingSpaces', array( $param_info['clean'] ) @@ -833,7 +833,7 @@ private function check_string_has_no_leading_trailing_spaces( $matched_content, } if ( rtrim( $content_without_quotes ) !== $content_without_quotes ) { $this->phpcsFile->addError( - 'Translatable string should not have trailing spaces. Found: %s', + 'Translatable string should not have trailing spaces, tabs, or new lines. Found: %s', $first_non_empty, 'NoLeadingTrailingSpaces', array( $param_info['clean'] ) diff --git a/WordPress/Tests/WP/I18nUnitTest.1.inc b/WordPress/Tests/WP/I18nUnitTest.1.inc index ca571f5b39..a4fce04768 100644 --- a/WordPress/Tests/WP/I18nUnitTest.1.inc +++ b/WordPress/Tests/WP/I18nUnitTest.1.inc @@ -355,5 +355,18 @@ __( 'string ', 'my-slug' ); // Bad: trailing tab. __( ' string ', 'my-slug' ); // Bad: leading and trailing tabs. __( ' string', 'my-slug' ); // Bad: two leading tabs. __( 'string ', 'my-slug' ); // Bad: two trailing tabs. +__( ' +string', 'my-slug' ); // Bad: leading new line. +__( 'string +', 'my-slug' ); // Bad: trailing new line. +__( ' +string +', 'my-slug' ); // Bad: leading and trailing new line. +__( ' + +string', 'my-slug' ); // Bad: two leading new lines. +__( 'string + +', 'my-slug' ); // Bad: two trailing new lines. // phpcs:enable WordPress.WP.I18n.MissingTranslatorsComment diff --git a/WordPress/Tests/WP/I18nUnitTest.1.inc.fixed b/WordPress/Tests/WP/I18nUnitTest.1.inc.fixed index ce2cd730ad..2b171abadb 100644 --- a/WordPress/Tests/WP/I18nUnitTest.1.inc.fixed +++ b/WordPress/Tests/WP/I18nUnitTest.1.inc.fixed @@ -355,5 +355,18 @@ __( 'string ', 'my-slug' ); // Bad: trailing tab. __( ' string ', 'my-slug' ); // Bad: leading and trailing tabs. __( ' string', 'my-slug' ); // Bad: two leading tabs. __( 'string ', 'my-slug' ); // Bad: two trailing tabs. +__( ' +string', 'my-slug' ); // Bad: leading new line. +__( 'string +', 'my-slug' ); // Bad: trailing new line. +__( ' +string +', 'my-slug' ); // Bad: leading and trailing new line. +__( ' + +string', 'my-slug' ); // Bad: two leading new lines. +__( 'string + +', 'my-slug' ); // Bad: two trailing new lines. // phpcs:enable WordPress.WP.I18n.MissingTranslatorsComment diff --git a/WordPress/Tests/WP/I18nUnitTest.php b/WordPress/Tests/WP/I18nUnitTest.php index 9d7186d5cb..db0450cadb 100644 --- a/WordPress/Tests/WP/I18nUnitTest.php +++ b/WordPress/Tests/WP/I18nUnitTest.php @@ -176,6 +176,11 @@ public function getErrorList( $testFile = '' ) { 355 => 2, 356 => 1, 357 => 1, + 358 => 1, + 360 => 1, + 362 => 2, + 365 => 1, + 368 => 1, ); case 'I18nUnitTest.2.inc': From b2150810c18a4587192f5d790b85a211e911749a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Amieiro?= <1667814+amieiro@users.noreply.github.com> Date: Wed, 11 Jun 2025 06:55:54 +0200 Subject: [PATCH 6/9] Refactor the method to have different error messages for each check --- WordPress/Sniffs/WP/I18nSniff.php | 63 ++++++++++++++++++++++++++++--- 1 file changed, 57 insertions(+), 6 deletions(-) diff --git a/WordPress/Sniffs/WP/I18nSniff.php b/WordPress/Sniffs/WP/I18nSniff.php index c21de0f285..a53dff255b 100644 --- a/WordPress/Sniffs/WP/I18nSniff.php +++ b/WordPress/Sniffs/WP/I18nSniff.php @@ -823,19 +823,70 @@ private function check_string_has_no_leading_trailing_spaces( $matched_content, $content_without_quotes = TextStrings::stripQuotes( $param_info['clean'] ); $first_non_empty = $this->phpcsFile->findNext( Tokens::$emptyTokens, $param_info['start'], ( $param_info['end'] + 1 ), true ); - if ( ltrim( $content_without_quotes ) !== $content_without_quotes ) { + // Define regex patterns. + $pattern_leading_spaces = '/^[ ]+/u'; + $pattern_trailing_spaces = '/[ ]+$/u'; + $pattern_leading_tabs = '/^\t+/u'; + $pattern_trailing_tabs = '/\t+$/u'; + $pattern_leading_newlines = '/^[\r\n]+/u'; + $pattern_trailing_newlines = '/[\r\n]+$/u'; + + // Check for leading spaces. + if ( preg_match( $pattern_leading_spaces, $content_without_quotes ) ) { $this->phpcsFile->addError( - 'Translatable string should not have leading spaces, tabs, or new lines. Found: %s', + 'Translatable string should not have leading spaces. Found: %s', $first_non_empty, - 'NoLeadingTrailingSpaces', + 'LeadingSpaces', array( $param_info['clean'] ) ); } - if ( rtrim( $content_without_quotes ) !== $content_without_quotes ) { + + // Check for trailing spaces. + if ( preg_match( $pattern_trailing_spaces, $content_without_quotes ) ) { $this->phpcsFile->addError( - 'Translatable string should not have trailing spaces, tabs, or new lines. Found: %s', + 'Translatable string should not have trailing spaces. Found: %s', $first_non_empty, - 'NoLeadingTrailingSpaces', + 'TrailingSpaces', + array( $param_info['clean'] ) + ); + } + + // Check for leading tabs. + if ( preg_match( $pattern_leading_tabs, $content_without_quotes ) ) { + $this->phpcsFile->addError( + 'Translatable string should not have leading tabs. Found: %s', + $first_non_empty, + 'LeadingTabs', + array( $param_info['clean'] ) + ); + } + + // Check for trailing tabs. + if ( preg_match( $pattern_trailing_tabs, $content_without_quotes ) ) { + $this->phpcsFile->addError( + 'Translatable string should not have trailing tabs. Found: %s', + $first_non_empty, + 'TrailingTabs', + array( $param_info['clean'] ) + ); + } + + // Check for leading new lines. + if ( preg_match( $pattern_leading_newlines, $content_without_quotes ) ) { + $this->phpcsFile->addError( + 'Translatable string should not have leading new lines. Found: %s', + $first_non_empty, + 'LeadingNewLines', + array( $param_info['clean'] ) + ); + } + + // Check for trailing new lines. + if ( preg_match( $pattern_trailing_newlines, $content_without_quotes ) ) { + $this->phpcsFile->addError( + 'Translatable string should not have trailing new lines. Found: %s', + $first_non_empty, + 'TrailingNewLines', array( $param_info['clean'] ) ); } From 7dc4bd4380839ad3d33f17362434d1e98ebf5600 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Amieiro?= <1667814+amieiro@users.noreply.github.com> Date: Wed, 11 Jun 2025 07:25:27 +0200 Subject: [PATCH 7/9] Add vertical tabs to the checks --- WordPress/Sniffs/WP/I18nSniff.php | 22 +++++++++++++++++++++ WordPress/Tests/WP/I18nUnitTest.1.inc | 5 +++++ WordPress/Tests/WP/I18nUnitTest.1.inc.fixed | 5 +++++ WordPress/Tests/WP/I18nUnitTest.php | 5 +++++ 4 files changed, 37 insertions(+) diff --git a/WordPress/Sniffs/WP/I18nSniff.php b/WordPress/Sniffs/WP/I18nSniff.php index a53dff255b..c3829d2c13 100644 --- a/WordPress/Sniffs/WP/I18nSniff.php +++ b/WordPress/Sniffs/WP/I18nSniff.php @@ -830,6 +830,8 @@ private function check_string_has_no_leading_trailing_spaces( $matched_content, $pattern_trailing_tabs = '/\t+$/u'; $pattern_leading_newlines = '/^[\r\n]+/u'; $pattern_trailing_newlines = '/[\r\n]+$/u'; + $pattern_leading_vtabs = '/^\x0B+/u'; + $pattern_trailing_vtabs = '/\x0B+$/u'; // Check for leading spaces. if ( preg_match( $pattern_leading_spaces, $content_without_quotes ) ) { @@ -890,6 +892,26 @@ private function check_string_has_no_leading_trailing_spaces( $matched_content, array( $param_info['clean'] ) ); } + + // Check for leading vertical tabs. + if ( preg_match( $pattern_leading_vtabs, $content_without_quotes ) ) { + $this->phpcsFile->addError( + 'Translatable string should not have leading vertical tabs. Found: %s', + $first_non_empty, + 'LeadingVTabs', + array( $param_info['clean'] ) + ); + } + + // Check for trailing vertical tabs. + if ( preg_match( $pattern_trailing_vtabs, $content_without_quotes ) ) { + $this->phpcsFile->addError( + 'Translatable string should not have trailing vertical tabs. Found: %s', + $first_non_empty, + 'TrailingVTabs', + array( $param_info['clean'] ) + ); + } } /** diff --git a/WordPress/Tests/WP/I18nUnitTest.1.inc b/WordPress/Tests/WP/I18nUnitTest.1.inc index a4fce04768..fb9bdf7828 100644 --- a/WordPress/Tests/WP/I18nUnitTest.1.inc +++ b/WordPress/Tests/WP/I18nUnitTest.1.inc @@ -368,5 +368,10 @@ string', 'my-slug' ); // Bad: two leading new lines. __( 'string ', 'my-slug' ); // Bad: two trailing new lines. +__( " String with leading vertical tab", 'my-slug' ); // Bad, has leading vertical tab. +__( "String with trailing vertical tab ", 'my-slug' ); // Bad, has trailing vertical tab. +__( " String with leading and trailing vertical tabs ", 'my-slug' ); // Bad, has both leading and trailing vertical tabs. +__( " String with 2 leading vertical tabs", 'my-slug' ); // Bad, has 2 leading vertical tabs. +__( "String with 2 trailing vertical tabs ", 'my-slug' ); // Bad, has 2 trailing vertical tabs. // phpcs:enable WordPress.WP.I18n.MissingTranslatorsComment diff --git a/WordPress/Tests/WP/I18nUnitTest.1.inc.fixed b/WordPress/Tests/WP/I18nUnitTest.1.inc.fixed index 2b171abadb..2c828c66d2 100644 --- a/WordPress/Tests/WP/I18nUnitTest.1.inc.fixed +++ b/WordPress/Tests/WP/I18nUnitTest.1.inc.fixed @@ -368,5 +368,10 @@ string', 'my-slug' ); // Bad: two leading new lines. __( 'string ', 'my-slug' ); // Bad: two trailing new lines. +__( " String with leading vertical tab", 'my-slug' ); // Bad, has leading vertical tab. +__( "String with trailing vertical tab ", 'my-slug' ); // Bad, has trailing vertical tab. +__( " String with leading and trailing vertical tabs ", 'my-slug' ); // Bad, has both leading and trailing vertical tabs. +__( " String with 2 leading vertical tabs", 'my-slug' ); // Bad, has 2 leading vertical tabs. +__( "String with 2 trailing vertical tabs ", 'my-slug' ); // Bad, has 2 trailing vertical tabs. // phpcs:enable WordPress.WP.I18n.MissingTranslatorsComment diff --git a/WordPress/Tests/WP/I18nUnitTest.php b/WordPress/Tests/WP/I18nUnitTest.php index db0450cadb..0eec46cdd9 100644 --- a/WordPress/Tests/WP/I18nUnitTest.php +++ b/WordPress/Tests/WP/I18nUnitTest.php @@ -181,6 +181,11 @@ public function getErrorList( $testFile = '' ) { 362 => 2, 365 => 1, 368 => 1, + 371 => 1, + 372 => 1, + 373 => 2, + 374 => 1, + 375 => 1, ); case 'I18nUnitTest.2.inc': From b8b4fc5d2ebaa45fceffe6e78cd4c195e8e42546 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Amieiro?= <1667814+amieiro@users.noreply.github.com> Date: Fri, 13 Jun 2025 19:29:41 +0200 Subject: [PATCH 8/9] Change the regexs to use hex codes and reorganize some code --- WordPress/Sniffs/WP/I18nSniff.php | 46 ++++++++++----------- WordPress/Tests/WP/I18nUnitTest.1.inc | 10 ++--- WordPress/Tests/WP/I18nUnitTest.1.inc.fixed | 10 ++--- WordPress/Tests/WP/I18nUnitTest.php | 16 +++---- 4 files changed, 41 insertions(+), 41 deletions(-) diff --git a/WordPress/Sniffs/WP/I18nSniff.php b/WordPress/Sniffs/WP/I18nSniff.php index c3829d2c13..e0a96580bb 100644 --- a/WordPress/Sniffs/WP/I18nSniff.php +++ b/WordPress/Sniffs/WP/I18nSniff.php @@ -824,14 +824,14 @@ private function check_string_has_no_leading_trailing_spaces( $matched_content, $first_non_empty = $this->phpcsFile->findNext( Tokens::$emptyTokens, $param_info['start'], ( $param_info['end'] + 1 ), true ); // Define regex patterns. - $pattern_leading_spaces = '/^[ ]+/u'; - $pattern_trailing_spaces = '/[ ]+$/u'; - $pattern_leading_tabs = '/^\t+/u'; - $pattern_trailing_tabs = '/\t+$/u'; - $pattern_leading_newlines = '/^[\r\n]+/u'; - $pattern_trailing_newlines = '/[\r\n]+$/u'; + $pattern_leading_spaces = '/^[\x20]+/u'; + $pattern_trailing_spaces = '/[\x20]+$/u'; + $pattern_leading_tabs = '/^\x09+/u'; + $pattern_trailing_tabs = '/\x09+$/u'; $pattern_leading_vtabs = '/^\x0B+/u'; $pattern_trailing_vtabs = '/\x0B+$/u'; + $pattern_leading_newlines = '/^\x0A+/u'; + $pattern_trailing_newlines = '/\x0A+$/u'; // Check for leading spaces. if ( preg_match( $pattern_leading_spaces, $content_without_quotes ) ) { @@ -873,42 +873,42 @@ private function check_string_has_no_leading_trailing_spaces( $matched_content, ); } - // Check for leading new lines. - if ( preg_match( $pattern_leading_newlines, $content_without_quotes ) ) { + // Check for leading vertical tabs. + if ( preg_match( $pattern_leading_vtabs, $content_without_quotes ) ) { $this->phpcsFile->addError( - 'Translatable string should not have leading new lines. Found: %s', + 'Translatable string should not have leading vertical tabs. Found: %s', $first_non_empty, - 'LeadingNewLines', + 'LeadingVTabs', array( $param_info['clean'] ) ); } - // Check for trailing new lines. - if ( preg_match( $pattern_trailing_newlines, $content_without_quotes ) ) { + // Check for trailing vertical tabs. + if ( preg_match( $pattern_trailing_vtabs, $content_without_quotes ) ) { $this->phpcsFile->addError( - 'Translatable string should not have trailing new lines. Found: %s', + 'Translatable string should not have trailing vertical tabs. Found: %s', $first_non_empty, - 'TrailingNewLines', + 'TrailingVTabs', array( $param_info['clean'] ) ); } - - // Check for leading vertical tabs. - if ( preg_match( $pattern_leading_vtabs, $content_without_quotes ) ) { + + // Check for leading new lines. + if ( preg_match( $pattern_leading_newlines, $content_without_quotes ) ) { $this->phpcsFile->addError( - 'Translatable string should not have leading vertical tabs. Found: %s', + 'Translatable string should not have leading new lines. Found: %s', $first_non_empty, - 'LeadingVTabs', + 'LeadingNewLines', array( $param_info['clean'] ) ); } - // Check for trailing vertical tabs. - if ( preg_match( $pattern_trailing_vtabs, $content_without_quotes ) ) { + // Check for trailing new lines. + if ( preg_match( $pattern_trailing_newlines, $content_without_quotes ) ) { $this->phpcsFile->addError( - 'Translatable string should not have trailing vertical tabs. Found: %s', + 'Translatable string should not have trailing new lines. Found: %s', $first_non_empty, - 'TrailingVTabs', + 'TrailingNewLines', array( $param_info['clean'] ) ); } diff --git a/WordPress/Tests/WP/I18nUnitTest.1.inc b/WordPress/Tests/WP/I18nUnitTest.1.inc index fb9bdf7828..58b1e5f2da 100644 --- a/WordPress/Tests/WP/I18nUnitTest.1.inc +++ b/WordPress/Tests/WP/I18nUnitTest.1.inc @@ -355,6 +355,11 @@ __( 'string ', 'my-slug' ); // Bad: trailing tab. __( ' string ', 'my-slug' ); // Bad: leading and trailing tabs. __( ' string', 'my-slug' ); // Bad: two leading tabs. __( 'string ', 'my-slug' ); // Bad: two trailing tabs. +__( " String with leading vertical tab", 'my-slug' ); // Bad: leading vertical tab. +__( "String with trailing vertical tab ", 'my-slug' ); // Bad: trailing vertical tab. +__( " String with leading and trailing vertical tabs ", 'my-slug' ); // Bad: leading and trailing vertical tabs. +__( " String with 2 leading vertical tabs", 'my-slug' ); // Bad: 2 leading vertical tabs. +__( "String with 2 trailing vertical tabs ", 'my-slug' ); // Bad: 2 trailing vertical tabs. __( ' string', 'my-slug' ); // Bad: leading new line. __( 'string @@ -368,10 +373,5 @@ string', 'my-slug' ); // Bad: two leading new lines. __( 'string ', 'my-slug' ); // Bad: two trailing new lines. -__( " String with leading vertical tab", 'my-slug' ); // Bad, has leading vertical tab. -__( "String with trailing vertical tab ", 'my-slug' ); // Bad, has trailing vertical tab. -__( " String with leading and trailing vertical tabs ", 'my-slug' ); // Bad, has both leading and trailing vertical tabs. -__( " String with 2 leading vertical tabs", 'my-slug' ); // Bad, has 2 leading vertical tabs. -__( "String with 2 trailing vertical tabs ", 'my-slug' ); // Bad, has 2 trailing vertical tabs. // phpcs:enable WordPress.WP.I18n.MissingTranslatorsComment diff --git a/WordPress/Tests/WP/I18nUnitTest.1.inc.fixed b/WordPress/Tests/WP/I18nUnitTest.1.inc.fixed index 2c828c66d2..1772edf711 100644 --- a/WordPress/Tests/WP/I18nUnitTest.1.inc.fixed +++ b/WordPress/Tests/WP/I18nUnitTest.1.inc.fixed @@ -355,6 +355,11 @@ __( 'string ', 'my-slug' ); // Bad: trailing tab. __( ' string ', 'my-slug' ); // Bad: leading and trailing tabs. __( ' string', 'my-slug' ); // Bad: two leading tabs. __( 'string ', 'my-slug' ); // Bad: two trailing tabs. +__( " String with leading vertical tab", 'my-slug' ); // Bad: leading vertical tab. +__( "String with trailing vertical tab ", 'my-slug' ); // Bad: trailing vertical tab. +__( " String with leading and trailing vertical tabs ", 'my-slug' ); // Bad: leading and trailing vertical tabs. +__( " String with 2 leading vertical tabs", 'my-slug' ); // Bad: 2 leading vertical tabs. +__( "String with 2 trailing vertical tabs ", 'my-slug' ); // Bad: 2 trailing vertical tabs. __( ' string', 'my-slug' ); // Bad: leading new line. __( 'string @@ -368,10 +373,5 @@ string', 'my-slug' ); // Bad: two leading new lines. __( 'string ', 'my-slug' ); // Bad: two trailing new lines. -__( " String with leading vertical tab", 'my-slug' ); // Bad, has leading vertical tab. -__( "String with trailing vertical tab ", 'my-slug' ); // Bad, has trailing vertical tab. -__( " String with leading and trailing vertical tabs ", 'my-slug' ); // Bad, has both leading and trailing vertical tabs. -__( " String with 2 leading vertical tabs", 'my-slug' ); // Bad, has 2 leading vertical tabs. -__( "String with 2 trailing vertical tabs ", 'my-slug' ); // Bad, has 2 trailing vertical tabs. // phpcs:enable WordPress.WP.I18n.MissingTranslatorsComment diff --git a/WordPress/Tests/WP/I18nUnitTest.php b/WordPress/Tests/WP/I18nUnitTest.php index 0eec46cdd9..ef267e3e8d 100644 --- a/WordPress/Tests/WP/I18nUnitTest.php +++ b/WordPress/Tests/WP/I18nUnitTest.php @@ -177,15 +177,15 @@ public function getErrorList( $testFile = '' ) { 356 => 1, 357 => 1, 358 => 1, - 360 => 1, - 362 => 2, + 359 => 1, + 360 => 2, + 361 => 1, + 362 => 1, + 363 => 1, 365 => 1, - 368 => 1, - 371 => 1, - 372 => 1, - 373 => 2, - 374 => 1, - 375 => 1, + 367 => 2, + 370 => 1, + 373 => 1, ); case 'I18nUnitTest.2.inc': From 0f6a099daef43b4727d6449751a1762e96940c61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Amieiro?= <1667814+amieiro@users.noreply.github.com> Date: Wed, 22 Apr 2026 17:58:09 +0200 Subject: [PATCH 9/9] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20refactor(I18n):=20cons?= =?UTF-8?q?olidate=20leading/trailing=20whitespace=20check=20into=20two=20?= =?UTF-8?q?error=20codes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - replace eight per-character error codes (Leading/Trailing x Spaces/Tabs/VTabs/NewLines) with two generic codes: LeadingWhiteSpace and TrailingWhiteSpace - reduce the eight hex-code regexes to a single `\s` match on each side (covers space, tab, vertical tab, newline, CR, form feed in one pass) - reword error messages to use the generic term "whitespace" - bump @since annotation from 3.2.0 to 3.4.0 - add four combination test cases (leading tab+space, trailing space+tab, mixed leading/trailing, multi-line with newline+tab on each side) to verify strings with multiple whitespace types produce at most one leading and one trailing error --- WordPress/Sniffs/WP/I18nSniff.php | 93 +++------------------ WordPress/Tests/WP/I18nUnitTest.1.inc | 6 ++ WordPress/Tests/WP/I18nUnitTest.1.inc.fixed | 6 ++ WordPress/Tests/WP/I18nUnitTest.php | 4 + 4 files changed, 26 insertions(+), 83 deletions(-) diff --git a/WordPress/Sniffs/WP/I18nSniff.php b/WordPress/Sniffs/WP/I18nSniff.php index e0a96580bb..6463b7179e 100644 --- a/WordPress/Sniffs/WP/I18nSniff.php +++ b/WordPress/Sniffs/WP/I18nSniff.php @@ -380,7 +380,7 @@ public function process_parameters( $stackPtr, $group_name, $matched_content, $p $has_content = $this->check_string_has_translatable_content( $matched_content, $param_name, $param_info ); if ( true === $has_content ) { $this->check_string_has_no_html_wrapper( $matched_content, $param_name, $param_info ); - $this->check_string_has_no_leading_trailing_spaces( $matched_content, $param_name, $param_info ); + $this->check_string_has_no_leading_trailing_whitespace( $matched_content, $param_name, $param_info ); } } } @@ -806,9 +806,9 @@ private function check_string_has_no_html_wrapper( $matched_content, $param_name } /** - * Check if a translatable string has leading or trailing spaces. + * Check if a translatable string has leading or trailing whitespace. * - * @since 3.2.0 + * @since 3.4.0 * * @param string $matched_content The token content (function name) which was matched * in lowercase. @@ -818,97 +818,24 @@ private function check_string_has_no_html_wrapper( $matched_content, $param_name * * @return void */ - private function check_string_has_no_leading_trailing_spaces( $matched_content, $param_name, $param_info ) { - // Strip surrounding quotes. + private function check_string_has_no_leading_trailing_whitespace( $matched_content, $param_name, $param_info ) { $content_without_quotes = TextStrings::stripQuotes( $param_info['clean'] ); $first_non_empty = $this->phpcsFile->findNext( Tokens::$emptyTokens, $param_info['start'], ( $param_info['end'] + 1 ), true ); - // Define regex patterns. - $pattern_leading_spaces = '/^[\x20]+/u'; - $pattern_trailing_spaces = '/[\x20]+$/u'; - $pattern_leading_tabs = '/^\x09+/u'; - $pattern_trailing_tabs = '/\x09+$/u'; - $pattern_leading_vtabs = '/^\x0B+/u'; - $pattern_trailing_vtabs = '/\x0B+$/u'; - $pattern_leading_newlines = '/^\x0A+/u'; - $pattern_trailing_newlines = '/\x0A+$/u'; - - // Check for leading spaces. - if ( preg_match( $pattern_leading_spaces, $content_without_quotes ) ) { - $this->phpcsFile->addError( - 'Translatable string should not have leading spaces. Found: %s', - $first_non_empty, - 'LeadingSpaces', - array( $param_info['clean'] ) - ); - } - - // Check for trailing spaces. - if ( preg_match( $pattern_trailing_spaces, $content_without_quotes ) ) { - $this->phpcsFile->addError( - 'Translatable string should not have trailing spaces. Found: %s', - $first_non_empty, - 'TrailingSpaces', - array( $param_info['clean'] ) - ); - } - - // Check for leading tabs. - if ( preg_match( $pattern_leading_tabs, $content_without_quotes ) ) { - $this->phpcsFile->addError( - 'Translatable string should not have leading tabs. Found: %s', - $first_non_empty, - 'LeadingTabs', - array( $param_info['clean'] ) - ); - } - - // Check for trailing tabs. - if ( preg_match( $pattern_trailing_tabs, $content_without_quotes ) ) { + if ( preg_match( '/^\s+/u', $content_without_quotes ) ) { $this->phpcsFile->addError( - 'Translatable string should not have trailing tabs. Found: %s', + 'Translatable string should not have leading whitespace. Found: %s', $first_non_empty, - 'TrailingTabs', - array( $param_info['clean'] ) - ); - } - - // Check for leading vertical tabs. - if ( preg_match( $pattern_leading_vtabs, $content_without_quotes ) ) { - $this->phpcsFile->addError( - 'Translatable string should not have leading vertical tabs. Found: %s', - $first_non_empty, - 'LeadingVTabs', - array( $param_info['clean'] ) - ); - } - - // Check for trailing vertical tabs. - if ( preg_match( $pattern_trailing_vtabs, $content_without_quotes ) ) { - $this->phpcsFile->addError( - 'Translatable string should not have trailing vertical tabs. Found: %s', - $first_non_empty, - 'TrailingVTabs', + 'LeadingWhiteSpace', array( $param_info['clean'] ) ); } - // Check for leading new lines. - if ( preg_match( $pattern_leading_newlines, $content_without_quotes ) ) { - $this->phpcsFile->addError( - 'Translatable string should not have leading new lines. Found: %s', - $first_non_empty, - 'LeadingNewLines', - array( $param_info['clean'] ) - ); - } - - // Check for trailing new lines. - if ( preg_match( $pattern_trailing_newlines, $content_without_quotes ) ) { + if ( preg_match( '/\s+$/u', $content_without_quotes ) ) { $this->phpcsFile->addError( - 'Translatable string should not have trailing new lines. Found: %s', + 'Translatable string should not have trailing whitespace. Found: %s', $first_non_empty, - 'TrailingNewLines', + 'TrailingWhiteSpace', array( $param_info['clean'] ) ); } diff --git a/WordPress/Tests/WP/I18nUnitTest.1.inc b/WordPress/Tests/WP/I18nUnitTest.1.inc index 58b1e5f2da..0b6a694da4 100644 --- a/WordPress/Tests/WP/I18nUnitTest.1.inc +++ b/WordPress/Tests/WP/I18nUnitTest.1.inc @@ -373,5 +373,11 @@ string', 'my-slug' ); // Bad: two leading new lines. __( 'string ', 'my-slug' ); // Bad: two trailing new lines. +__( ' string', 'my-slug' ); // Bad: leading tab + space (1 leading whitespace error). +__( 'string ', 'my-slug' ); // Bad: trailing space + tab (1 trailing whitespace error). +__( ' string ', 'my-slug' ); // Bad: mixed leading and trailing whitespace (2 errors). +__( ' + string +', 'my-slug' ); // Bad: leading newline+tab and trailing tab+newline (2 errors). // phpcs:enable WordPress.WP.I18n.MissingTranslatorsComment diff --git a/WordPress/Tests/WP/I18nUnitTest.1.inc.fixed b/WordPress/Tests/WP/I18nUnitTest.1.inc.fixed index 1772edf711..faecf34381 100644 --- a/WordPress/Tests/WP/I18nUnitTest.1.inc.fixed +++ b/WordPress/Tests/WP/I18nUnitTest.1.inc.fixed @@ -373,5 +373,11 @@ string', 'my-slug' ); // Bad: two leading new lines. __( 'string ', 'my-slug' ); // Bad: two trailing new lines. +__( ' string', 'my-slug' ); // Bad: leading tab + space (1 leading whitespace error). +__( 'string ', 'my-slug' ); // Bad: trailing space + tab (1 trailing whitespace error). +__( ' string ', 'my-slug' ); // Bad: mixed leading and trailing whitespace (2 errors). +__( ' + string +', 'my-slug' ); // Bad: leading newline+tab and trailing tab+newline (2 errors). // phpcs:enable WordPress.WP.I18n.MissingTranslatorsComment diff --git a/WordPress/Tests/WP/I18nUnitTest.php b/WordPress/Tests/WP/I18nUnitTest.php index ef267e3e8d..caa037ecdf 100644 --- a/WordPress/Tests/WP/I18nUnitTest.php +++ b/WordPress/Tests/WP/I18nUnitTest.php @@ -186,6 +186,10 @@ public function getErrorList( $testFile = '' ) { 367 => 2, 370 => 1, 373 => 1, + 376 => 1, + 377 => 1, + 378 => 2, + 379 => 2, ); case 'I18nUnitTest.2.inc':