Skip to content

Search Blocks: enable @wordpress/i18n in the IAPI view bundle (SEARCH-168)#48551

Draft
kangzj wants to merge 2 commits intotrunkfrom
echo/search-168-localize-rating-aria-label
Draft

Search Blocks: enable @wordpress/i18n in the IAPI view bundle (SEARCH-168)#48551
kangzj wants to merge 2 commits intotrunkfrom
echo/search-168-localize-rating-aria-label

Conversation

@kangzj
Copy link
Copy Markdown
Contributor

@kangzj kangzj commented May 6, 2026

Fixes SEARCH-168

Why

A shopper using a screen reader on a non-English Jetpack Search store currently hears the product rating in English ("4.5 out of 5 stars based on 42 reviews") even though everything else on the page is translated. The fix gives the Interactivity API view bundle a real @wordpress/i18n it can call, so the rating aria-label — and every other JS-emitted view-bundle string — picks up the page locale on first paint.

This also unblocks any future Jetpack Search block code that wants to call __() / _n() / sprintf() directly instead of detouring through state.strings.* seeded from PHP.

Proposed changes

  • Webpack — externalize @wordpress/i18n as a script module. tools/webpack.blocks.config.js now passes a requestToExternalModule callback that returns 'module @wordpress/i18n' so the IAPI bundle emits a hoisted static import * from "@wordpress/i18n" (same pattern DEP already uses for @wordpress/interactivity). DEP previously threw "Attempted to use WordPress script in a module" because core only registers @wordpress/interactivity (and a11y / router) as script modules.
  • Tiny ESM shim at src/search-blocks/store/i18n-shim.js. Re-exports window.wp.i18n's methods (__, _x, _n, _nx, sprintf, isRTL, hasTranslation, setLocaleData) with identity fallbacks if wp-i18n somehow didn't load. Built as its own webpack entry → build/search-blocks/store/i18n-shim.js.
  • PHP — register the shim under the canonical @wordpress/i18n module ID in Search_Blocks::register_i18n_module() via wp_register_script_module(). Browser's import map now resolves @wordpress/i18n to the shim.
  • PHP — populate window.wp.i18n with the page locale. Search_Blocks::enqueue_i18n_runtime() enqueues the classic wp-i18n script and wp_add_inline_scripts a setLocaleData() call built from get_translations_for_domain( 'jetpack-search-pkg' ). Classic scripts run synchronously before deferred modules, so by the time the shim evaluates, window.wp.i18n.__() already knows the locale's translations. The JSON payload is encoded with JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT so a translation containing </script> can't break out of the inline tag.
  • Use the new path in three view-bundle call sites:
    • result-utils.js buildRatingAriaLabel — the original SEARCH-168 offender. Now calls __( '%s out of 5 stars', ... ) for the no-reviews branch and _n( '%1$s out of 5 stars based on %2$d review', '%1$s out of 5 stars based on %2$d reviews', reviewCount, ... ) + sprintf for the with-reviews branch (matches the source strings the editor's edit.js already extracts).
    • store/index.js computeResultsCountText — drops the state.strings?.searching ?? 'Searching…' indirection in favour of native __/_n/sprintf.
    • active-filters/view.js activePills — same swap on the "Remove %s" pill aria-label.
  • Drop the now-unused build_initial_strings() PHP seed and its test assertions. Strings live in JS source now.

Related product discussion/links

Does this pull request change what data or activity we track or use?

No.

Testing instructions

  1. Pull the branch and run the four-layer build matrix:
    pnpm jetpack build packages/my-jetpack
    pnpm jetpack build plugins/jetpack
    pnpm jetpack build packages/search
    pnpm jetpack build plugins/search
    
  2. Unit tests:
    • cd projects/packages/search && pnpm run test-scripts — 416/416 JS tests pass.
    • cd projects/packages/search && composer phpunit -- --filter=Search_Blocks_Test — 18/18 PHP tests pass.
  3. In the browser (any dev env where Jetpack Search blocks are enabled — e.g. with add_filter( 'jetpack_search_blocks_enabled', '__return_true' )):
    • Visit a page that uses any Jetpack Search 3.0 block (e.g. search-results).
    • In DevTools, open the page source and confirm the import map contains:
      "@wordpress/i18n":"http://.../jetpack-search/.../build/search-blocks/store/i18n-shim.js?ver=..."
      
    • In the console:
      wp.i18n.sprintf( wp.i18n._n( 'Found %d result', 'Found %d results', 5, 'jetpack-search-pkg' ), 5 )
      // → "Found 5 results"   (en_US — source string round-trips)
      // → translated equivalent on a non-English locale that has a .mo file
    • Run a search that returns hits — the count text below the input should read "Found N results" and update as you type. Source it from the new __/_n/sprintf path, not the old PHP-seeded state.strings.
    • If WC products are indexed, switch a search-results block to the product layout and inspect a card's rating row: the aria-label should now be the translated "X out of 5 stars based on N reviews" string (front end matches editor preview).

@kangzj kangzj added [Status] Needs Review This PR is ready for review. [Package] Search Contains core Search functionality for Jetpack and Search plugins labels May 6, 2026
@kangzj kangzj self-assigned this May 6, 2026
@kangzj

This comment has been minimized.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 6, 2026

Are you an Automattician? Please test your changes on all WordPress.com environments to help mitigate accidental explosions.

  • To test on WoA, go to the Plugins menu on a WoA dev site. Click on the "Upload" button and follow the upgrade flow to be able to upload, install, and activate the Jetpack Beta plugin. Once the plugin is active, go to Jetpack > Jetpack Beta, select your plugin (Jetpack), and enable the echo/search-168-localize-rating-aria-label branch.
  • To test on Simple, run the following command on your sandbox:
bin/jetpack-downloader test jetpack echo/search-168-localize-rating-aria-label

Interested in more tips and information?

  • In your local development environment, use the jetpack rsync command to sync your changes to a WoA dev blog.
  • Read more about our development workflow here: PCYsg-eg0-p2
  • Figure out when your changes will be shipped to customers here: PCYsg-eg5-p2

@claude

This comment has been minimized.

@kangzj

This comment has been minimized.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 6, 2026

Thank you for your PR!

When contributing to Jetpack, we have a few suggestions that can help us test and review your patch:

  • ✅ Include a description of your PR changes.
  • ✅ Add a "[Status]" label (In Progress, Needs Review, ...).
  • ✅ Add testing instructions.
  • ✅ Specify whether this PR includes any changes to data or privacy.
  • ✅ Add changelog entries to affected projects

This comment will be updated as you work on your PR and make changes. If you think that some of those checks are not needed for your PR, please explain why you think so. Thanks for cooperation 🤖


Follow this PR Review Process:

  1. Ensure all required checks appearing at the bottom of this PR are passing.
  2. Make sure to test your changes on all platforms that it applies to. You're responsible for the quality of the code you ship.
  3. You can use GitHub's Reviewers functionality to request a review.
  4. When it's reviewed and merged, you will be pinged in Slack to deploy the changes to WordPress.com simple once the build is done.

If you have questions about anything, reach out in #jetpack-developers for guidance!

This comment has been minimized.

Copilot finished work on behalf of kangzj May 6, 2026 03:48
@jp-launch-control

This comment has been minimized.

@kangzj kangzj force-pushed the echo/search-168-localize-rating-aria-label branch from 3cf1b71 to 74f8b90 Compare May 6, 2026 03:56
@kangzj

This comment has been minimized.

@kangzj

This comment has been minimized.

@kangzj kangzj added [Status] In Progress and removed [Status] Needs Review This PR is ready for review. labels May 6, 2026
@kangzj

This comment has been minimized.

@kangzj

This comment has been minimized.

@claude

This comment has been minimized.

This comment has been minimized.

Copilot finished work on behalf of kangzj May 6, 2026 04:00
kangzj added 2 commits May 6, 2026 16:10
…-168)

