Commit b2e7345efab for woocommerce

commit b2e7345efab6fe658d235c14ffef10bb7379db08
Author: Miroslav Mitev <m1r0@users.noreply.github.com>
Date:   Thu Aug 27 13:21:09 2026 +0300

    Add localized thousands separators to legacy report counts (#68051)

    * Format legacy report legend counts with number_format_i18n()

diff --git a/plugins/woocommerce/changelog/67978-add-localized-thousands-separators-to-legacy-report-counts b/plugins/woocommerce/changelog/67978-add-localized-thousands-separators-to-legacy-report-counts
new file mode 100644
index 00000000000..03ac6ba9484
--- /dev/null
+++ b/plugins/woocommerce/changelog/67978-add-localized-thousands-separators-to-legacy-report-counts
@@ -0,0 +1,4 @@
+Significance: minor
+Type: enhancement
+
+Format count values in the legacy Reports chart legends with locale-aware thousands separators.
diff --git a/plugins/woocommerce/includes/admin/reports/class-wc-report-coupon-usage.php b/plugins/woocommerce/includes/admin/reports/class-wc-report-coupon-usage.php
index e5e8aade6d6..4fb6d93a915 100644
--- a/plugins/woocommerce/includes/admin/reports/class-wc-report-coupon-usage.php
+++ b/plugins/woocommerce/includes/admin/reports/class-wc-report-coupon-usage.php
@@ -116,7 +116,7 @@ class WC_Report_Coupon_Usage extends WC_Admin_Report {

 		$legend[] = array(
 			/* translators: %s: coupons amount */
-			'title'            => sprintf( __( '%s coupons used in total', 'woocommerce' ), '<strong>' . $total_coupons . '</strong>' ),
+			'title'            => sprintf( __( '%s coupons used in total', 'woocommerce' ), '<strong>' . number_format_i18n( $total_coupons ) . '</strong>' ),
 			'color'            => $this->chart_colours['coupon_count'],
 			'highlight_series' => 0,
 		);
diff --git a/plugins/woocommerce/includes/admin/reports/class-wc-report-customers.php b/plugins/woocommerce/includes/admin/reports/class-wc-report-customers.php
index 42ac76f73a6..35ef5a5acde 100644
--- a/plugins/woocommerce/includes/admin/reports/class-wc-report-customers.php
+++ b/plugins/woocommerce/includes/admin/reports/class-wc-report-customers.php
@@ -41,7 +41,7 @@ class WC_Report_Customers extends WC_Admin_Report {

 		$legend[] = array(
 			/* translators: %s: signups amount */
-			'title'            => sprintf( __( '%s signups in this period', 'woocommerce' ), '<strong>' . count( $this->customers ) . '</strong>' ),
+			'title'            => sprintf( __( '%s signups in this period', 'woocommerce' ), '<strong>' . number_format_i18n( count( $this->customers ) ) . '</strong>' ),
 			'color'            => $this->chart_colours['signups'],
 			'highlight_series' => 2,
 		);
diff --git a/plugins/woocommerce/includes/admin/reports/class-wc-report-sales-by-date.php b/plugins/woocommerce/includes/admin/reports/class-wc-report-sales-by-date.php
index c7c900682bd..a6df6ac8f9c 100644
--- a/plugins/woocommerce/includes/admin/reports/class-wc-report-sales-by-date.php
+++ b/plugins/woocommerce/includes/admin/reports/class-wc-report-sales-by-date.php
@@ -523,7 +523,7 @@ class WC_Report_Sales_By_Date extends WC_Admin_Report {
 			'title'            => sprintf(
 				/* translators: %s: total orders */
 				__( '%s orders placed', 'woocommerce' ),
-				'<strong>' . $data->total_orders . '</strong>'
+				'<strong>' . number_format_i18n( absint( $data->total_orders ) ) . '</strong>'
 			),
 			'color'            => $this->chart_colours['order_count'],
 			'highlight_series' => 1,
@@ -533,7 +533,7 @@ class WC_Report_Sales_By_Date extends WC_Admin_Report {
 			'title'            => sprintf(
 				/* translators: %s: total items */
 				__( '%s items purchased', 'woocommerce' ),
-				'<strong>' . $data->total_items . '</strong>'
+				'<strong>' . number_format_i18n( absint( $data->total_items ) ) . '</strong>'
 			),
 			'color'            => $this->chart_colours['item_count'],
 			'highlight_series' => 0,
@@ -541,10 +541,10 @@ class WC_Report_Sales_By_Date extends WC_Admin_Report {
 		$legend[] = array(
 			'title'            => sprintf(
 				/* translators: 1: total refunds 2: total refunded orders 3: refunded items */
-				_n( '%1$s refunded %2$d order (%3$d item)', '%1$s refunded %2$d orders (%3$d items)', $this->report_data->total_refunded_orders, 'woocommerce' ),
+				_n( '%1$s refunded %2$s order (%3$s item)', '%1$s refunded %2$s orders (%3$s items)', $this->report_data->total_refunded_orders, 'woocommerce' ),
 				'<strong>' . wc_price( $data->total_refunds ) . '</strong>',
-				$this->report_data->total_refunded_orders,
-				$this->report_data->refunded_order_items
+				number_format_i18n( absint( $this->report_data->total_refunded_orders ) ),
+				number_format_i18n( absint( $this->report_data->refunded_order_items ) )
 			),
 			'color'            => $this->chart_colours['refund_amount'],
 			'highlight_series' => 8,
diff --git a/plugins/woocommerce/includes/admin/reports/class-wc-report-sales-by-product.php b/plugins/woocommerce/includes/admin/reports/class-wc-report-sales-by-product.php
index a365dd78f60..14ac4cb0d7b 100644
--- a/plugins/woocommerce/includes/admin/reports/class-wc-report-sales-by-product.php
+++ b/plugins/woocommerce/includes/admin/reports/class-wc-report-sales-by-product.php
@@ -127,7 +127,7 @@ class WC_Report_Sales_By_Product extends WC_Admin_Report {

 		$legend[] = array(
 			/* translators: %s: total items purchased */
-			'title'            => sprintf( __( '%s purchases for the selected items', 'woocommerce' ), '<strong>' . ( $total_items ) . '</strong>' ),
+			'title'            => sprintf( __( '%s purchases for the selected items', 'woocommerce' ), '<strong>' . number_format_i18n( $total_items ) . '</strong>' ),
 			'color'            => $this->chart_colours['item_count'],
 			'highlight_series' => 0,
 		);
diff --git a/plugins/woocommerce/tests/legacy/unit-tests/admin/reports/class-wc-tests-report-customers.php b/plugins/woocommerce/tests/legacy/unit-tests/admin/reports/class-wc-tests-report-customers.php
new file mode 100644
index 00000000000..af381ec042b
--- /dev/null
+++ b/plugins/woocommerce/tests/legacy/unit-tests/admin/reports/class-wc-tests-report-customers.php
@@ -0,0 +1,63 @@
+<?php
+/**
+ * Class WC_Tests_Report_Customers file.
+ *
+ * @package WooCommerce\Tests\Admin\Reports
+ */
+
+declare(strict_types=1);
+
+/**
+ * Tests for the WC_Report_Customers class.
+ */
+class WC_Tests_Report_Customers extends WC_Unit_Test_Case {
+
+	/**
+	 * Thousands separator to restore after a test overrides it.
+	 *
+	 * @var string|null
+	 */
+	private $original_thousands_sep = null;
+
+	/**
+	 * Load the necessary files, as they're not automatically loaded by WooCommerce.
+	 */
+	public static function setUpBeforeClass(): void {
+		parent::setUpBeforeClass();
+
+		include_once WC_Unit_Tests_Bootstrap::instance()->plugin_dir . '/includes/admin/reports/class-wc-admin-report.php';
+		include_once WC_Unit_Tests_Bootstrap::instance()->plugin_dir . '/includes/admin/reports/class-wc-report-customers.php';
+	}
+
+	/**
+	 * Tear down the test.
+	 */
+	public function tearDown(): void {
+		global $wp_locale;
+
+		if ( null !== $this->original_thousands_sep ) {
+			$wp_locale->number_format['thousands_sep'] = $this->original_thousands_sep;
+			$this->original_thousands_sep              = null;
+		}
+
+		parent::tearDown();
+	}
+
+	/**
+	 * @testdox Should format the signup count with the locale thousands separator.
+	 */
+	public function test_get_chart_legend_formats_signups_with_locale_separator() {
+		global $wp_locale;
+
+		$this->original_thousands_sep              = $wp_locale->number_format['thousands_sep'];
+		$wp_locale->number_format['thousands_sep'] = '.';
+
+		$report                = new WC_Report_Customers();
+		$report->customers     = array_fill( 0, 1500, 0 );
+		$report->chart_colours = array( 'signups' => '#000000' );
+
+		$legend = implode( ' ', wp_list_pluck( $report->get_chart_legend(), 'title' ) );
+
+		$this->assertStringContainsString( '<strong>1.500</strong> signups in this period', $legend );
+	}
+}
diff --git a/plugins/woocommerce/tests/legacy/unit-tests/admin/reports/class-wc-tests-report-sales-by-date.php b/plugins/woocommerce/tests/legacy/unit-tests/admin/reports/class-wc-tests-report-sales-by-date.php
index b871119e446..569eed535c4 100644
--- a/plugins/woocommerce/tests/legacy/unit-tests/admin/reports/class-wc-tests-report-sales-by-date.php
+++ b/plugins/woocommerce/tests/legacy/unit-tests/admin/reports/class-wc-tests-report-sales-by-date.php
@@ -12,6 +12,20 @@ use Automattic\WooCommerce\Enums\OrderStatus;
  */
 class WC_Tests_Report_Sales_By_Date extends WC_Unit_Test_Case {

+	/**
+	 * Thousands separator to restore after a test overrides it.
+	 *
+	 * @var string|null
+	 */
+	private $original_thousands_sep = null;
+
+	/**
+	 * Counts to give the report through the woocommerce_admin_report_data filter.
+	 *
+	 * @var array
+	 */
+	private $report_counts = array();
+
 	/**
 	 * Load the necessary files, as they're not automatically loaded by WooCommerce.
 	 */
@@ -23,13 +37,17 @@ class WC_Tests_Report_Sales_By_Date extends WC_Unit_Test_Case {
 	}

 	/**
-	 * Set up the test.
+	 * Tear down the test.
 	 */
-	public function setUp(): void {
-		parent::setUp();
-		if ( \Automattic\WooCommerce\Utilities\OrderUtil::custom_orders_table_usage_is_enabled() ) {
-			$this->markTestSkipped( 'This test is not compatible with the custom orders table.' );
+	public function tearDown(): void {
+		global $wp_locale;
+
+		if ( null !== $this->original_thousands_sep ) {
+			$wp_locale->number_format['thousands_sep'] = $this->original_thousands_sep;
+			$this->original_thousands_sep              = null;
 		}
+
+		parent::tearDown();
 	}

 	/**
@@ -45,6 +63,10 @@ class WC_Tests_Report_Sales_By_Date extends WC_Unit_Test_Case {
 	 * Test: get_report_data
 	 */
 	public function test_get_report_data() {
+		if ( \Automattic\WooCommerce\Utilities\OrderUtil::custom_orders_table_usage_is_enabled() ) {
+			$this->markTestSkipped( 'This test is not compatible with the custom orders table.' );
+		}
+
 		update_option( 'woocommerce_default_customer_address', 'base' );
 		update_option( 'woocommerce_tax_based_on', 'base' );
 		update_option( 'woocommerce_calc_taxes', 'yes' );
@@ -159,4 +181,90 @@ class WC_Tests_Report_Sales_By_Date extends WC_Unit_Test_Case {
 		$this->assertEquals( 0, $data->total_shipping_tax_refunded );
 		$this->assertEquals( 0, $data->total_refunded_orders );
 	}
+
+	/**
+	 * @testdox Should format chart legend counts with the locale thousands separator.
+	 *
+	 * @dataProvider provide_legend_counts
+	 *
+	 * @param array    $counts   Counts to give the report.
+	 * @param string[] $expected Strings the legend should contain.
+	 */
+	public function test_get_chart_legend_formats_counts_with_locale_separator( $counts, $expected ) {
+		global $wp_locale;
+
+		$this->original_thousands_sep              = $wp_locale->number_format['thousands_sep'];
+		$wp_locale->number_format['thousands_sep'] = '.';
+
+		$this->report_counts = $counts;
+		add_filter( 'woocommerce_admin_report_data', array( $this, 'set_report_counts' ) );
+
+		$report                 = new WC_Report_Sales_By_Date();
+		$report->chart_colours  = array_fill_keys(
+			array( 'sales_amount', 'average', 'net_sales_amount', 'net_average', 'order_count', 'item_count', 'refund_amount', 'shipping_amount', 'coupon_amount' ),
+			'#000000'
+		);
+		$report->start_date     = strtotime( '-1 month' );
+		$report->end_date       = time();
+		$report->chart_groupby  = 'day';
+		$report->group_by_query = 'YEAR(posts.post_date), MONTH(posts.post_date), DAY(posts.post_date)';
+
+		$legend = implode( ' ', wp_list_pluck( $report->get_chart_legend(), 'title' ) );
+
+		foreach ( $expected as $needle ) {
+			$this->assertStringContainsString( $needle, $legend, 'Legend counts should use the locale thousands separator.' );
+		}
+	}
+
+	/**
+	 * Counts to render the legend with, and the strings they should produce.
+	 *
+	 * The refund line is a _n() string, so it needs a case for each form.
+	 *
+	 * @return array[]
+	 */
+	public function provide_legend_counts() {
+		return array(
+			'plural refunds'  => array(
+				array(
+					'total_orders'          => 12345,
+					'total_items'           => 67890,
+					'total_refunded_orders' => 1234,
+					'refunded_order_items'  => 5678,
+				),
+				array(
+					'<strong>12.345</strong> orders placed',
+					'<strong>67.890</strong> items purchased',
+					'refunded 1.234 orders (5.678 items)',
+				),
+			),
+			'singular refund' => array(
+				array(
+					'total_orders'          => 1000,
+					'total_items'           => 2000,
+					'total_refunded_orders' => 1,
+					'refunded_order_items'  => 1500,
+				),
+				array(
+					'<strong>1.000</strong> orders placed',
+					'<strong>2.000</strong> items purchased',
+					'refunded 1 order (1.500 item)',
+				),
+			),
+		);
+	}
+
+	/**
+	 * Give the report the counts the running test needs.
+	 *
+	 * @param stdClass $data Report data.
+	 * @return stdClass
+	 */
+	public function set_report_counts( $data ) {
+		foreach ( $this->report_counts as $key => $value ) {
+			$data->$key = $value;
+		}
+
+		return $data;
+	}
 }