@wordpress/components
Version:
UI components for WordPress.
170 lines (166 loc) • 4.81 kB
text/typescript
/**
* WordPress dependencies
*/
import { __, sprintf } from '@wordpress/i18n';
import { useMemo } from '@wordpress/element';
/**
* Internal dependencies
*/
import type { Modifiers, BaseProps } from '../types';
function isLocaleRTL( localeCode: string ) {
const localeObj = new Intl.Locale( localeCode );
if ( 'getTextInfo' in localeObj ) {
// @ts-expect-error - getTextInfo is not typed yet
// see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Locale/getTextInfo
return localeObj.getTextInfo().direction === 'rtl';
}
return [
'ar', // Arabic
'he', // Hebrew
'fa', // Persian (Farsi)
'ur', // Urdu
'ps', // Pashto
'syr', // Syriac
'dv', // Divehi
'ku', // Kurdish (Sorani)
'yi', // Yiddish
].includes( localeObj.language );
}
/**
* Returns localization props for the calendar components.
*
* Notes:
* - the following props should be intended as defaults, and should
* be overridden by consumer props if listed as public props.
* - It is possible for the translated strings to use a different locale
* than the formatted dates and the computed `dir`. This is because the
* translation function doesn't expose the locale used for the translated
* strings, meaning that the dates are formatted using the `locale` prop.
* For a correct localized experience, consumers should make sure that
* translation context and `locale` prop are consistent.
* @param props
* @param props.locale
* @param props.timeZone
* @param props.mode
*/
export const useLocalizationProps = ( {
locale,
timeZone,
mode,
}: {
locale: NonNullable< BaseProps[ 'locale' ] >;
timeZone: BaseProps[ 'timeZone' ];
mode: 'single' | 'range';
} ) => {
return useMemo( () => {
// ie. April 2025
const monthNameFormatter = new Intl.DateTimeFormat( locale.code, {
year: 'numeric',
month: 'long',
timeZone,
} );
// ie. M, T, W, T, F, S, S
const weekdayNarrowFormatter = new Intl.DateTimeFormat( locale.code, {
weekday: 'narrow',
timeZone,
} );
// ie. Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday
const weekdayLongFormatter = new Intl.DateTimeFormat( locale.code, {
weekday: 'long',
timeZone,
} );
// ie. Monday, April 29, 2025
const fullDateFormatter = new Intl.DateTimeFormat( locale.code, {
weekday: 'long',
year: 'numeric',
month: 'long',
day: 'numeric',
timeZone,
} );
// Note: the following props should be intended as defaults, and should
// be overridden by consumer props if listed as public props.
return {
'aria-label':
mode === 'single'
? __( 'Date calendar' )
: __( 'Date range calendar' ),
labels: {
/**
* The label for the month grid.
* @param date
*/
labelGrid: ( date: Date ) => monthNameFormatter.format( date ),
/**
* The label for the gridcell, when the calendar is not interactive.
* @param date
* @param modifiers
*/
labelGridcell: (
date: Date,
/** The modifiers for the day. */
modifiers?: Modifiers
) => {
const formattedDate = fullDateFormatter.format( date );
let label = formattedDate;
if ( modifiers?.today ) {
label = sprintf(
// translators: %s is the full date (e.g. "Monday, April 29, 2025")
__( 'Today, %s' ),
formattedDate
);
}
return label;
},
/** The label for the "next month" button. */
labelNext: () => __( 'Go to the Next Month' ),
/** The label for the "previous month" button. */
labelPrevious: () => __( 'Go to the Previous Month' ),
/**
* The label for the day button.
* @param date
* @param modifiers
*/
labelDayButton: (
date: Date,
/** The modifiers for the day. */
modifiers?: Modifiers
) => {
const formattedDate = fullDateFormatter.format( date );
let label = formattedDate;
if ( modifiers?.today ) {
label = sprintf(
// translators: %s is the full date (e.g. "Monday, April 29, 2025")
__( 'Today, %s' ),
formattedDate
);
}
if ( modifiers?.selected ) {
label = sprintf(
// translators: %s is the full date (e.g. "Monday, April 29, 2025")
__( '%s, selected' ),
formattedDate
);
}
return label;
},
/**
* The label for the weekday.
* @param date
*/
labelWeekday: ( date: Date ) =>
weekdayLongFormatter.format( date ),
},
locale,
dir: isLocaleRTL( locale.code ) ? 'rtl' : 'ltr',
formatters: {
formatWeekdayName: ( date: Date ) => {
return weekdayNarrowFormatter.format( date );
},
formatCaption: ( date: Date ) => {
return monthNameFormatter.format( date );
},
},
timeZone,
} as const;
}, [ locale, timeZone, mode ] );
};