Commit a99b4813ef5 for woocommerce

commit a99b4813ef5d4c87ebd8d9899b5563191be26ffd
Author: Jorge A. Torres <jorge.torres@automattic.com>
Date:   Fri Jul 3 12:26:26 2026 +0100

    Warn when additional checkout fields are registered too early (#66172)

diff --git a/docs/block-development/extensible-blocks/cart-and-checkout-blocks/additional-checkout-fields.md b/docs/block-development/extensible-blocks/cart-and-checkout-blocks/additional-checkout-fields.md
index df738bdb8a1..9111fa6e1f7 100644
--- a/docs/block-development/extensible-blocks/cart-and-checkout-blocks/additional-checkout-fields.md
+++ b/docs/block-development/extensible-blocks/cart-and-checkout-blocks/additional-checkout-fields.md
@@ -199,7 +199,11 @@ There are plans to expand this list, but for now these are the types available.

 To register additional checkout fields you must use the `woocommerce_register_additional_checkout_field` function.

-It is recommended to run this function after the `woocommerce_init` action.
+:::note
+
+Register fields on the `woocommerce_init` action (or later). Registering earlier can cause initialization and translation issues.
+
+:::

 The registration function takes an array of options describing your field. Some field types take additional options.

diff --git a/plugins/woocommerce/changelog/issue-64592-checkout-fields-textdomain-too-early b/plugins/woocommerce/changelog/issue-64592-checkout-fields-textdomain-too-early
new file mode 100644
index 00000000000..8f5b0169cef
--- /dev/null
+++ b/plugins/woocommerce/changelog/issue-64592-checkout-fields-textdomain-too-early
@@ -0,0 +1,4 @@
+Significance: patch
+Type: fix
+
+Warn when additional checkout fields are registered before the woocommerce_init action, which can load translations too early.
diff --git a/plugins/woocommerce/src/Blocks/Domain/Services/CheckoutFields.php b/plugins/woocommerce/src/Blocks/Domain/Services/CheckoutFields.php
index 5e87b2f5504..458c98959b5 100644
--- a/plugins/woocommerce/src/Blocks/Domain/Services/CheckoutFields.php
+++ b/plugins/woocommerce/src/Blocks/Domain/Services/CheckoutFields.php
@@ -189,6 +189,11 @@ class CheckoutFields {
 	 * @return WP_Error|void True if the field was registered, a WP_Error otherwise.
 	 */
 	public function register_checkout_field( $options ) {
+		// Warn when fields are registered before `after_setup_theme`. Registering that early can cause problems, such as loading translations before they're ready.
+		if ( ! did_action( 'after_setup_theme' ) && ! doing_action( 'after_setup_theme' ) ) {
+			_doing_it_wrong( 'woocommerce_register_additional_checkout_field', 'Additional checkout fields should be registered on the woocommerce_init action or later.', '11.0.0' );
+		}
+
 		// Check the options and show warnings if they're not supplied. Return early if an error that would prevent registration is encountered.
 		if ( false === $this->validate_options( $options ) ) {
 			return;
diff --git a/plugins/woocommerce/tests/e2e/test-plugins/blocks/additional-checkout-fields.php b/plugins/woocommerce/tests/e2e/test-plugins/blocks/additional-checkout-fields.php
index 8c64fd01d65..ae2bfcf214d 100644
--- a/plugins/woocommerce/tests/e2e/test-plugins/blocks/additional-checkout-fields.php
+++ b/plugins/woocommerce/tests/e2e/test-plugins/blocks/additional-checkout-fields.php
@@ -14,7 +14,7 @@ class Additional_Checkout_Fields_Test_Helper {
 	public function __construct() {
 		add_action( 'plugins_loaded', array( $this, 'enable_custom_checkout_fields' ) );
 		add_action( 'plugins_loaded', array( $this, 'disable_custom_checkout_fields' ) );
-		add_action( 'woocommerce_loaded', array( $this, 'register_custom_checkout_fields' ) );
+		add_action( 'woocommerce_init', array( $this, 'register_custom_checkout_fields' ) );
 	}

 	/**
diff --git a/plugins/woocommerce/tests/php/src/Blocks/Domain/Services/CheckoutFieldsTest.php b/plugins/woocommerce/tests/php/src/Blocks/Domain/Services/CheckoutFieldsTest.php
index 394ead84ef6..678ec6ec342 100644
--- a/plugins/woocommerce/tests/php/src/Blocks/Domain/Services/CheckoutFieldsTest.php
+++ b/plugins/woocommerce/tests/php/src/Blocks/Domain/Services/CheckoutFieldsTest.php
@@ -188,4 +188,30 @@ class CheckoutFieldsTest extends WP_UnitTestCase {
 		$this->assertArrayHasKey( 'plugin-namespace/gov-id', $fields );
 		$this->assertArrayNotHasKey( 'namespace/vat-number', $fields );
 	}
+
+	/**
+	 * Registering a field before after_setup_theme warns the developer.
+	 */
+	public function test_registering_before_after_setup_theme_triggers_notice() {
+		$this->setExpectedIncorrectUsage( 'woocommerce_register_additional_checkout_field' );
+
+		$saved_action = $GLOBALS['wp_actions']['after_setup_theme'] ?? null;
+		unset( $GLOBALS['wp_actions']['after_setup_theme'] );
+
+		try {
+			woocommerce_register_additional_checkout_field(
+				array(
+					'id'       => 'test-namespace/early-field',
+					'label'    => 'Early field',
+					'location' => 'contact',
+				)
+			);
+		} finally {
+			if ( null !== $saved_action ) {
+				// phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited -- Restore the count cleared above to simulate registering before after_setup_theme.
+				$GLOBALS['wp_actions']['after_setup_theme'] = $saved_action;
+			}
+			__internal_woocommerce_blocks_deregister_checkout_field( 'test-namespace/early-field' );
+		}
+	}
 }