Plugin Directory: Stop block validator falsely warning about missing script translations#624
Conversation
…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>
|
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 Core Committers: Use this line as a base for the props when committing in SVN: To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook. |
There was a problem hiding this comment.
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.jsontextdomainas 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.
…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>
…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>
…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>
… 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>
…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>
…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>
…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>
…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>
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 atextdomaindeclared inblock.json. Since WordPress 5.7, core automatically callswp_set_script_translations()for every script handle declared inblock.json(script,viewScript,editorScript, etc.) when atextdomainis 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 literalwp_set_script_translationsand warned otherwise, regardless of whether translations were actually being registered.Changes
cli/class-block-plugin-checker.php—check_for_translation_function()now also suppresses the warning when the plugin callsregister_block_type/register_block_type_from_metadataand at least one parsedblock.jsondeclares atextdomain. Ifregister_block_typeis called but noblock.jsonsetstextdomain, 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
/developers/block-plugin-validator/, run the checker against a block plugin that registers viaregister_block_type( __DIR__ )with"textdomain": "..."inblock.jsonand no explicitwp_set_script_translations()— confirm the "No translations are loaded for the scripts." warning is gone.register_block_typewithout atextdomaininblock.json— the warning should still appear.wp_set_script_translations()directly — no warning (existing behavior, unchanged).<details>on a plugin that does trigger it, and verify the updated help text reads correctly.🤖 Generated with Claude Code