Externalize @wordpress/i18n to a script-module reference (via DEP's
requestToExternalModule) and register a tiny ESM shim that re-exports
window.wp.i18n as the @wordpress/i18n module. The classic wp-i18n script
is enqueued on every search-blocks page so window.wp.i18n is populated
synchronously before any deferred module evaluates; translations are
inlined as setLocaleData() against PHP's already-loaded gettext entries
for jetpack-search-pkg, so __() / _n() / sprintf() return localized
strings on first paint without depending on per-handle .json files.

Use the new path in:
- result-utils.js buildRatingAriaLabel — fixes the SEARCH-168 frontend
  product-rating aria label (was hardcoded English when used outside the
  editor preview).
- store/index.js computeResultsCountText — replaces the PHP-seeded
  state.strings reads with native __()/_n()/sprintf().
- active-filters/view.js activePills — same swap.

Drop the now-unused build_initial_strings() PHP seed and the test
assertions that pinned its shape.
…hangelog Type

- Fix the inverted "first-wins" claim in register_i18n_module()'s
  docblock: ours wins because we register first; flag the maintenance
  hazard if WP core later ships a native @wordpress/i18n module
  (raised by both Copilot and claude[bot]).
- Extract build_locale_data_payload() out of collect_locale_data() and
  cover it with two unit tests (Jed shape from a fake Translations,
  default Plural-Forms fallback when the header is missing). Closes
  the gap both reviewers flagged for the new PHP code.
