@@ -12,8 +12,17 @@ import {
1212 min ,
1313 max ,
1414} from 'date-fns' ;
15+ import type { Locale } from 'date-fns' ;
1516import { DatePickerSingleProps } from '../../types' ;
1617
18+ declare global {
19+ interface Window {
20+ dateFns ?: {
21+ locale ?: Record < string , Locale > ;
22+ } ;
23+ }
24+ }
25+
1726/**
1827 * Converts relevant moment.js format tokens to unicode tokens suitable for use
1928 * in date-fns. This maintains backwards compatibility with our publicly
@@ -31,12 +40,42 @@ function convertFormatTokens(momentFormat: string): string {
3140 . replace ( / X / g, 't' ) ; // Unix timestamp (seconds)
3241}
3342
43+ /**
44+ * Matches the user's preferred locale against the locales that have been loaded
45+ * externally on the page from the assets folder or via a script tag
46+ */
47+ export function getUserLocale ( ) : Locale | undefined {
48+ // Check if page has loaded any external locales
49+ const availableLocales = window . dateFns ?. locale ?? { } ;
50+
51+ // Match available locales against user locale preferences
52+ const localeKeys = Object . keys ( availableLocales ) ;
53+ const userLanguages = navigator . languages || [ navigator . language ] ;
54+ for ( const lang of userLanguages ) {
55+ // First check full locale string for regional variants (e.g., 'fr-CA')
56+ const normalizedLang = lang . replace ( '-' , '' ) ;
57+ if ( availableLocales [ normalizedLang ] ) {
58+ return availableLocales [ normalizedLang ] ;
59+ }
60+
61+ // Fallback to simple language code (e.g., 'fr')
62+ const langCode = lang . split ( '-' ) [ 0 ] ;
63+ if ( availableLocales [ langCode ] ) {
64+ return availableLocales [ langCode ] ;
65+ }
66+ }
67+
68+ // No match found against user language preferences, we'll use first
69+ // loaded locale (ultimately determined by script order in HTML)
70+ return availableLocales [ localeKeys [ 0 ] ] ;
71+ }
72+
3473export function formatDate ( date ?: Date , formatStr = 'YYYY-MM-DD' ) : string {
3574 if ( ! date ) {
3675 return '' ;
3776 }
3877 const convertedFormat = convertFormatTokens ( formatStr ) ;
39- return format ( date , convertedFormat ) ;
78+ return format ( date , convertedFormat , { locale : getUserLocale ( ) } ) ;
4079}
4180
4281/*
@@ -59,8 +98,11 @@ export function strAsDate(
5998 return undefined ;
6099 }
61100
101+ const locale = getUserLocale ( ) ;
62102 let parsed = formatStr
63- ? parse ( dateStr , convertFormatTokens ( formatStr ) , new Date ( ) )
103+ ? parse ( dateStr , convertFormatTokens ( formatStr ) , new Date ( ) , {
104+ locale,
105+ } )
64106 : parseISO ( dateStr ) ;
65107
66108 // Fallback to native Date constructor for non-ISO formats
@@ -148,7 +190,9 @@ export function formatMonth(
148190) : string {
149191 const { monthFormat} = extractFormats ( formatStr ) ;
150192 const convertedFormat = convertFormatTokens ( monthFormat ) ;
151- return format ( new Date ( year , month , 1 ) , convertedFormat ) ;
193+ return format ( new Date ( year , month , 1 ) , convertedFormat , {
194+ locale : getUserLocale ( ) ,
195+ } ) ;
152196}
153197
154198/**
@@ -187,7 +231,9 @@ export function getMonthOptions(
187231
188232 return Array . from ( { length : 12 } , ( _ , i ) => {
189233 const monthStart = new Date ( year , i , 1 ) ;
190- const label = format ( monthStart , convertedFormat ) ;
234+ const label = format ( monthStart , convertedFormat , {
235+ locale : getUserLocale ( ) ,
236+ } ) ;
191237
192238 // Check if this month is outside the allowed range (month-level comparison)
193239 const disabled =
@@ -204,7 +250,9 @@ export function getMonthOptions(
204250export function formatYear ( year : number , formatStr ?: string ) : string {
205251 const { yearFormat} = extractFormats ( formatStr ) ;
206252 const convertedFormat = convertFormatTokens ( yearFormat ) ;
207- return format ( new Date ( year , 0 , 1 ) , convertedFormat ) ;
253+ return format ( new Date ( year , 0 , 1 ) , convertedFormat , {
254+ locale : getUserLocale ( ) ,
255+ } ) ;
208256}
209257
210258/**
0 commit comments