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.