From 37a61636a29796c5c49d7cc4e0878331c8ad894d Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Wed, 7 Oct 2026 13:21:47 -0700 Subject: [PATCH 1/3] Type the nooped plural in post mime types as literal strings. `translate_nooped_plural()` now requires a nooped plural whose strings are literal, but `get_post_mime_types()` documented the third element of each mime type as a bare `array`. The literal types returned by `_n_noop()` were therefore lost before the counts on the media library screen were translated in `media.php`. The nooped plural shape is now declared in the return type of `get_post_mime_types()`, in the `post_mime_types` filter, whose documented parameter type is what PHPStan uses for the filtered return value, and in the return type of `wp_edit_attachments_query()`, which passes the mime types through. Co-Authored-By: Claude Opus 5.5 --- src/wp-admin/includes/post.php | 15 +++++++++++++++ src/wp-includes/post.php | 24 ++++++++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/src/wp-admin/includes/post.php b/src/wp-admin/includes/post.php index c39135316978f..a255673aad601 100644 --- a/src/wp-admin/includes/post.php +++ b/src/wp-admin/includes/post.php @@ -1412,6 +1412,21 @@ function wp_edit_attachments_query_vars( $q = false ) { * @type array $0 Post mime types. See get_post_mime_types(). * @type string[] $1 Available post mime types. * } + * + * @phpstan-return array{ + * 0: array, + * 1: string[], + * } */ function wp_edit_attachments_query( $q = false ) { wp( wp_edit_attachments_query_vars( $q ) ); diff --git a/src/wp-includes/post.php b/src/wp-includes/post.php index 10bb778bbd30a..1490c8775114d 100644 --- a/src/wp-includes/post.php +++ b/src/wp-includes/post.php @@ -3654,6 +3654,18 @@ function wp_count_attachments( $mime_type = '' ) { * value is a three-item array: the plural name of the * group, the label for its "Manage" screen, and the * translatable count strings returned by _n_noop(). + * + * @phpstan-return array */ function get_post_mime_types() { $post_mime_types = array( // array( adj, noun ) @@ -3748,6 +3760,18 @@ function get_post_mime_types() { * * @param array $post_mime_types Default list of post mime types. * See {@see get_post_mime_types()}. + * + * @phpstan-param array $post_mime_types */ return apply_filters( 'post_mime_types', $post_mime_types ); } From 6151ee1f49a1cc87c4ce7294fbd549cb6a49808b Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Wed, 7 Oct 2026 13:25:34 -0700 Subject: [PATCH 2/3] Build nooped plurals from runtime values without `_n_noop()`. `_n_noop()` marks strings for extraction into translation files, so it now requires literal strings. Two callers passed runtime values instead, only to build the nooped plural structure: the default `label_count` in `register_post_status()`, which reuses the status label, and the old-style `single_text` and `multiple_text` arguments of `wp_generate_tag_cloud()`. Both now build the same array directly, so the result is unchanged. This also removes the `phpcs:ignore` comments that were needed for passing non-literal strings to `_n_noop()`. Co-Authored-By: Claude Opus 5.5 --- src/wp-includes/category-template.php | 16 +++++++++++++--- src/wp-includes/post.php | 14 ++++++++++++-- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/src/wp-includes/category-template.php b/src/wp-includes/category-template.php index b4c40d2ac971f..bcc5a5d87c457 100644 --- a/src/wp-includes/category-template.php +++ b/src/wp-includes/category-template.php @@ -894,9 +894,19 @@ function wp_generate_tag_cloud( $tags, $args = '' ) { $translate_nooped_plural = false; } } elseif ( isset( $args['single_text'] ) && isset( $args['multiple_text'] ) ) { - // If no callback exists, look for the old-style single_text and multiple_text arguments. - // phpcs:ignore WordPress.WP.I18n.NonSingularStringLiteralSingular,WordPress.WP.I18n.NonSingularStringLiteralPlural - $translate_nooped_plural = _n_noop( $args['single_text'], $args['multiple_text'] ); + /* + * If no callback exists, look for the old-style single_text and multiple_text arguments. + * These are runtime values, so they cannot be marked for translation with _n_noop(), + * which requires string literals. The nooped plural is built directly instead. + */ + $translate_nooped_plural = array( + 0 => $args['single_text'], + 1 => $args['multiple_text'], + 'singular' => $args['single_text'], + 'plural' => $args['multiple_text'], + 'context' => null, + 'domain' => null, + ); } else { // This is the default for when no callback, plural, or argument is passed in. /* translators: %s: Number of items (tags). */ diff --git a/src/wp-includes/post.php b/src/wp-includes/post.php index 1490c8775114d..d605c68d0dd85 100644 --- a/src/wp-includes/post.php +++ b/src/wp-includes/post.php @@ -1502,8 +1502,18 @@ function register_post_status( $post_status, $args = array() ) { } if ( false === $args->label_count ) { - // phpcs:ignore WordPress.WP.I18n.NonSingularStringLiteralSingular,WordPress.WP.I18n.NonSingularStringLiteralPlural - $args->label_count = _n_noop( $args->label, $args->label ); + /* + * The label is a runtime value, so it cannot be marked for translation with _n_noop(), + * which requires string literals. The nooped plural is built directly instead. + */ + $args->label_count = array( + 0 => $args->label, + 1 => $args->label, + 'singular' => $args->label, + 'plural' => $args->label, + 'context' => null, + 'domain' => null, + ); } $wp_post_statuses[ $post_status ] = $args; From 354cda771740a434d7a7c2a0693fbda7af07f584 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Wed, 7 Oct 2026 13:31:50 -0700 Subject: [PATCH 3/3] Use `_n_noop()` again with inline ignores for runtime strings. The previous commit built the nooped plurals in `register_post_status()` and `wp_generate_tag_cloud()` by hand, so that runtime values were no longer passed to `_n_noop()`. That duplicated the structure returned by `_n_noop()`, and it did not make the strings literal: it only hid them from static analysis, while they still reach `translate_nooped_plural()`. The `_n_noop()` calls are restored, and the `literal-string` errors are ignored inline with the reason, next to the existing `phpcs:ignore` for the corresponding I18n sniffs. This keeps the deviation visible where the runtime values enter the nooped plural. Each line ignores `argument.type` twice, once for `$singular` and once for `$plural`. Co-Authored-By: Claude Opus 5.5 --- src/wp-includes/category-template.php | 16 +++------------- src/wp-includes/post.php | 14 ++------------ 2 files changed, 5 insertions(+), 25 deletions(-) diff --git a/src/wp-includes/category-template.php b/src/wp-includes/category-template.php index bcc5a5d87c457..dbb1ff8ab0d2c 100644 --- a/src/wp-includes/category-template.php +++ b/src/wp-includes/category-template.php @@ -894,19 +894,9 @@ function wp_generate_tag_cloud( $tags, $args = '' ) { $translate_nooped_plural = false; } } elseif ( isset( $args['single_text'] ) && isset( $args['multiple_text'] ) ) { - /* - * If no callback exists, look for the old-style single_text and multiple_text arguments. - * These are runtime values, so they cannot be marked for translation with _n_noop(), - * which requires string literals. The nooped plural is built directly instead. - */ - $translate_nooped_plural = array( - 0 => $args['single_text'], - 1 => $args['multiple_text'], - 'singular' => $args['single_text'], - 'plural' => $args['multiple_text'], - 'context' => null, - 'domain' => null, - ); + // If no callback exists, look for the old-style single_text and multiple_text arguments. + // phpcs:ignore WordPress.WP.I18n.NonSingularStringLiteralSingular,WordPress.WP.I18n.NonSingularStringLiteralPlural + $translate_nooped_plural = _n_noop( $args['single_text'], $args['multiple_text'] ); // @phpstan-ignore argument.type, argument.type (These are runtime values, so there is nothing to extract for translation.) } else { // This is the default for when no callback, plural, or argument is passed in. /* translators: %s: Number of items (tags). */ diff --git a/src/wp-includes/post.php b/src/wp-includes/post.php index d605c68d0dd85..3708c578ea8d0 100644 --- a/src/wp-includes/post.php +++ b/src/wp-includes/post.php @@ -1502,18 +1502,8 @@ function register_post_status( $post_status, $args = array() ) { } if ( false === $args->label_count ) { - /* - * The label is a runtime value, so it cannot be marked for translation with _n_noop(), - * which requires string literals. The nooped plural is built directly instead. - */ - $args->label_count = array( - 0 => $args->label, - 1 => $args->label, - 'singular' => $args->label, - 'plural' => $args->label, - 'context' => null, - 'domain' => null, - ); + // phpcs:ignore WordPress.WP.I18n.NonSingularStringLiteralSingular,WordPress.WP.I18n.NonSingularStringLiteralPlural + $args->label_count = _n_noop( $args->label, $args->label ); // @phpstan-ignore argument.type, argument.type (The label is a runtime value, so there is nothing to extract for translation.) } $wp_post_statuses[ $post_status ] = $args;