@oxy.so/bloom 4.31.0 → 4.33.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (190) hide show
  1. package/docs/adoption-matrix.mdx +4 -3
  2. package/docs/date-picker.mdx +24 -0
  3. package/docs/locale.mdx +46 -0
  4. package/docs/provider.mdx +11 -0
  5. package/lib/commonjs/calendar/shared.js +5 -11
  6. package/lib/commonjs/calendar/shared.js.map +1 -1
  7. package/lib/commonjs/date-picker/Calendar.js +12 -1
  8. package/lib/commonjs/date-picker/Calendar.js.map +1 -1
  9. package/lib/commonjs/date-picker/CalendarMonth.js +4 -2
  10. package/lib/commonjs/date-picker/CalendarMonth.js.map +1 -1
  11. package/lib/commonjs/date-picker/DatePicker.js +15 -3
  12. package/lib/commonjs/date-picker/DatePicker.js.map +1 -1
  13. package/lib/commonjs/date-picker/DateRangePicker.js +21 -9
  14. package/lib/commonjs/date-picker/DateRangePicker.js.map +1 -1
  15. package/lib/commonjs/date-picker/MeetingScheduler.js +19 -13
  16. package/lib/commonjs/date-picker/MeetingScheduler.js.map +1 -1
  17. package/lib/commonjs/date-picker/PickerShell.js +4 -2
  18. package/lib/commonjs/date-picker/PickerShell.js.map +1 -1
  19. package/lib/commonjs/date-picker/calendar-grid.js +15 -21
  20. package/lib/commonjs/date-picker/calendar-grid.js.map +1 -1
  21. package/lib/commonjs/date-picker/messages.js +526 -0
  22. package/lib/commonjs/date-picker/messages.js.map +1 -0
  23. package/lib/commonjs/index.js +1063 -1034
  24. package/lib/commonjs/index.js.map +1 -1
  25. package/lib/commonjs/index.web.js +1063 -1034
  26. package/lib/commonjs/index.web.js.map +1 -1
  27. package/lib/commonjs/locale/context.js +35 -0
  28. package/lib/commonjs/locale/context.js.map +1 -0
  29. package/lib/commonjs/locale/format-date.js +34 -0
  30. package/lib/commonjs/locale/format-date.js.map +1 -0
  31. package/lib/commonjs/locale/index.js +32 -0
  32. package/lib/commonjs/locale/index.js.map +1 -0
  33. package/lib/commonjs/locale/languages.js +35 -0
  34. package/lib/commonjs/locale/languages.js.map +1 -0
  35. package/lib/commonjs/locale/messages.js +34 -0
  36. package/lib/commonjs/locale/messages.js.map +1 -0
  37. package/lib/commonjs/locale/plural.js +53 -0
  38. package/lib/commonjs/locale/plural.js.map +1 -0
  39. package/lib/commonjs/provider/BloomProvider.js +18 -13
  40. package/lib/commonjs/provider/BloomProvider.js.map +1 -1
  41. package/lib/module/calendar/shared.js +5 -11
  42. package/lib/module/calendar/shared.js.map +1 -1
  43. package/lib/module/date-picker/Calendar.js +12 -1
  44. package/lib/module/date-picker/Calendar.js.map +1 -1
  45. package/lib/module/date-picker/CalendarMonth.js +4 -2
  46. package/lib/module/date-picker/CalendarMonth.js.map +1 -1
  47. package/lib/module/date-picker/DatePicker.js +15 -3
  48. package/lib/module/date-picker/DatePicker.js.map +1 -1
  49. package/lib/module/date-picker/DateRangePicker.js +21 -9
  50. package/lib/module/date-picker/DateRangePicker.js.map +1 -1
  51. package/lib/module/date-picker/MeetingScheduler.js +19 -13
  52. package/lib/module/date-picker/MeetingScheduler.js.map +1 -1
  53. package/lib/module/date-picker/PickerShell.js +4 -2
  54. package/lib/module/date-picker/PickerShell.js.map +1 -1
  55. package/lib/module/date-picker/calendar-grid.js +15 -21
  56. package/lib/module/date-picker/calendar-grid.js.map +1 -1
  57. package/lib/module/date-picker/messages.js +523 -0
  58. package/lib/module/date-picker/messages.js.map +1 -0
  59. package/lib/module/index.js +1 -0
  60. package/lib/module/index.js.map +1 -1
  61. package/lib/module/index.web.js +1 -0
  62. package/lib/module/index.web.js.map +1 -1
  63. package/lib/module/locale/context.js +30 -0
  64. package/lib/module/locale/context.js.map +1 -0
  65. package/lib/module/locale/format-date.js +30 -0
  66. package/lib/module/locale/format-date.js.map +1 -0
  67. package/lib/module/locale/index.js +7 -0
  68. package/lib/module/locale/index.js.map +1 -0
  69. package/lib/module/locale/languages.js +29 -0
  70. package/lib/module/locale/languages.js.map +1 -0
  71. package/lib/module/locale/messages.js +30 -0
  72. package/lib/module/locale/messages.js.map +1 -0
  73. package/lib/module/locale/plural.js +48 -0
  74. package/lib/module/locale/plural.js.map +1 -0
  75. package/lib/module/provider/BloomProvider.js +16 -11
  76. package/lib/module/provider/BloomProvider.js.map +1 -1
  77. package/lib/typescript/commonjs/calendar/shared.d.ts.map +1 -1
  78. package/lib/typescript/commonjs/date-picker/Calendar.d.ts.map +1 -1
  79. package/lib/typescript/commonjs/date-picker/CalendarMonth.d.ts +4 -1
  80. package/lib/typescript/commonjs/date-picker/CalendarMonth.d.ts.map +1 -1
  81. package/lib/typescript/commonjs/date-picker/DatePicker.d.ts +1 -1
  82. package/lib/typescript/commonjs/date-picker/DatePicker.d.ts.map +1 -1
  83. package/lib/typescript/commonjs/date-picker/DateRangePicker.d.ts +1 -1
  84. package/lib/typescript/commonjs/date-picker/DateRangePicker.d.ts.map +1 -1
  85. package/lib/typescript/commonjs/date-picker/MeetingScheduler.d.ts +1 -1
  86. package/lib/typescript/commonjs/date-picker/MeetingScheduler.d.ts.map +1 -1
  87. package/lib/typescript/commonjs/date-picker/PickerShell.d.ts +3 -1
  88. package/lib/typescript/commonjs/date-picker/PickerShell.d.ts.map +1 -1
  89. package/lib/typescript/commonjs/date-picker/calendar-grid.d.ts +3 -3
  90. package/lib/typescript/commonjs/date-picker/calendar-grid.d.ts.map +1 -1
  91. package/lib/typescript/commonjs/date-picker/index.d.ts +1 -1
  92. package/lib/typescript/commonjs/date-picker/index.d.ts.map +1 -1
  93. package/lib/typescript/commonjs/date-picker/messages.d.ts +31 -0
  94. package/lib/typescript/commonjs/date-picker/messages.d.ts.map +1 -0
  95. package/lib/typescript/commonjs/date-picker/types.d.ts +54 -12
  96. package/lib/typescript/commonjs/date-picker/types.d.ts.map +1 -1
  97. package/lib/typescript/commonjs/index.d.ts +2 -0
  98. package/lib/typescript/commonjs/index.d.ts.map +1 -1
  99. package/lib/typescript/commonjs/index.web.d.ts +2 -0
  100. package/lib/typescript/commonjs/index.web.d.ts.map +1 -1
  101. package/lib/typescript/commonjs/locale/context.d.ts +18 -0
  102. package/lib/typescript/commonjs/locale/context.d.ts.map +1 -0
  103. package/lib/typescript/commonjs/locale/format-date.d.ts +13 -0
  104. package/lib/typescript/commonjs/locale/format-date.d.ts.map +1 -0
  105. package/lib/typescript/commonjs/locale/index.d.ts +3 -0
  106. package/lib/typescript/commonjs/locale/index.d.ts.map +1 -0
  107. package/lib/typescript/commonjs/locale/languages.d.ts +16 -0
  108. package/lib/typescript/commonjs/locale/languages.d.ts.map +1 -0
  109. package/lib/typescript/commonjs/locale/messages.d.ts +18 -0
  110. package/lib/typescript/commonjs/locale/messages.d.ts.map +1 -0
  111. package/lib/typescript/commonjs/locale/plural.d.ts +19 -0
  112. package/lib/typescript/commonjs/locale/plural.d.ts.map +1 -0
  113. package/lib/typescript/commonjs/provider/BloomProvider.d.ts +1 -1
  114. package/lib/typescript/commonjs/provider/BloomProvider.d.ts.map +1 -1
  115. package/lib/typescript/commonjs/provider/types.d.ts +6 -0
  116. package/lib/typescript/commonjs/provider/types.d.ts.map +1 -1
  117. package/lib/typescript/module/calendar/shared.d.ts.map +1 -1
  118. package/lib/typescript/module/date-picker/Calendar.d.ts.map +1 -1
  119. package/lib/typescript/module/date-picker/CalendarMonth.d.ts +4 -1
  120. package/lib/typescript/module/date-picker/CalendarMonth.d.ts.map +1 -1
  121. package/lib/typescript/module/date-picker/DatePicker.d.ts +1 -1
  122. package/lib/typescript/module/date-picker/DatePicker.d.ts.map +1 -1
  123. package/lib/typescript/module/date-picker/DateRangePicker.d.ts +1 -1
  124. package/lib/typescript/module/date-picker/DateRangePicker.d.ts.map +1 -1
  125. package/lib/typescript/module/date-picker/MeetingScheduler.d.ts +1 -1
  126. package/lib/typescript/module/date-picker/MeetingScheduler.d.ts.map +1 -1
  127. package/lib/typescript/module/date-picker/PickerShell.d.ts +3 -1
  128. package/lib/typescript/module/date-picker/PickerShell.d.ts.map +1 -1
  129. package/lib/typescript/module/date-picker/calendar-grid.d.ts +3 -3
  130. package/lib/typescript/module/date-picker/calendar-grid.d.ts.map +1 -1
  131. package/lib/typescript/module/date-picker/index.d.ts +1 -1
  132. package/lib/typescript/module/date-picker/index.d.ts.map +1 -1
  133. package/lib/typescript/module/date-picker/messages.d.ts +31 -0
  134. package/lib/typescript/module/date-picker/messages.d.ts.map +1 -0
  135. package/lib/typescript/module/date-picker/types.d.ts +54 -12
  136. package/lib/typescript/module/date-picker/types.d.ts.map +1 -1
  137. package/lib/typescript/module/index.d.ts +2 -0
  138. package/lib/typescript/module/index.d.ts.map +1 -1
  139. package/lib/typescript/module/index.web.d.ts +2 -0
  140. package/lib/typescript/module/index.web.d.ts.map +1 -1
  141. package/lib/typescript/module/locale/context.d.ts +18 -0
  142. package/lib/typescript/module/locale/context.d.ts.map +1 -0
  143. package/lib/typescript/module/locale/format-date.d.ts +13 -0
  144. package/lib/typescript/module/locale/format-date.d.ts.map +1 -0
  145. package/lib/typescript/module/locale/index.d.ts +3 -0
  146. package/lib/typescript/module/locale/index.d.ts.map +1 -0
  147. package/lib/typescript/module/locale/languages.d.ts +16 -0
  148. package/lib/typescript/module/locale/languages.d.ts.map +1 -0
  149. package/lib/typescript/module/locale/messages.d.ts +18 -0
  150. package/lib/typescript/module/locale/messages.d.ts.map +1 -0
  151. package/lib/typescript/module/locale/plural.d.ts +19 -0
  152. package/lib/typescript/module/locale/plural.d.ts.map +1 -0
  153. package/lib/typescript/module/provider/BloomProvider.d.ts +1 -1
  154. package/lib/typescript/module/provider/BloomProvider.d.ts.map +1 -1
  155. package/lib/typescript/module/provider/types.d.ts +6 -0
  156. package/lib/typescript/module/provider/types.d.ts.map +1 -1
  157. package/package.json +18 -1
  158. package/src/calendar/shared.ts +5 -12
  159. package/src/date-picker/Calendar.tsx +17 -2
  160. package/src/date-picker/CalendarMonth.tsx +7 -2
  161. package/src/date-picker/DatePicker.tsx +12 -3
  162. package/src/date-picker/DateRangePicker.tsx +17 -8
  163. package/src/date-picker/MeetingScheduler.tsx +18 -11
  164. package/src/date-picker/PickerShell.tsx +6 -2
  165. package/src/date-picker/calendar-grid.ts +19 -24
  166. package/src/date-picker/index.ts +4 -0
  167. package/src/date-picker/messages.ts +470 -0
  168. package/src/date-picker/types.ts +66 -12
  169. package/src/index.ts +2 -0
  170. package/src/index.web.ts +2 -0
  171. package/src/locale/context.ts +23 -0
  172. package/src/locale/format-date.ts +28 -0
  173. package/src/locale/index.ts +4 -0
  174. package/src/locale/languages.ts +45 -0
  175. package/src/locale/messages.ts +29 -0
  176. package/src/locale/plural.ts +53 -0
  177. package/src/provider/BloomProvider.tsx +39 -35
  178. package/src/provider/types.ts +6 -0
  179. package/src/__tests__/support/adoption-matrix.ts +0 -708
  180. package/src/__tests__/support/collision-fixture-barrel.ts +0 -20
  181. package/src/__tests__/support/commerce-harness.tsx +0 -97
  182. package/src/__tests__/support/composite.ts +0 -68
  183. package/src/__tests__/support/constructed-style-sheets.ts +0 -68
  184. package/src/__tests__/support/press-host.ts +0 -30
  185. package/src/__tests__/support/rendered-style.ts +0 -99
  186. package/src/__tests__/support/unread-hook-fixture.ts +0 -33
  187. package/src/__tests__/support/worklet-capture-fixture.tsx +0 -28
  188. package/src/theme/__tests__/__fixtures__/golden-resolved-tokens.json +0 -9346
  189. package/src/theme/__tests__/__fixtures__/tonal-glass-primary-failures.json +0 -69
  190. package/src/theme/__tests__/fixtures/color-engine-golden.json +0 -1
