Commit 8ffab6d6147 for woocommerce
commit 8ffab6d6147da79b12f1218ffa541a71a0675dce
Author: Raluca Stan <ralucastn@gmail.com>
Date: Thu Aug 27 15:09:50 2026 +0200
Translate the default Cart page empty cart headings at render time (#67651)
* Fix empty cart headings not translating on the default Cart page
* Add changelog entry for the cart empty message translation fix
* Register cart patterns independently of the Cart block type
* Restore the Browse store link on the default empty cart page
The empty cart lost its "Browse store" link in 8.3.0 (#40867), when the default
Cart page moved from a block template to installer-generated content that inlined
only the title. Keep the full cart-empty-message pattern and reference it from the
installer so the link comes back and stays translatable, with no change to the
pattern's registered content.
Move the pattern-registration test into the BlockTypesController suite where the
registration now lives, and fix the now-stale Cart::register_patterns docblock.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* Address review notes on the cart pattern registration test
Drop the try/finally from the pattern registration test: re-running the method
under test can't restore state a failing run left behind, and on a passing run
it does nothing.
Reword the comment that claimed the controller runs unconditionally, which is
no longer accurate, and trim the changelog entry.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* Call wc_get_page_permalink through the legacy proxy
Route the shop permalink lookup in register_block_patterns() through
WC()->call_function() so it can be mocked in unit tests, matching how the rest
of src/ calls legacy functions.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
diff --git a/plugins/woocommerce/changelog/fix-48583-cart-empty-message-translation b/plugins/woocommerce/changelog/fix-48583-cart-empty-message-translation
new file mode 100644
index 00000000000..94660996227
--- /dev/null
+++ b/plugins/woocommerce/changelog/fix-48583-cart-empty-message-translation
@@ -0,0 +1,4 @@
+Significance: patch
+Type: fix
+
+Store the default Cart page empty cart headings as block pattern references so they follow the site language instead of the install language, and restore the missing "Browse store" link.
diff --git a/plugins/woocommerce/includes/class-wc-install.php b/plugins/woocommerce/includes/class-wc-install.php
index a7c47fbd0a5..fed5ac272b1 100644
--- a/plugins/woocommerce/includes/class-wc-install.php
+++ b/plugins/woocommerce/includes/class-wc-install.php
@@ -3188,17 +3188,13 @@ EOT;
<!-- /wp:woocommerce/filled-cart-block -->
<!-- wp:woocommerce/empty-cart-block -->
-<div class="wp-block-woocommerce-empty-cart-block"><!-- wp:heading {"textAlign":"center","className":"with-empty-cart-icon wc-block-cart__empty-cart__title"} -->
-<h2 class="wp-block-heading has-text-align-center with-empty-cart-icon wc-block-cart__empty-cart__title">' . __( 'Your cart is currently empty!', 'woocommerce' ) . '</h2>
-<!-- /wp:heading -->
+<div class="wp-block-woocommerce-empty-cart-block"><!-- wp:pattern {"slug":"woocommerce/cart-empty-message"} /-->
<!-- wp:separator {"className":"is-style-dots"} -->
<hr class="wp-block-separator has-alpha-channel-opacity is-style-dots"/>
<!-- /wp:separator -->
-<!-- wp:heading {"textAlign":"center"} -->
-<h2 class="wp-block-heading has-text-align-center">' . __( 'New in store', 'woocommerce' ) . '</h2>
-<!-- /wp:heading -->
+<!-- wp:pattern {"slug":"woocommerce/cart-new-in-store-message"} /-->
<!-- wp:woocommerce/product-new {"columns":4,"rows":1} /--></div>
<!-- /wp:woocommerce/empty-cart-block --></div>
diff --git a/plugins/woocommerce/src/Blocks/BlockTypes/Cart.php b/plugins/woocommerce/src/Blocks/BlockTypes/Cart.php
index aea17c68d7d..7c8465fc088 100644
--- a/plugins/woocommerce/src/Blocks/BlockTypes/Cart.php
+++ b/plugins/woocommerce/src/Blocks/BlockTypes/Cart.php
@@ -68,11 +68,13 @@ class Cart extends AbstractBlock {
}
/**
- * Register block pattern for Empty Cart Message to make it translatable.
+ * Register the Cart heading and cross-sells message block patterns.
+ *
+ * The empty cart and "New in store" patterns the default Cart page references are registered
+ * in BlockTypesController::register_block_patterns() instead, so they resolve even when the
+ * Cart block type is not registered.
*/
public function register_patterns() {
- $shop_permalink = wc_get_page_permalink( 'shop' );
-
register_block_pattern(
'woocommerce/cart-heading',
array(
@@ -89,25 +91,6 @@ class Cart extends AbstractBlock {
'content' => '<!-- wp:heading {"fontSize":"large"} --><h2 class="wp-block-heading has-large-font-size">' . esc_html__( 'You may be interested in…', 'woocommerce' ) . '</h2><!-- /wp:heading -->',
)
);
- register_block_pattern(
- 'woocommerce/cart-empty-message',
- array(
- 'title' => '',
- 'inserter' => false,
- 'content' => '
- <!-- wp:heading {"textAlign":"center","className":"with-empty-cart-icon wc-block-cart__empty-cart__title"} --><h2 class="wp-block-heading has-text-align-center with-empty-cart-icon wc-block-cart__empty-cart__title">' . esc_html__( 'Your cart is currently empty!', 'woocommerce' ) . '</h2><!-- /wp:heading -->
- <!-- wp:paragraph {"align":"center"} --><p class="has-text-align-center"><a href="' . esc_attr( esc_url( $shop_permalink ) ) . '">' . esc_html__( 'Browse store', 'woocommerce' ) . '</a></p><!-- /wp:paragraph -->
- ',
- )
- );
- register_block_pattern(
- 'woocommerce/cart-new-in-store-message',
- array(
- 'title' => '',
- 'inserter' => false,
- 'content' => '<!-- wp:heading {"textAlign":"center"} --><h2 class="wp-block-heading has-text-align-center">' . esc_html__( 'New in store', 'woocommerce' ) . '</h2><!-- /wp:heading -->',
- )
- );
}
/**
diff --git a/plugins/woocommerce/src/Blocks/BlockTypesController.php b/plugins/woocommerce/src/Blocks/BlockTypesController.php
index 44cfa050716..13646e13738 100644
--- a/plugins/woocommerce/src/Blocks/BlockTypesController.php
+++ b/plugins/woocommerce/src/Blocks/BlockTypesController.php
@@ -243,6 +243,24 @@ final class BlockTypesController {
'content' => '<!-- wp:heading {"level":2,"style":{"typography":{"fontSize":"24px"}}} --><h2 class="wp-block-heading" style="font-size:24px">' . esc_html__( 'Additional information', 'woocommerce' ) . '</h2><!-- /wp:heading -->',
)
);
+ // Referenced from the default Cart page content created at install; registration must not depend on the Cart block type being enabled, or the page renders nothing for the reference.
+ $shop_permalink = WC()->call_function( 'wc_get_page_permalink', 'shop' );
+ register_block_pattern(
+ 'woocommerce/cart-empty-message',
+ array(
+ 'title' => '',
+ 'inserter' => false,
+ 'content' => '<!-- wp:heading {"textAlign":"center","className":"with-empty-cart-icon wc-block-cart__empty-cart__title"} --><h2 class="wp-block-heading has-text-align-center with-empty-cart-icon wc-block-cart__empty-cart__title">' . esc_html__( 'Your cart is currently empty!', 'woocommerce' ) . '</h2><!-- /wp:heading --><!-- wp:paragraph {"align":"center"} --><p class="has-text-align-center"><a href="' . esc_attr( esc_url( $shop_permalink ) ) . '">' . esc_html__( 'Browse store', 'woocommerce' ) . '</a></p><!-- /wp:paragraph -->',
+ )
+ );
+ register_block_pattern(
+ 'woocommerce/cart-new-in-store-message',
+ array(
+ 'title' => '',
+ 'inserter' => false,
+ 'content' => '<!-- wp:heading {"textAlign":"center"} --><h2 class="wp-block-heading has-text-align-center">' . esc_html__( 'New in store', 'woocommerce' ) . '</h2><!-- /wp:heading -->',
+ )
+ );
}
/**
diff --git a/plugins/woocommerce/tests/php/includes/class-wc-install-test.php b/plugins/woocommerce/tests/php/includes/class-wc-install-test.php
index 71c659f795d..f0fb7c791ed 100644
--- a/plugins/woocommerce/tests/php/includes/class-wc-install-test.php
+++ b/plugins/woocommerce/tests/php/includes/class-wc-install-test.php
@@ -615,4 +615,74 @@ class WC_Install_Test extends \WC_Unit_Test_Case {
'A custom merchant placeholder attachment should not be deleted.'
);
}
+
+ /**
+ * @testdox Should reference block patterns instead of baking translated empty cart strings into the Cart page content.
+ */
+ public function test_cart_block_content_references_empty_cart_patterns(): void {
+ $method = new ReflectionMethod( WC_Install::class, 'get_cart_block_content' );
+ $method->setAccessible( true );
+ $content = $method->invoke( null );
+
+ $this->assertStringContainsString(
+ '<!-- wp:pattern {"slug":"woocommerce/cart-empty-message"} /-->',
+ $content,
+ 'The empty cart title should be stored as a pattern reference so it is translated at render time.'
+ );
+ $this->assertStringContainsString(
+ '<!-- wp:pattern {"slug":"woocommerce/cart-new-in-store-message"} /-->',
+ $content,
+ 'The "New in store" heading should be stored as a pattern reference so it is translated at render time.'
+ );
+ $this->assertStringNotContainsString(
+ 'Your cart is currently empty!',
+ $content,
+ 'The empty cart title must not be frozen into the page content in the install-time locale.'
+ );
+ $this->assertStringNotContainsString(
+ 'New in store',
+ $content,
+ 'The "New in store" heading must not be frozen into the page content in the install-time locale.'
+ );
+ }
+
+ /**
+ * @testdox Should render the empty cart title, the Browse store link, and the New in store heading from the referenced patterns.
+ */
+ public function test_empty_cart_message_patterns_render_expected_markup(): void {
+ $registry = WP_Block_Patterns_Registry::get_instance();
+ $this->assertTrue(
+ $registry->is_registered( 'woocommerce/cart-empty-message' ),
+ 'The cart-empty-message pattern must be registered during bootstrap; the installed Cart page renders nothing for it otherwise.'
+ );
+ $this->assertTrue(
+ $registry->is_registered( 'woocommerce/cart-new-in-store-message' ),
+ 'The cart-new-in-store-message pattern must be registered during bootstrap; the installed Cart page renders nothing for it otherwise.'
+ );
+
+ $rendered = do_blocks(
+ '<!-- wp:pattern {"slug":"woocommerce/cart-empty-message"} /--><!-- wp:pattern {"slug":"woocommerce/cart-new-in-store-message"} /-->'
+ );
+
+ $this->assertStringContainsString(
+ 'Your cart is currently empty!',
+ $rendered,
+ 'The cart-empty-message pattern should render the empty cart title.'
+ );
+ $this->assertStringContainsString(
+ 'wc-block-cart__empty-cart__title',
+ $rendered,
+ 'The rendered empty cart title should keep the markup the installer previously inlined.'
+ );
+ $this->assertStringContainsString(
+ 'New in store',
+ $rendered,
+ 'The cart-new-in-store-message pattern should render the "New in store" heading.'
+ );
+ $this->assertStringContainsString(
+ 'Browse store',
+ $rendered,
+ 'The cart-empty-message pattern should render the Browse store link that the default Cart page lost when it moved to installer-generated content in 8.3.0.'
+ );
+ }
}
diff --git a/plugins/woocommerce/tests/php/src/Blocks/BlockTypesController.php b/plugins/woocommerce/tests/php/src/Blocks/BlockTypesController.php
index 6edfde59d5f..06cd5041e96 100644
--- a/plugins/woocommerce/tests/php/src/Blocks/BlockTypesController.php
+++ b/plugins/woocommerce/tests/php/src/Blocks/BlockTypesController.php
@@ -93,4 +93,32 @@ class BlockTypesController extends WC_Unit_Test_Case {
$answer = $this->block_types_controller->block_should_have_data_attributes( 'child-of-woo/block-name' );
$this->assertTrue( $answer );
}
+
+ /**
+ * @testdox register_block_patterns() registers the empty cart message patterns referenced by the installed Cart page.
+ */
+ public function test_register_block_patterns_registers_installed_cart_page_patterns(): void {
+ $registry = \WP_Block_Patterns_Registry::get_instance();
+
+ // The default Cart page created at install references these patterns
+ // (see WC_Install::get_cart_block_content()). Registering them here rather than
+ // in the Cart block type means the page can still resolve the references when
+ // the Cart block itself is not registered.
+ $slugs = array( 'woocommerce/cart-empty-message', 'woocommerce/cart-new-in-store-message' );
+
+ foreach ( $slugs as $slug ) {
+ if ( $registry->is_registered( $slug ) ) {
+ unregister_block_pattern( $slug );
+ }
+ }
+
+ $this->block_types_controller->register_block_patterns();
+
+ foreach ( $slugs as $slug ) {
+ $this->assertTrue(
+ $registry->is_registered( $slug ),
+ "BlockTypesController::register_block_patterns() should register {$slug}; the installed Cart page depends on it."
+ );
+ }
+ }
}