Commit dd6636bcf5f for woocommerce

commit dd6636bcf5ff188cb86dfc76daab38d181e8396c
Author: Miroslav Mitev <m1r0@users.noreply.github.com>
Date:   Tue Sep 22 16:48:36 2026 +0300

    Fix Analytics calendar showing English weekday headers (#68516)

diff --git a/packages/js/date/changelog/fix-analytics-calendar-weekday-headers b/packages/js/date/changelog/fix-analytics-calendar-weekday-headers
new file mode 100644
index 00000000000..dc7508ac0e0
--- /dev/null
+++ b/packages/js/date/changelog/fix-analytics-calendar-weekday-headers
@@ -0,0 +1,4 @@
+Significance: patch
+Type: fix
+
+Fill moment's minimal weekday names in from the translated short ones so calendar week headers are not English on translated sites.
diff --git a/packages/js/date/src/index.ts b/packages/js/date/src/index.ts
index a4890864ad2..0c4581aad1a 100644
--- a/packages/js/date/src/index.ts
+++ b/packages/js/date/src/index.ts
@@ -464,6 +464,60 @@ function ensureMomentStartOfWeek() {

 ensureMomentStartOfWeek();

+/**
+ * Compares two lists of weekday names.
+ *
+ * @param {Array} names      - Names to compare.
+ * @param {Array} otherNames - Names to compare against.
+ * @return {boolean} - Whether both lists hold the same names in the same order.
+ */
+function isSameNames( names: string[], otherNames: string[] ) {
+	return (
+		names.length === otherNames.length &&
+		names.every( ( name, index ) => name === otherNames[ index ] )
+	);
+}
+
+/**
+ * Fills the moment locale's minimal weekday names in from the translated short
+ * ones. WordPress sets the locale up with translated `weekdays` and
+ * `weekdaysShort` but never `weekdaysMin`, so moment keeps its English fallback
+ * for that one. react-dates builds the calendar week header from `weekdaysMin`,
+ * which is why the date picker headers stay English on a translated site.
+ *
+ * The names are read back off moment so they always belong to the locale that
+ * is actually active.
+ */
+function ensureMomentWeekdayNames() {
+	const localeData = moment.localeData();
+	const weekdaysMin = localeData.weekdaysMin();
+	const weekdaysShort = localeData.weekdaysShort();
+
+	// A locale can hold its weekday names in shapes other than a plain list.
+	if ( ! Array.isArray( weekdaysMin ) || ! Array.isArray( weekdaysShort ) ) {
+		return;
+	}
+
+	const englishLocaleData = moment.localeData( 'en' );
+
+	// Anything that already replaced the English fallback is a better source
+	// than the short names, so leave it alone.
+	if ( ! isSameNames( weekdaysMin, englishLocaleData.weekdaysMin() ) ) {
+		return;
+	}
+
+	// The English fallback is already right for English locales.
+	if ( isSameNames( weekdaysShort, englishLocaleData.weekdaysShort() ) ) {
+		return;
+	}
+
+	moment.updateLocale( moment.locale(), {
+		weekdaysMin: [ ...weekdaysShort ],
+	} );
+}
+
+ensureMomentWeekdayNames();
+
 /**
  * Get a DateValue object for a period prior to the current period.
  *
diff --git a/packages/js/date/src/test/index.ts b/packages/js/date/src/test/index.ts
index 1de3cd60128..31afd4c4dd2 100644
--- a/packages/js/date/src/test/index.ts
+++ b/packages/js/date/src/test/index.ts
@@ -795,6 +795,112 @@ describe( 'start of week setting', () => {
 	} );
 } );

+describe( 'weekday names', () => {
+	const HEBREW_WEEKDAYS_SHORT = [ 'א', 'ב', 'ג', 'ד', 'ה', 'ו', 'ש' ];
+	const ENGLISH_WEEKDAYS_SHORT = [
+		'Sun',
+		'Mon',
+		'Tue',
+		'Wed',
+		'Thu',
+		'Fri',
+		'Sat',
+	];
+	const ENGLISH_WEEKDAYS_MIN = [ 'Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa' ];
+	const GERMAN_WEEKDAYS_SHORT = [
+		'So.',
+		'Mo.',
+		'Di.',
+		'Mi.',
+		'Do.',
+		'Fr.',
+		'Sa.',
+	];
+	const GERMAN_WEEKDAYS_MIN = [ 'So', 'Mo', 'Di', 'Mi', 'Do', 'Fr', 'Sa' ];
+	const RUSSIAN_WEEKDAYS_SHORT = [ 'вс', 'пн', 'вт', 'ср', 'чт', 'пт', 'сб' ];
+
+	/**
+	 * Defines a locale and loads the package against it, on a moment of their
+	 * own so the surrounding tests keep theirs. Without a `weekdaysMin` this is
+	 * the shape WordPress leaves behind, and moment falls back to the English
+	 * names.
+	 *
+	 * @param {Object} config - Locale configuration.
+	 * @return {Object} - The locale data moment holds once the package loaded.
+	 */
+	function loadWithLocale( config: moment.LocaleSpecification ) {
+		let localeData = moment.localeData();
+
+		jest.isolateModules( () => {
+			/* eslint-disable @typescript-eslint/no-require-imports -- isolateModules only tracks synchronous requires. */
+			const momentLib = require( 'moment' );
+
+			momentLib.defineLocale( 'test_locale', config );
+			require( '../index' );
+			/* eslint-enable @typescript-eslint/no-require-imports */
+
+			localeData = momentLib.localeData();
+		} );
+
+		return localeData;
+	}
+
+	it( 'should fill in the missing weekdaysMin from the translated short names', () => {
+		const localeData = loadWithLocale( {
+			weekdaysShort: HEBREW_WEEKDAYS_SHORT,
+		} );
+
+		expect( localeData.weekdaysMin() ).toEqual( HEBREW_WEEKDAYS_SHORT );
+	} );
+
+	it( 'should keep the rest of the locale data untouched', () => {
+		const localeData = loadWithLocale( {
+			weekdaysShort: HEBREW_WEEKDAYS_SHORT,
+			longDateFormat: {
+				L: 'DD/MM/YYYY',
+				LL: 'D [ב]MMMM YYYY',
+				LLL: 'D [ב]MMMM YYYY HH:mm',
+				LLLL: 'dddd, D [ב]MMMM YYYY HH:mm',
+				LT: 'HH:mm',
+				LTS: 'HH:mm:ss',
+			},
+		} );
+
+		expect( localeData.weekdaysMin() ).toEqual( HEBREW_WEEKDAYS_SHORT );
+		expect( localeData.longDateFormat( 'L' ) ).toBe( 'DD/MM/YYYY' );
+		expect( localeData.longDateFormat( 'LL' ) ).toBe( 'D [ב]MMMM YYYY' );
+		expect( localeData.weekdaysShort() ).toEqual( HEBREW_WEEKDAYS_SHORT );
+	} );
+
+	it( 'should leave an English locale on the moment fallback', () => {
+		const localeData = loadWithLocale( {
+			weekdaysShort: ENGLISH_WEEKDAYS_SHORT,
+		} );
+
+		expect( localeData.weekdaysMin() ).toEqual( ENGLISH_WEEKDAYS_MIN );
+	} );
+
+	it( 'should keep the weekdaysMin something else already supplied', () => {
+		const localeData = loadWithLocale( {
+			weekdaysShort: GERMAN_WEEKDAYS_SHORT,
+			weekdaysMin: GERMAN_WEEKDAYS_MIN,
+		} );
+
+		expect( localeData.weekdaysMin() ).toEqual( GERMAN_WEEKDAYS_MIN );
+	} );
+
+	it( 'should leave a locale that holds its short names in another shape alone', () => {
+		const localeData = loadWithLocale( {
+			weekdaysShort: {
+				format: RUSSIAN_WEEKDAYS_SHORT,
+				standalone: RUSSIAN_WEEKDAYS_SHORT,
+			} as unknown as string[],
+		} );
+
+		expect( localeData.weekdaysMin() ).toEqual( ENGLISH_WEEKDAYS_MIN );
+	} );
+} );
+
 describe( 'getRangeLabel', () => {
 	it( 'should return correct string for dates on the same day', () => {
 		const label = getRangeLabel(
diff --git a/plugins/woocommerce/changelog/fix-analytics-calendar-weekday-headers b/plugins/woocommerce/changelog/fix-analytics-calendar-weekday-headers
new file mode 100644
index 00000000000..69e31fa2348
--- /dev/null
+++ b/plugins/woocommerce/changelog/fix-analytics-calendar-weekday-headers
@@ -0,0 +1,4 @@
+Significance: patch
+Type: fix
+
+Fix the Analytics date range calendar showing English weekday headers on translated sites.