@@ -14,5 +14,11 @@ export interface BloomProviderProps extends Omit<BloomThemeProviderProps, 'child
14
14
  imageResolver?: ImageResolver;
15
15
  /** `false` disables haptic feedback app-wide (honored by every `useHaptics()` call). */
16
16
  haptics?: boolean;
17
+ /**
18
+ * BCP 47 locale for every Bloom component below — dates, month names and
19
+ * Bloom's own strings (Cancel, Apply, …). A component's own `locale` prop
20
+ * wins. Unset, components follow the runtime's locale.
21
+ */
22
+ locale?: string;
17
23
  }
18
24
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/provider/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AACvC,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAC3D,OAAO,EAAE,KAAK,aAAa,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,EAAE,KAAK,uBAAuB,EAAE,MAAM,UAAU,CAAC;AAExD,MAAM,WAAW,kBAAmB,SAAQ,IAAI,CAAC,uBAAuB,EAAE,UAAU,CAAC;IACnF,QAAQ,EAAE,SAAS,CAAC;IACpB,8EAA8E;IAC9E,aAAa,CAAC,EAAE,mBAAmB,CAAC;IACpC;;;;OAIG;IACH,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,wFAAwF;IACxF,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/provider/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AACvC,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAC3D,OAAO,EAAE,KAAK,aAAa,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,EAAE,KAAK,uBAAuB,EAAE,MAAM,UAAU,CAAC;AAExD,MAAM,WAAW,kBAAmB,SAAQ,IAAI,CAAC,uBAAuB,EAAE,UAAU,CAAC;IACnF,QAAQ,EAAE,SAAS,CAAC;IACpB,8EAA8E;IAC9E,aAAa,CAAC,EAAE,mBAAmB,CAAC;IACpC;;;;OAIG;IACH,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,wFAAwF;IACxF,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oxy.so/bloom",
3
- "version": "4.31.0",
3
+ "version": "4.33.0",
4
4
  "packageManager": "bun@1.3.14",