- Make webpack.blocks.config.js's requestToExternalModule explicit:
  early-return for non-i18n requests so the DEP fall-through is
  visible at a glance (claude[bot] nit).
- Update changelog Type from "changed" to "fixed" to match the
  SEARCH-168 framing (Copilot nit).
@kangzj kangzj force-pushed the echo/search-168-localize-rating-aria-label branch from 74f8b90 to ef8151e Compare May 6, 2026 04:10
@kangzj
Copy link
Copy Markdown
Contributor Author

kangzj commented May 6, 2026

Review-cycle summary — 3cf1b71ef8151e

2 round(s); CI green; both AI reviewers approved; 0 inline threads to resolve.

What changed during the cycle

Commits added:

  • a74c2a5 — Search Blocks: enable @wordpress/i18n in the IAPI view bundle (SEARCH-168)
  • ef8151e — Address review: docblock, test coverage, explicit DEP fall-through, changelog Type

Diff summary: 10 files changed, 335 insertions(+), 90 deletions(-)

Review threads addressed:

Source Comment Resolution
claude[bot] (#IC_kwDOAOho7M8AAAABBVwvcA) inverted "first-wins" docblock; missing collect_locale_data() test coverage; explicit DEP fall-through; Plural-Forms casing; _nx fallback signature Docblock rewritten to match actual semantics (ef8151e); build_locale_data_payload() extracted with two new PHPUnit cases; requestToExternalModule now leads with an explicit early-return; casing handled via ?? fallback to the Western 2-form rule; shim fallback signature scoped to the unreachable wp-i18n-missing case.
copilot-swe-agent (#IC_kwDOAOho7M8AAAABBVyIQQ) same three actionable items + changelog Type: changedType: fixed Same commit ef8151e; changelog Type updated.
claude[bot] (re-review) (#IC_kwDOAOho7M8AAAABBV05Wg) "Ready to merge." No action — confirmation.
copilot-swe-agent (re-review) (#IC_kwDOAOho7M8AAAABBV1YTA) "Looks good to merge." No action — confirmation.
jp-launch-control (#IC_kwDOAOho7M8AAAABBVz1Cw) Code-coverage delta (-6.80% in class-search-blocks.php; new i18n-shim.js at 0/16) Coverage check itself is pass. The drop is enqueue_i18n_runtime() / register_i18n_module() / collect_locale_data() wrapping WP registry functions (not unit-testable without a WP bootstrap), and i18n-shim.js is a browser-only ESM shim whose runtime path is exercised through result-utils.test.js / store.test.js against the real @wordpress/i18n npm package. Both reviewers explicitly accepted the gap as "no action needed."

Unaddressed (flagged for owner): None.

CI: all required checks passing (Test plugin upgrades is pending, deploy-preview only — not blocking).

@kangzj kangzj added [Status] Needs Team Review Obsolete. Use Needs Review instead. and removed [Status] In Progress labels May 6, 2026
@kangzj kangzj marked this pull request as draft May 6, 2026 04:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

[Package] Search Contains core Search functionality for Jetpack and Search plugins [Status] Needs Team Review Obsolete. Use Needs Review instead. [Tests] Includes Tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants