Repository: gpbl/react-day-picker
Pull request #2348 (2024-08-04 23:24:07)

feat: allow partial locales, export `defaultLocale`


Patch:
diff --git a/src/DayPicker.tsx b/src/DayPicker.tsx
index 70e66c2f3..6b6a14743 100644
--- a/src/DayPicker.tsx
+++ b/src/DayPicker.tsx
@@ -20,6 +20,7 @@ import { getWeekdays } from "./helpers/getWeekdays.js";
 import { getYearOptions } from "./helpers/getYearOptions.js";
 import * as defaultLabels from "./labels/index.js";
 import type { FormatOptions, LabelOptions } from "./lib/dateLib.js";
+import { enUS } from "./lib/locales.js";
 import type {
   DayPickerProps,
   Modifiers,
@@ -43,27 +44,29 @@ import { isDateRange } from "./utils/typeguards.js";
  * @see https://daypicker.dev
  */
 export function DayPicker(props: DayPickerProps) {
-  const { components, formatters, labels, dateLib, classNames } = useMemo(
-    () => ({
-      dateLib: getDateLib(props.dateLib),
-      components: getComponents(props.components),
-      formatters: getFormatters(props.formatters),
-      labels: { ...defaultLabels, ...props.labels },
-      classNames: { ...getDefaultClassNames(), ...props.classNames }
-    }),
-    [
-      props.classNames,
-      props.components,
-      props.dateLib,
-      props.formatters,
-      props.labels
-    ]
-  );
+  const { components, formatters, labels, dateLib, locale, classNames } =
+    useMemo(
+      () => ({
+        dateLib: getDateLib(props.dateLib),
+        components: getComponents(props.components),
+        formatters: getFormatters(props.formatters),
+        labels: { ...defaultLabels, ...props.labels },
+        locale: { ...enUS, ...props.locale },
+        classNames: { ...getDefaultClassNames(), ...props.classNames }
+      }),
+      [
+        props.classNames,
+        props.components,
+        props.dateLib,
+        props.formatters,
+        props.labels,
+        props.locale
+      ]
+    );
 
   const {
     captionLayout,
     firstWeekContainsDate,
-    locale,
     mode,
     onDayBlur,
     onDayClick,
diff --git a/src/index.ts b/src/index.ts
index c3b80e64c..35598d546 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -13,3 +13,5 @@ export * from "./utils/index.js";
 export * from "./UI.js";
 
 export * from "./useDayPicker.js";
+
+export { enUS as defaultLocale } from "./lib/locales.js";
diff --git a/src/types/props.ts b/src/types/props.ts
index aa314a0fd..14a42eaf2 100644
--- a/src/types/props.ts
+++ b/src/types/props.ts
@@ -37,8 +37,11 @@ export type DayPickerProps = PropsBase &
   );
 
 /**
- * Props used for customization of the calendar, localization, and event
- * handling.
+ * Props for customizing the calendar, handling localization, and managing
+ * events. These exclude the selection mode props.
+ *
+ * @group DayPicker
+ * @see https://daypicker.dev/api/interfaces/PropsBase
  */
 export interface PropsBase {
   /**
@@ -335,12 +338,17 @@ export interface PropsBase {
   /** Add the language tag to the container element. */
   lang?: HTMLDivElement["lang"];
   /**
-   * The date-fns locale object used to localize dates.
+   * The locale object used to localize dates. Pass a locale from `date-fns` to
+   * localize the calendar.
+   *
+   * @example
+   *   import { es } from "date-fns/locale";
+   *   <DayPicker locale={es} />
    *
-   * @defaultValue en-US
+   * @defaultValue enUS - The English locale default of `date-fns`.
    * @see https://daypicker.dev/docs/localization
    */
-  locale?: Locale | undefined;
+  locale?: Partial<Locale> | undefined;
   /**
    * The index of the first day of the week (0 - Sunday). Overrides the locale's
    * one.
diff --git a/src/useDayPicker.ts b/src/useDayPicker.ts
index db6d75dcd..2cce86743 100644
--- a/src/useDayPicker.ts
+++ b/src/useDayPicker.ts
@@ -6,7 +6,7 @@ import type { DayPickerProps } from "./types/props.js";
 import type { SelectedValue, SelectHandler } from "./types/selection.js";
 import { Modifiers } from "./types/shared.js";
 
-// Create a context with a default value
+/** @private */
 export const dayPickerContext = createContext<
   DayPickerContext<DayPickerProps> | undefined
 >(undefined);
diff --git a/website/docs/docs/localization.mdx b/website/docs/docs/localization.mdx
index 6e502ca7a..01ecac05b 100644
--- a/website/docs/docs/localization.mdx
+++ b/website/docs/docs/localization.mdx
@@ -41,6 +41,27 @@ import { es } from "date-fns/locale";
   <Examples.Spanish />
 </BrowserWindow>
 
+:::tip Customizing the Locale
+
+You can customize the locale by passing an object with the desired translations. Use the `defaultLocale` object for the default translations.
+
+```tsx
+import { DayPicker, defaultLocale } from "react-day-picker";
+
+<DayPicker
+  locale={{
+    localize: {
+      ...defaultLocale.localize,
+      day: (day) => "custom-localized-day"
+    }
+  }}
+  month={new Date(2023, 0, 1)}
+  mode="single"
+/>;
+```
+
+:::
+
 ## First Date of the Week
 
 Use the `weekStartsOn` prop to specify the starting day of the week.


Test patch:
diff --git a/src/DayPicker.test.tsx b/src/DayPicker.test.tsx
index a6b6863f7..513170f98 100644
--- a/src/DayPicker.test.tsx
+++ b/src/DayPicker.test.tsx
@@ -1,6 +1,7 @@
 import React from "react";
 
 import { addMonths, startOfDay, startOfMonth } from "date-fns";
+import { defaultLocale } from "react-day-picker";
 
 import {
   activeElement,
@@ -158,3 +159,19 @@ describe("when the `startMonth` is changed programmatically", () => {
     expect(grid(labelGrid(newStartMonth))).toBeInTheDocument();
   });
 });
+
+test("extends the default locale", () => {
+  render(
+    <DayPicker
+      month={new Date(2024, 0)}
+      locale={{
+        localize: {
+          ...defaultLocale.localize,
+          month: () => "bar"
+        }
+      }}
+    />
+  );
+  // Check if the custom month name is rendered
+  expect(grid("bar 2024")).toBeInTheDocument();
+});