5
5
  "description": "Bloom UI — Oxy ecosystem component library for React Native + Expo + Web",
6
6
  "main": "lib/commonjs/index.js",
@@ -79,6 +79,20 @@
79
79
  "default": "./lib/commonjs/image-resolver/index.js"
80
80
  }
81
81
  },
82
+ "./locale": {
83
+ "react-native": {
84
+ "types": "./lib/typescript/module/locale/index.d.ts",
85
+ "default": "./src/locale/index.ts"
86
+ },
87
+ "import": {
88
+ "types": "./lib/typescript/module/locale/index.d.ts",
89
+ "default": "./lib/module/locale/index.js"
90
+ },
91
+ "require": {
92
+ "types": "./lib/typescript/commonjs/locale/index.d.ts",
93
+ "default": "./lib/commonjs/locale/index.js"
94
+ }
95
+ },
82
96
  "./image-aspect-ratio-cache": {
83
97
  "react-native": {
84
98
  "types": "./lib/typescript/module/image-aspect-ratio-cache/index.d.ts",
@@ -3452,6 +3466,9 @@
3452
3466
  "image-resolver": [
3453
3467
  "./lib/typescript/module/image-resolver/index.d.ts"
3454
3468
  ],
3469
+ "locale": [
3470
+ "./lib/typescript/module/locale/index.d.ts"
3471
+ ],
3455
3472
  "image-aspect-ratio-cache": [
3456
3473
  "./lib/typescript/module/image-aspect-ratio-cache/index.d.ts"
3457
3474
  ],
@@ -3,6 +3,7 @@ import { Platform, useWindowDimensions } from 'react-native';
3
3
 
4
4
  import { addDays, buildMonthGrid, isSameMonth, startOfDay } from '../date-picker/calendar-grid';
5
5
  import type { CalendarViewEvent } from './types';
6
+ import { formatGregorian } from '../locale/format-date';
6
7
 
7
8
  export const IS_WEB = Platform.OS === 'web';
8
9
 
@@ -95,18 +96,10 @@ export function currentGmtLabel(now: Date = new Date()): string {
95
96
  return `GMT${offset >= 0 ? '+' : ''}${offset}`;
96
97
  }
97
98
 
98
- function format(date: Date, locale: string | undefined, options: Intl.DateTimeFormatOptions): string | null {
99
- try {
100
- return new Intl.DateTimeFormat(locale, options).format(date);
101
- } catch {
102
- return null;
103
- }
104
- }
105
-
106
99
  /** `Tue, Aug 11` — the details panel's date line. */
107
100
  export function formatEventDate(date: Date, locale?: string): string {
108
101
  return (
109
- format(date, locale, {
102
+ formatGregorian(date, locale, {
110
103
  weekday: 'short',
111
104
  month: 'short',
112
105
  day: 'numeric',
@@ -116,7 +109,7 @@ export function formatEventDate(date: Date, locale?: string): string {
116
109
 
117
110
  /** `Aug` — the month switcher's label below `sm`. */
118
111
  export function formatShortMonth(date: Date, locale?: string): string {
119
- return format(date, locale, { month: 'short' }) ?? String(date.getMonth() + 1);
112
+ return formatGregorian(date, locale, { month: 'short' }) ?? String(date.getMonth() + 1);
120
113
  }
121
114
 
122
115
  /** `Sun` … `Sat` — the month grid's weekday pills. */
@@ -126,8 +119,8 @@ export function weekdayShortLabels(locale?: string): { short: string; long: stri
126
119
  return fallback.map((name, index) => {
127
120
  const date = addDays(sunday, index);
128
121
  return {
129
- short: format(date, locale, { weekday: 'short' }) ?? name,
130
- long: format(date, locale, { weekday: 'long' }) ?? name,
122
+ short: formatGregorian(date, locale, { weekday: 'short' }) ?? name,
123
+ long: formatGregorian(date, locale, { weekday: 'long' }) ?? name,
131
124
  };
132
125
  });
133
126
  }
@@ -2,6 +2,7 @@ import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
2
2
  import { View, type StyleProp, type ViewStyle } from 'react-native';
3
3
 
4
4
  import { useControllableState } from '../hooks/use-controllable-state';
5
+ import { useMessages } from '../locale/messages';
5
6
  import { adoptStyleSheet } from '../styles/adopt-style-sheet';
6
7
  import { useTheme } from '../theme/use-theme';
7
8
  import {
@@ -21,8 +22,15 @@ import {
21
22
  type DayCellState,
22
23
  } from './calendar-grid';
23
24
  import { CALENDAR_CSS, CalendarMonth, dayKey } from './CalendarMonth';
25
+ import { DATE_PICKER_MESSAGES } from './messages';
24
26
  import { resolveCalendarPalette } from './palette';
25
- import type { CalendarConstraintProps, CalendarProps, DateRange, RangeCalendarProps } from './types';
27
+ import type {
28
+ CalendarConstraintProps,
29
+ CalendarLabels,
30
+ CalendarProps,
31
+ DateRange,
32
+ RangeCalendarProps,
33
+ } from './types';
26
34
 
27
35
  /**
28
36
  * `Calendar` (one day) and `RangeCalendar` (a span of days): month panels
@@ -51,6 +59,7 @@ interface CalendarFrameProps extends CalendarConstraintProps {
51
59
  /** The day under the pointer, or under keyboard focus. */
52
60
  onPreviewDay?: (date: Date | null) => void;
53
61
  accessibilityLabel?: string;
62
+ labels?: CalendarLabels;
54
63
  style?: StyleProp<ViewStyle>;
55
64
  testID?: string;
56
65
  }
@@ -66,12 +75,16 @@ function CalendarFrame({
66
75
  maxDate,
67
76
  isDateUnavailable,
68
77
  weekStartsOn = 0,
69
- locale,
78
+ locale: localeProp,
70
79
  accessibilityLabel,
80
+ labels,
71
81
  style,
72
82
  testID,
73
83
  }: CalendarFrameProps) {
74
84
  const theme = useTheme();
85
+ const { locale, messages } = useMessages(DATE_PICKER_MESSAGES, localeProp);
86
+ const previousLabel = labels?.previousMonth ?? messages.previousMonth;
87
+ const nextLabel = labels?.nextMonth ?? messages.nextMonth;
75
88
  const palette = useMemo(() => resolveCalendarPalette(theme), [theme]);
76
89
  const [firstMonth, setFirstMonth] = useState(() => startOfMonth(initialDate));
77
90
  const [focusedDate, setFocusedDate] = useState(() => startOfDay(initialDate));
@@ -194,6 +207,8 @@ function CalendarFrame({
194
207
  onKeyDown={onKeyDown}
195
208
  registerNode={registerNode}
196
209
  accessibilityLabel={accessibilityLabel}
210
+ previousLabel={previousLabel}
211
+ nextLabel={nextLabel}
197
212
  testID={testID ? `${testID}-month-${index}` : undefined}
198
213
  />
199
214
  ))}
@@ -281,6 +281,9 @@ export interface CalendarMonthProps {
281
281
  onKeyDown: (key: string, preventDefault: () => void) => void;
282
282
  registerNode: (key: string, node: View | null) => void;
283
283
  accessibilityLabel?: string;
284
+ /** The chevrons' names, already localised by the calendar. */
285
+ previousLabel: string;
286
+ nextLabel: string;
284
287
  testID?: string;
285
288
  }
286
289
 
@@ -308,6 +311,8 @@ export function CalendarMonth({
308
311
  onKeyDown,
309
312
  registerNode,
310
313
  accessibilityLabel,
314
+ previousLabel,
315
+ nextLabel,
311
316
  testID,
312
317
  }: CalendarMonthProps) {
313
318
  const title = formatMonthTitle(month, locale);
@@ -339,7 +344,7 @@ export function CalendarMonth({
339
344
  {onPrevious ? (
340
345
  <NavButton
341
346
  direction="left"
342
- label="Previous month"
347
+ label={previousLabel}
343
348
  onPress={onPrevious}
344
349
  disabled={previousDisabled}
345
350
  palette={palette}
@@ -356,7 +361,7 @@ export function CalendarMonth({
356
361
  {onNext ? (
357
362
  <NavButton
358
363
  direction="right"
359
- label="Next month"
364
+ label={nextLabel}
360
365
  onPress={onNext}
361
366
  disabled={nextDisabled}
362
367
  palette={palette}
@@ -2,9 +2,11 @@ import React, { useState } from 'react';
2
2
  import { View } from 'react-native';
3
3
 
4
4
  import { useControllableState } from '../hooks/use-controllable-state';
5
+ import { useMessages } from '../locale/messages';
5
6
  import { Calendar } from './Calendar';
6
7
  import { formatTriggerDate } from './calendar-grid';
7
8
  import { DateChipInput } from './DateChipInput';
9
+ import { DATE_PICKER_MESSAGES } from './messages';
8
10
  import { PickerActions, PickerShell, usePickerPalette } from './PickerShell';
9
11
  import { SummaryPresence } from './SummaryPresence';
10
12
  import type { DatePickerProps } from './types';
@@ -27,8 +29,9 @@ export function DatePicker({
27
29
  value,
28
30
  defaultValue = null,
29
31
  onChange,
30
- placeholder = 'Select date',
31
- accessibilityLabel = 'Date',
32
+ placeholder: placeholderProp,
33
+ accessibilityLabel: accessibilityLabelProp,
34
+ labels,
32
35
  disabled,
33
36
  open: openProp,
34
37
  defaultOpen = false,
@@ -37,11 +40,14 @@ export function DatePicker({
37
40
  maxDate,
38
41
  isDateUnavailable,
39
42
  weekStartsOn,
40
- locale,
43
+ locale: localeProp,
41
44
  style,
42
45
  testID,
43
46
  }: DatePickerProps) {
44
47
  const palette = usePickerPalette();
48
+ const { locale, messages } = useMessages(DATE_PICKER_MESSAGES, localeProp);
49
+ const placeholder = placeholderProp ?? messages.datePlaceholder;
50
+ const accessibilityLabel = accessibilityLabelProp ?? messages.dateLabel;
45
51
  const [committed, setCommitted] = useControllableState<Date | null>({
46
52
  value,
47
53
  defaultValue,
@@ -91,6 +97,7 @@ export function DatePicker({
91
97
  weekStartsOn={weekStartsOn}
92
98
  locale={locale}
93
99
  accessibilityLabel={accessibilityLabel}
100
+ labels={labels}
94
101
  testID={testID ? `${testID}-calendar` : undefined}
95
102
  />
96
103
  <View
@@ -126,6 +133,8 @@ export function DatePicker({
126
133
  setOpenState(false);
127
134
  }}
128
135
  applyDisabled={!pending}
136
+ cancelLabel={labels?.cancel ?? messages.cancel}
137
+ applyLabel={labels?.apply ?? messages.apply}
129
138
  testID={testID}
130
139
  />
131
140
  </View>
@@ -2,6 +2,7 @@ import React, { useMemo, useState } from 'react';
2
2
  import { Platform, Pressable, View, useWindowDimensions, type TextStyle } from 'react-native';
3
3
 
4
4
  import { useControllableState } from '../hooks/use-controllable-state';
5
+ import { useMessages } from '../locale/messages';
5
6
  import type { WebCssStyle } from '../styles/web-view-style';
6
7
  import { Text, TYPE_SCALE } from '../typography';
7
8
  import {
@@ -13,6 +14,7 @@ import {
13
14
  import { RangeCalendar } from './Calendar';
14
15
  import { MONTH_PANEL_WIDTH } from './CalendarMonth';
15
16
  import { DateChipInput } from './DateChipInput';
17
+ import { DATE_PICKER_MESSAGES } from './messages';
16
18
  import type { CalendarPalette } from './palette';
17
19
  import { PickerActions, PickerShell, usePickerPalette } from './PickerShell';
18
20
  import { SummaryPresence } from './SummaryPresence';
@@ -85,9 +87,10 @@ export function DateRangePicker({
85
87
  value,
86
88
  defaultValue = null,
87
89
  onChange,
88
- placeholder = 'Select date range',
89
- accessibilityLabel = 'Date range',
90
+ placeholder: placeholderProp,
91
+ accessibilityLabel: accessibilityLabelProp,
90
92
  quickSelect = true,
93
+ labels,
91
94
  disabled,
92
95
  open: openProp,
93
96
  defaultOpen = false,
@@ -96,11 +99,14 @@ export function DateRangePicker({
96
99
  maxDate,
97
100
  isDateUnavailable,
98
101
  weekStartsOn,
99
- locale,
102
+ locale: localeProp,
100
103
  style,
101
104
  testID,
102
105
  }: DateRangePickerProps) {
103
106
  const palette = usePickerPalette();
107
+ const { locale, messages } = useMessages(DATE_PICKER_MESSAGES, localeProp);
108
+ const placeholder = placeholderProp ?? messages.rangePlaceholder;
109
+ const accessibilityLabel = accessibilityLabelProp ?? messages.rangeLabel;
104
110
  const { width: windowWidth } = useWindowDimensions();
105
111
  const [committed, setCommitted] = useControllableState<DateRange | null>({
106
112
  value,
@@ -154,8 +160,8 @@ export function DateRangePicker({
154
160
  <View style={{ width: 118, gap: 6 }}>
155
161
  {presets.map((preset) => (
156
162
  <PresetRow
157
- key={preset.label}
158
- label={preset.label}
163
+ key={preset.key}
164
+ label={labels?.presets?.[preset.key] ?? messages.presets[preset.key]}
159
165
  active={isSameRange(pending, preset.range)}
160
166
  onPress={() => choose(preset.range)}
161
167
  palette={palette}
@@ -183,6 +189,7 @@ export function DateRangePicker({
183
189
  weekStartsOn={weekStartsOn}
184
190
  locale={locale}
185
191
  accessibilityLabel={accessibilityLabel}
192
+ labels={labels}
186
193
  testID={testID ? `${testID}-calendar` : undefined}
187
194
  />
188
195
  <View
@@ -206,7 +213,7 @@ export function DateRangePicker({
206
213
  <View style={{ flexDirection: 'row', alignItems: 'center', gap: 5 }}>
207
214
  <DateChipInput
208
215
  date={pending.start}
209
- label="Start date"
216
+ label={labels?.startDate ?? messages.startDate}
210
217
  palette={palette}
211
218
  onCommit={(start) =>
212
219
  setPending({ start, end: start > pending.end ? start : pending.end })
@@ -215,7 +222,7 @@ export function DateRangePicker({
215
222
  <Text style={{ ...BODY, color: palette.secondaryText }}>-</Text>
216
223
  <DateChipInput
217
224
  date={pending.end}
218
- label="End date"
225
+ label={labels?.endDate ?? messages.endDate}
219
226
  palette={palette}
220
227
  onCommit={(end) =>
221
228
  setPending({ start: end < pending.start ? end : pending.start, end })
@@ -234,7 +241,7 @@ export function DateRangePicker({
234
241
  }}
235
242
  >
236
243
  <Text style={{ ...BODY, color: palette.secondaryText }}>
237
- {days} day{days === 1 ? '' : 's'} selected
244
+ {(labels?.daysSelected ?? messages.daysSelected)(days)}
238
245
  </Text>
239
246
  </View>
240
247
  ) : null}
@@ -252,6 +259,8 @@ export function DateRangePicker({
252
259
  setOpenState(false);
253
260
  }}
254
261
  applyDisabled={!pending}
262
+ cancelLabel={labels?.cancel ?? messages.cancel}
263
+ applyLabel={labels?.apply ?? messages.apply}
255
264
  testID={testID}
256
265
  />
257
266
  </View>
@@ -16,11 +16,14 @@ import { useControllableState } from '../hooks/use-controllable-state';
16
16
  import { RiFlagLine } from '../icons/remix/RiFlagLine';
17
17
  import { RiGlobalLine } from '../icons/remix/RiGlobalLine';
18
18
  import { RiVideoLine } from '../icons/remix/RiVideoLine';
19
+ import { formatGregorian } from '../locale/format-date';
20
+ import { useMessages } from '../locale/messages';
19
21
  import type { WebCssStyle } from '../styles/web-view-style';
20
22
  import { useTheme } from '../theme/use-theme';
21
23
  import { Text, TYPE_SCALE } from '../typography';
22
24
  import { Calendar } from './Calendar';
23
25
  import { formatTriggerDate, today } from './calendar-grid';
26
+ import { DATE_PICKER_MESSAGES } from './messages';
24
27
  import type { CalendarPalette } from './palette';
25
28
  import { PickerShell, usePickerPalette } from './PickerShell';
26
29
  import type {
@@ -95,11 +98,11 @@ function formatTime(time: string, format: MeetingSchedulerHourFormat): string {
95
98
  }
96
99
 
97
100
  function weekdayShort(date: Date, locale?: string): string {
98
- try {
99
- return new Intl.DateTimeFormat(locale, { weekday: 'short' }).format(date).slice(0, 2);
100
- } catch {
101
- return ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'][date.getDay()] ?? '';
102
- }
101
+ return (
102
+ formatGregorian(date, locale, { weekday: 'short' })?.slice(0, 2) ??
103
+ ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'][date.getDay()] ??
104
+ ''
105
+ );
103
106
  }
104
107
 
105
108
  // ---------------------------------------------------------------------------
@@ -352,8 +355,8 @@ export function MeetingScheduler({
352
355
  defaultValue,
353
356
  onChange,
354
357
  defaultHourFormat = '24h',
355
- triggerLabel = 'Schedule a meeting',
356
- accessibilityLabel = 'Schedule meeting',
358
+ triggerLabel: triggerLabelProp,
359
+ accessibilityLabel: accessibilityLabelProp,
357
360
  labels,
358
361
  disabled,
359
362
  open: openProp,
@@ -363,11 +366,14 @@ export function MeetingScheduler({
363
366
  maxDate,
364
367
  isDateUnavailable,
365
368
  weekStartsOn,
366
- locale,
369
+ locale: localeProp,
367
370
  style,
368
371
  testID,
369
372
  }: MeetingSchedulerProps) {
370
373
  const theme = useTheme();
374
+ const { locale, messages } = useMessages(DATE_PICKER_MESSAGES, localeProp);
375
+ const triggerLabel = triggerLabelProp ?? messages.meetingTrigger;
376
+ const accessibilityLabel = accessibilityLabelProp ?? messages.meetingLabel;
371
377
  const palette = usePickerPalette();
372
378
  const { width: windowWidth } = useWindowDimensions();
373
379
  const slots = useMemo(() => timeSlots ?? defaultTimeSlots(), [timeSlots]);
@@ -397,8 +403,8 @@ export function MeetingScheduler({
397
403
 
398
404
  const wide = windowWidth >= WIDE_WIDTH + 32;
399
405
  const shadow2xs = theme.isDark ? SHADOW_2XS.dark : SHADOW_2XS.light;
400
- const sendLabel = labels?.send ?? 'Send meeting';
401
- const durationText = (labels?.duration ?? ((n: number) => `${n} minutes`))(meeting.durationMinutes);
406
+ const sendLabel = labels?.send ?? messages.send;
407
+ const durationText = (labels?.duration ?? messages.duration)(meeting.durationMinutes);
402
408
  const chevron = useMemo(() => ({ open, color: palette.secondaryText }), [open, palette.secondaryText]);
403
409
 
404
410
  const hostColumn = (
@@ -469,6 +475,7 @@ export function MeetingScheduler({
469
475
  weekStartsOn={weekStartsOn}
470
476
  locale={locale}
471
477
  accessibilityLabel={accessibilityLabel}
478
+ labels={labels}
472
479
  testID={testID ? `${testID}-calendar` : undefined}
473
480
  />
474
481
  <View
@@ -504,7 +511,7 @@ export function MeetingScheduler({
504
511
  {formatTriggerDate(pending.date, locale)}
505
512
  </Text>
506
513
  <Text style={[BODY_MEDIUM, { color: palette.secondaryText }]}>
507
- {pending.time ?? labels?.selectTime ?? 'Select a time'}
514
+ {pending.time ?? labels?.selectTime ?? messages.selectTime}
508
515
  </Text>
509
516
  </>
510
517
  ) : null}
@@ -99,20 +99,24 @@ export function PickerActions({
99
99
  onCancel,
100
100
  onApply,
101
101
  applyDisabled,
102
+ cancelLabel,
103
+ applyLabel,
102
104
  testID,
103
105
  }: {
104
106
  onCancel: () => void;
105
107
  onApply: () => void;
106
108
  applyDisabled: boolean;
109
+ cancelLabel: string;
110
+ applyLabel: string;
107
111
  testID?: string;
108
112
  }) {
109
113
  return (
110
114
  <View style={{ flexDirection: 'row', alignItems: 'center', gap: 10 }}>
111
115
  <Button onPress={onCancel} testID={testID ? `${testID}-cancel` : undefined} appearance="subtle" tone="neutral">
112
- Cancel
116
+ {cancelLabel}
113
117
  </Button>
114
118
  <Button onPress={onApply} disabled={applyDisabled} testID={testID ? `${testID}-apply` : undefined}>
115
- Apply
119
+ {applyLabel}
116
120
  </Button>
117
121
  </View>
118
122
  );
@@ -10,7 +10,8 @@
10
10
  * internal state. The time of day on an input is ignored.
11
11
  */
12
12
 
13
- import type { DateRange, WeekStart } from './types';
13
+ import type { DateRange, DateRangePreset, WeekStart } from './types';
14
+ import { formatGregorian } from '../locale/format-date';
14
15
 
15
16
  const MS_PER_DAY = 86_400_000;
16
17
 
@@ -130,37 +131,29 @@ export function weekdayLabels(
130
131
  const sunday = new Date(2023, 0, 1);
131
132
  return Array.from({ length: 7 }, (_, column) => {
132
133
  const date = addDays(sunday, (weekStartsOn + column) % 7);
133
- const long = format(date, locale, { weekday: 'long' }) ?? FALLBACK_WEEKDAYS[date.getDay()]!;
134
- const short = format(date, locale, { weekday: 'short' }) ?? long;
134
+ const long = formatGregorian(date, locale, { weekday: 'long' }) ?? FALLBACK_WEEKDAYS[date.getDay()]!;
135
+ const short = formatGregorian(date, locale, { weekday: 'short' }) ?? long;
135
136
  return { short: short.slice(0, 2), long };
136
137
  });
137
138
  }
138
139
 
139
- function format(date: Date, locale: string | undefined, options: Intl.DateTimeFormatOptions): string | null {
140
- try {
141
- return new Intl.DateTimeFormat(locale, options).format(date);
142
- } catch {
143
- return null;
144
- }
145
- }
146
-
147
140
  /** `September 2026` — the month panel's title. */
148
141
  export function formatMonthTitle(month: Date, locale?: string): string {
149
142
  return (
150
- format(month, locale, { month: 'long', year: 'numeric' }) ??
143
+ formatGregorian(month, locale, { month: 'long', year: 'numeric' }) ??
151
144
  `${month.getMonth() + 1}/${month.getFullYear()}`
152
145
  );
153
146
  }
154
147
 
155
148
  /** `Sep 16, 2026` — the trigger's label. */
156
149
  export function formatTriggerDate(date: Date, locale?: string): string {
157
- return format(date, locale, { month: 'short', day: 'numeric', year: 'numeric' }) ?? formatChipDate(date);
150
+ return formatGregorian(date, locale, { month: 'short', day: 'numeric', year: 'numeric' }) ?? formatChipDate(date);
158
151
  }
159
152
 
160
153
  /** `Wednesday, September 16, 2026` — a day button's accessible name. */
161
154
  export function formatAccessibleDate(date: Date, locale?: string): string {
162
155
  return (
163
- format(date, locale, { weekday: 'long', month: 'long', day: 'numeric', year: 'numeric' }) ??
156
+ formatGregorian(date, locale, { weekday: 'long', month: 'long', day: 'numeric', year: 'numeric' }) ??
164
157
  formatChipDate(date)
165
158
  );
166
159
  }
@@ -334,20 +327,22 @@ export function clampDate(date: Date, { minDate, maxDate }: DateConstraints): Da
334
327
  return startOfDay(date);
335
328
  }
336
329
 
337
- /** Quick-select presets, relative to `now`. */
338
- export function quickSelectPresets(now: Date = today()): { label: string; range: DateRange }[] {
330
+ /** Quick-select presets, relative to `now`. Their names are `DatePickerMessages.presets`. */
331
+ export function quickSelectPresets(
332
+ now: Date = today(),
333
+ ): { key: DateRangePreset; range: DateRange }[] {
339
334
  const lastMonth = addMonths(now, -1);
340
335
  const lastYear = new Date(now.getFullYear() - 1, now.getMonth(), 1);
341
336
  const yesterday = addDays(now, -1);
342
337
  return [
343
- { label: 'Today', range: { start: now, end: now } },
344
- { label: 'Yesterday', range: { start: yesterday, end: yesterday } },
345
- { label: 'Last week', range: { start: addDays(now, -7), end: yesterday } },
346
- { label: 'This month', range: { start: startOfMonth(now), end: endOfMonth(now) } },
347
- { label: 'Last month', range: { start: startOfMonth(lastMonth), end: endOfMonth(lastMonth) } },
348
- { label: 'This year', range: { start: startOfYear(now), end: endOfYear(now) } },
349
- { label: 'Last year', range: { start: startOfYear(lastYear), end: endOfYear(lastYear) } },
350
- { label: 'All time', range: { start: addMonths(now, -120), end: now } },
338
+ { key: 'today', range: { start: now, end: now } },
339
+ { key: 'yesterday', range: { start: yesterday, end: yesterday } },
340
+ { key: 'lastWeek', range: { start: addDays(now, -7), end: yesterday } },
341
+ { key: 'thisMonth', range: { start: startOfMonth(now), end: endOfMonth(now) } },
342
+ { key: 'lastMonth', range: { start: startOfMonth(lastMonth), end: endOfMonth(lastMonth) } },
343
+ { key: 'thisYear', range: { start: startOfYear(now), end: endOfYear(now) } },
344
+ { key: 'lastYear', range: { start: startOfYear(lastYear), end: endOfYear(lastYear) } },
345
+ { key: 'allTime', range: { start: addMonths(now, -120), end: now } },
351
346
  ];
352
347
  }
353
348
 
@@ -8,10 +8,14 @@ export { TimeField } from './TimeField';
8
8
 
9
9
  export type {
10
10
  CalendarConstraintProps,
11
+ CalendarLabels,
11
12
  CalendarProps,
13
+ DatePickerLabels,
12
14
  DatePickerProps,
13
15
  DateRange,
16
+ DateRangePickerLabels,
14
17
  DateRangePickerProps,
18
+ DateRangePreset,
15
19
  HourFormat,
16
20
  MeetingSchedulerDetails,
17
21
  MeetingSchedulerHost,