hypeserver / react-date-range

A React component for choosing dates and date ranges.
MIT License
2.59k stars 679 forks source link

Example : How to internationalize the staticRanges #536

Open EliasTouil opened 2 years ago

EliasTouil commented 2 years ago

Subject of the issue

I notices there is no option to translate the labels of the default static ranges. This issue is meant to be used as wiki, it can be closed immediately.

Screen Shot 2021-11-27 at 4 24 24 PM

French translation

Default static ranges as React hook with react-intl

// useDefautlStaticRanges.tsx
// It's a rewrite of the provided default static ranges file

import {
  addDays,
  addMonths,
  endOfDay,
  endOfMonth,
  endOfWeek,
  isSameDay,
  startOfDay,
  startOfMonth,
  startOfWeek
} from 'date-fns';
import { Range, StaticRange } from 'react-date-range';
import { useIntl } from 'react-intl';

const useDefaultStaticRanges: (selectedRange: Range) => StaticRange[] = (
  selectedRange
) => {
  const selectableDateRangesLimits: Record<string, Date> = {
    startOfWeek: startOfWeek(new Date()),
    endOfWeek: endOfWeek(new Date()),
    startOfLastWeek: startOfWeek(addDays(new Date(), -7)),
    endOfLastWeek: endOfWeek(addDays(new Date(), -7)),
    startOfToday: startOfDay(new Date()),
    endOfToday: endOfDay(new Date()),
    startOfYesterday: startOfDay(addDays(new Date(), -1)),
    endOfYesterday: endOfDay(addDays(new Date(), -1)),
    startOfMonth: startOfMonth(new Date()),
    endOfMonth: endOfMonth(new Date()),
    startOfLastMonth: startOfMonth(addMonths(new Date(), -1)),
    endOfLastMonth: endOfMonth(addMonths(new Date(), -1))
  };

  const intl = useIntl();

  const selectableRangesWithoutIsSelected: Omit<StaticRange, 'isSelected'>[] = [
    {
      label: intl.formatMessage({ id: 'Today' }),
      range: () => ({
        startDate: selectableDateRangesLimits.startOfToday,
        endDate: selectableDateRangesLimits.endOfToday
      })
    },
    {
      label: intl.formatMessage({ id: 'Yesterday' }),
      range: () => ({
        startDate: selectableDateRangesLimits.startOfYesterday,
        endDate: selectableDateRangesLimits.endOfYesterday
      })
    },

    {
      label: intl.formatMessage({ id: 'This Week' }),
      range: () => ({
        startDate: selectableDateRangesLimits.startOfWeek,
        endDate: selectableDateRangesLimits.endOfWeek
      })
    },
    {
      label: intl.formatMessage({ id: 'Last Week' }),
      range: () => ({
        startDate: selectableDateRangesLimits.startOfLastWeek,
        endDate: selectableDateRangesLimits.endOfLastWeek
      })
    },
    {
      label: intl.formatMessage({ id: 'This Month' }),
      range: () => ({
        startDate: selectableDateRangesLimits.startOfMonth,
        endDate: selectableDateRangesLimits.endOfMonth
      })
    },
    {
      label: intl.formatMessage({ id: 'Last Month' }),
      range: () => ({
        startDate: selectableDateRangesLimits.startOfLastMonth,
        endDate: selectableDateRangesLimits.endOfLastMonth
      })
    }
  ];

  const isSelected = (staticRange: Omit<StaticRange, 'isSelected'>) =>
    selectedRange.startDate &&
    staticRange?.range()?.startDate &&
    selectedRange.endDate &&
    staticRange?.range()?.endDate &&
    isSameDay(selectedRange.startDate, staticRange.range().startDate as Date) &&
    isSameDay(selectedRange.endDate, staticRange.range().endDate as Date);

  const addIsSelectedToSelectableRanges: (
    ranges: Omit<StaticRange, 'isSelected'>[]
  ) => StaticRange[] = (ranges) => {
    return ranges.map(
      (range) =>
        ({
          isSelected: () => isSelected(range),
          ...range
        } as StaticRange)
    );
  };

  return addIsSelectedToSelectableRanges(selectableRangesWithoutIsSelected);
};

export default useDefaultStaticRanges;

How to use is

const TimeRangePicker: React.FC<{ onRangeChange(): void }> = ({
  onRangeChange
}) => {
  const { startDate, endDate, setStartDate, setEndDate } =
    useContext(TimeRangeContext);
  const router = useRouter();
  const handleRangeChange = (rangesByKey: RangeKeyDict) => {
    rangesByKey.range.startDate && setStartDate!(rangesByKey.range.startDate);
    rangesByKey.range.endDate && setEndDate!(rangesByKey.range.endDate);
    onRangeChange();
  };

  // use the hook here, providing your stateful startDate and endDate
  const staticRanges = useDefaultStaticRanges({ startDate, endDate });

  return (
    <Flex {...styles.container} {...styles.datePickerOverwrites}>
      <DateRangePicker
        ranges={[
          {
            startDate,
            endDate,
            key: 'range',
            color: chakraTheme.colors.primary[500]
          }
        ]}
        locale={router.locale === 'fr' ? fr : enCA}
        onChange={handleRangeChange}
        inputRanges={[]}
        staticRanges={staticRanges} // pass the ranges here
      />
    </Flex>
  );
};

export default TimeRangePicker;
mumtaz-dedi commented 3 weeks ago

it means that I must install date-fns and use locale from there to change react-date-range locale?