Skip to content

Plugin Directory: Stop block validator falsely warning about missing script translations#624

Open
obenland wants to merge 14 commits intoWordPress:trunkfrom
obenland:fix/block-validator-translation-false-positive
Open

Plugin Directory: Stop block validator falsely warning about missing script translations#624
obenland wants to merge 14 commits intoWordPress:trunkfrom
obenland:fix/block-validator-translation-false-positive

Conversation

@obenland
Copy link
Copy Markdown
Member

@obenland obenland commented May 7, 2026

Summary

The block plugin validator at https://wordpress.org/plugins/developers/block-plugin-validator/ shows the warning "No translations are loaded for the scripts." for plugins that use the modern register_block_type( __DIR__ ) pattern with a textdomain declared in block.json. Since WordPress 5.7, core automatically calls wp_set_script_translations() for every script handle declared in block.json (script, viewScript, editorScript, etc.) when a textdomain is set, so the warning is a false positive in that case.

Block_Plugin_Checker::check_for_translation_function() only searched the parsed PHP function calls for a literal wp_set_script_translations and warned otherwise, regardless of whether translations were actually being registered.

Changes

  • cli/class-block-plugin-checker.phpcheck_for_translation_function() now also suppresses the warning when the plugin calls register_block_type / register_block_type_from_metadata and at least one parsed block.json declares a textdomain. If register_block_type is called but no block.json sets textdomain, the warning still fires (translations genuinely won't load in that case).
  • shortcodes/class-block-validator.php — updated the detailed help text for that check to mention the auto-registration path so authors aren't told to add a redundant manual call.

Test plan

  • On /developers/block-plugin-validator/, run the checker against a block plugin that registers via register_block_type( __DIR__ ) with "textdomain": "..." in block.json and no explicit wp_set_script_translations() — confirm the "No translations are loaded for the scripts." warning is gone.
  • Run the checker against a plugin that calls register_block_type without a textdomain in block.json — the warning should still appear.
  • Run the checker against a plugin that calls wp_set_script_translations() directly — no warning (existing behavior, unchanged).
  • Open the warning's <details> on a plugin that does trigger it, and verify the updated help text reads correctly.

🤖 Generated with Claude Code

…re auto-loaded via register_block_type().

Since WordPress 5.7, register_block_type() and register_block_type_from_metadata() automatically call wp_set_script_translations() for scripts declared in block.json when a textdomain is set. The validator's translation check only looked for a literal wp_set_script_translations() call, producing a false-positive warning for plugins using the modern block.json registration pattern.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings May 7, 2026 21:44
@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 7, 2026

The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the props-bot label.

Core Committers: Use this line as a base for the props when committing in SVN:

Props obenland.

To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates the Plugin Directory’s block plugin validator to avoid a false-positive warning about missing script translations for plugins that rely on register_block_type( __DIR__ ) / metadata registration with textdomain set in block.json (where core auto-calls wp_set_script_translations() since WP 5.7).

Changes:

  • Extend Block_Plugin_Checker::check_for_translation_function() to treat metadata-based block registration + block.json textdomain as satisfying the script-translation requirement.
  • Update the validator’s detailed help text to describe the automatic translation registration path so authors aren’t told to add a redundant manual call.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

File Description
wordpress.org/public_html/wp-content/plugins/plugin-directory/cli/class-block-plugin-checker.php Suppresses the “No translations are loaded for the scripts.” warning when blocks are registered via metadata and block.json declares a textdomain.
wordpress.org/public_html/wp-content/plugins/plugin-directory/shortcodes/class-block-validator.php Updates the warning’s detailed help text to mention the auto-registration behavior in core.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

obenland and others added 2 commits May 7, 2026 16:49
…nslation check.

Locks in the new behavior so the false-positive warning can't regress: confirms the check passes when wp_set_script_translations is called directly, when register_block_type or register_block_type_from_metadata is paired with a textdomain in block.json, and that it still warns when the textdomain is missing or no translation signal is present at all.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…est cases.

Each test method now has a docblock spelling out the scenario it locks in (direct call, register_block_type with/without textdomain, etc.), and the class itself has a short description. Brings the file in line with WordPress-Docs phpcs.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings May 7, 2026 21:52
…Unit coverage metadata.

Documents intent and silences PHPUnit 11's missing-coverage-metadata notice without requiring the global config to opt into requireCoverageMetadata.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…translation check.

- find_php_functions() previously merged per-file results with array union (`+=`), which silently dropped every entry whose numeric key was already present from an earlier file. Switched to array_push() so calls from all files are retained — without this fix, the translation check could miss register_block_type() in any file other than the first.
- Renamed `$registers_block_from_metadata` to `$registers_block_type` since it also matches the classic register_block_type( 'namespace/name', $args ) form.
- Help text now mentions both register_block_type() and register_block_type_from_metadata() so authors using either function aren't told to add a redundant manual call.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

obenland and others added 2 commits May 7, 2026 17:02
…mment to a single line.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… metadata-based registration.

WordPress only auto-registers script translations when blocks are registered from their block.json metadata - register_block_type( __DIR__ ) or register_block_type_from_metadata(). The classic register_block_type( 'namespace/name', $args ) form does not trigger that wiring, so the previous suppression could mask a real "no translations loaded" warning if such a plugin happened to also have a stray block.json with a textdomain set.

Detection is now done by source-scanning PHP files for register_block_type() calls whose first argument is something other than a 'ns/name' string literal, plus any register_block_type_from_metadata() call. The result is cached on the instance so tests can seed it via reflection without filesystem fixtures.

Help text updated to describe the metadata pattern explicitly. Tests rewritten to drive the new logic via the cache, including coverage for the false-positive scenario the previous version allowed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings May 7, 2026 22:36
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

obenland and others added 2 commits May 7, 2026 17:42
… files that actually call register_block_type().

find_php_functions() already records every function call as [name, line, file] in $php_function_calls. The metadata-registration helper now consults that index first: it short-circuits true on any register_block_type_from_metadata() call (metadata-based by definition), short-circuits false when no register_block_type call is recorded anywhere, and otherwise only opens the specific files where register_block_type appears to inspect its first argument.

For plugins with no block registration at all, this drops the previous full-tree file_get_contents() pass entirely. For plugins that do register a block, it scans at most a handful of files instead of every PHP file in the tree.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…izer instead of raw regex.

Scanning raw file contents could match register_block_type( __DIR__ ) inside a comment or string literal and falsely treat the plugin as metadata-registering. The new helper uses token_get_all() so only real T_STRING calls are inspected: the first non-whitespace token after the opening paren is read directly, with T_CONSTANT_ENCAPSED_STRING values trimmed and matched against the classic 'namespace/name' shape. Anything else (variable, __DIR__, dirname() expression, path string) is treated as metadata. Verified against 12 representative cases including misleading comments and string literals.

Also consolidated two test cases that exercised the same scenario from the perspective of check_for_translation_function() (metadata-helper false + textdomain present); the surviving test name now describes the actual condition rather than the misleading "classic register_block_type" framing.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings May 8, 2026 00:53
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 5 comments.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

…comments and method/static call noise.

The first-argument scan was only skipping T_WHITESPACE, so an inline comment between `(` and the first argument (e.g. `register_block_type( /* name */ 'ns/block', $args )`) would produce a comment token where the function expected an argument and short-circuit to "metadata-based". Now also skips T_COMMENT and T_DOC_COMMENT.

The T_STRING match for `register_block_type` also flagged `$obj->register_block_type(...)`, `Foo::register_block_type(...)`, `$obj?->register_block_type(...)`, and `function register_block_type(...)` declarations. Now checks the previous significant token and skips the call when it follows T_OBJECT_OPERATOR, T_NULLSAFE_OBJECT_OPERATOR, T_DOUBLE_COLON, T_FUNCTION, or T_NEW.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated no new comments.

…view.

Drop the single-call lazy cache, dedupe three whitespace/comment-skip token
loops into a shared next_significant_token_index() helper, lift the canonical
block-name pattern into a BLOCK_NAME_REGEX class constant, and reshape the
unit tests to drive plugin_registers_block_from_metadata() through real
signals in $php_function_calls instead of reflecting a private cache.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…n check.

Replace the metadata-form `register_block_type()` disambiguation pass with a
simpler rule: any `register_block_type*` call plus a `textdomain` in block.json
counts as auto-loaded translations. The classic-form false-negative is a corner
case and not worth the tokenizer scaffolding.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings May 8, 2026 02:39
…ure plugin.

Adds an end-to-end test that runs find_blocks() and find_php_functions() over a
real fixture plugin (register_block_type_from_metadata + block.json with a
textdomain) to verify the translation check passes — exercising the actual
file-scanning paths instead of just seeded properties. Excludes tests/fixtures
from PHPUnit discovery so the fixture plugin file isn't treated as a test class.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

…k_type( __DIR__ ).

Drops the unnecessary add_action() wrapper from the textdomain fixture (which
tripped PEAR.Functions.FunctionCallSignature.MultipleArguments) — the tokenizer
just needs to see the bare register_block_type_from_metadata() call. Adds a
second fixture using register_block_type( __DIR__ ) form to cover the other
metadata-registration entry path the simplified check is meant to catch.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings May 8, 2026 02:50
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 8 out of 8 changed files in this pull request and generated no new comments.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants