diff --git a/frontend/src/components/DateField.jsx b/frontend/src/components/DateField.jsx index 654b8b4..5899517 100644 --- a/frontend/src/components/DateField.jsx +++ b/frontend/src/components/DateField.jsx @@ -36,7 +36,7 @@ export default function DateField({ id, value, onChange, placeholder, className else setText(dateToInputText(parsed)) }} /> - {touched && invalid && {t('Une année (1950) ou une date (12/03/1950)')}} + {touched && invalid && {t('Une année (1950) ou une date ({exemple})', { exemple: dateToInputText('1950-03-12') })}} ) } diff --git a/frontend/src/i18n/en/fiche.js b/frontend/src/i18n/en/fiche.js index 6875eca..763869e 100644 --- a/frontend/src/i18n/en/fiche.js +++ b/frontend/src/i18n/en/fiche.js @@ -468,7 +468,7 @@ export default { 'Merci, c’est envoyé. Le moment apparaîtra dans sa vie quand la personne qui gère l’arbre l’aura accepté.': 'Thanks, it has been sent. The moment will show in their life once the person who manages the tree has accepted it.', // Dates : année seule, décès sans date (issue #77) 'Année ou date': 'Year or date', - 'Une année (1950) ou une date (12/03/1950)': 'A year (1950) or a date (12/03/1950)', + 'Une année (1950) ou une date ({exemple})': 'A year (1950) or a date ({exemple})', 'Date inconnue': 'Date unknown', 'date inconnue': 'date unknown', 'Décédé(e), date inconnue': 'Deceased, date unknown', diff --git a/frontend/src/i18n/index.js b/frontend/src/i18n/index.js index 7ee5100..63c50d3 100644 --- a/frontend/src/i18n/index.js +++ b/frontend/src/i18n/index.js @@ -32,8 +32,23 @@ function detectLocale() { export const locale = typeof window === 'undefined' ? 'fr' : detectLocale() -// Pour toLocaleDateString et consorts. -export const dateLocale = locale === 'fr' ? 'fr-FR' : 'en-GB' +// Pour toLocaleDateString et consorts. En anglais, la région du navigateur décide de l'ordre jour/mois +// (en-US : 10/25/2026) ; hors anglais (interface anglaise sur un navigateur allemand…), en-GB (issue #152). +function englishDateLocale() { + const languages = typeof navigator === 'undefined' ? [] : (navigator.languages?.length ? navigator.languages : [navigator.language]) + const english = languages.find((lang) => /^en-[a-z]{2}$/i.test(String(lang))) + if (!english) return 'en-GB' + try { return Intl.DateTimeFormat.supportedLocalesOf(english)[0] || 'en-GB' } catch { return 'en-GB' } +} +export const dateLocale = locale === 'fr' ? 'fr-FR' : englishDateLocale() + +// Le mois avant le jour dans les dates en chiffres de cette langue (en-US) +export const monthFirst = (() => { + try { + const parts = new Intl.DateTimeFormat(dateLocale, { day: '2-digit', month: '2-digit', year: 'numeric' }).formatToParts(new Date(Date.UTC(2026, 9, 25))) + return parts.findIndex((p) => p.type === 'month') < parts.findIndex((p) => p.type === 'day') + } catch { return false } +})() if (typeof document !== 'undefined') document.documentElement.lang = locale diff --git a/frontend/src/utils/dateInput.js b/frontend/src/utils/dateInput.js index a47bb22..f202f27 100644 --- a/frontend/src/utils/dateInput.js +++ b/frontend/src/utils/dateInput.js @@ -1,14 +1,17 @@ // Saisie d'une date dans la fiche : une année seule (« 1950 ») ou une date complète (« 12/03/1950 »). // L'année seule se range au 1er janvier, la convention de toute l'appli (issue #77). +// L'ordre jour/mois suit la langue : « 03/12/1950 » pour un navigateur en-US (issue #152). +import { monthFirst } from '../i18n/index.js' const pad = (n) => String(n).padStart(2, '0') // Valeur stockée (« 1950-01-01 », « 1950 », « 1950-03-12 ») vers le texte du champ -export function dateToInputText(value) { +export function dateToInputText(value, mdy = monthFirst) { const raw = String(value ?? '').trim() if (!raw) return '' const full = raw.match(/^(\d{4})-(\d{2})-(\d{2})/) - if (full) return full[2] === '01' && full[3] === '01' ? full[1] : `${full[3]}/${full[2]}/${full[1]}` + if (full) if (full[2] === '01' && full[3] === '01') return full[1] + return mdy ? `${full[2]}/${full[3]}/${full[1]}` : `${full[3]}/${full[2]}/${full[1]}` const year = raw.match(/^(\d{4})$/) return year ? year[1] : raw } @@ -21,7 +24,7 @@ export function formatDigitsAsDate(text) { } // Texte du champ vers la valeur stockée : '' si vide, « AAAA-MM-JJ » si valable, undefined si on ne comprend pas -export function parseDateInput(text) { +export function parseDateInput(text, mdy = monthFirst) { const raw = String(text ?? '').trim() if (!raw) return '' if (/^\d{4}$/.test(raw)) return `${raw}-01-01` @@ -30,7 +33,8 @@ export function parseDateInput(text) { let year const dmy = raw.match(/^(\d{1,2})[/.\-\s](\d{1,2})[/.\-\s](\d{4})$/) const iso = raw.match(/^(\d{4})-(\d{2})-(\d{2})$/) - if (dmy) [, day, month, year] = dmy + if (dmy && mdy) [, month, day, year] = dmy + else if (dmy) [, day, month, year] = dmy else if (iso) [, year, month, day] = iso else return undefined const d = Number(day) diff --git a/frontend/src/utils/dateInput.test.js b/frontend/src/utils/dateInput.test.js index 5b0b668..6e0db7f 100644 --- a/frontend/src/utils/dateInput.test.js +++ b/frontend/src/utils/dateInput.test.js @@ -38,3 +38,16 @@ describe('formatDigitsAsDate', () => { expect(formatDigitsAsDate('1/3/1950')).toBe('1/3/1950') }) }) + +describe('ordre mois/jour d un navigateur en-US (issue #152)', () => { + it('affiche et lit le mois avant le jour', () => { + expect(dateToInputText('1950-03-12', true)).toBe('03/12/1950') + expect(parseDateInput('03/12/1950', true)).toBe('1950-03-12') + expect(parseDateInput('12/25/1950', true)).toBe('1950-12-25') + expect(parseDateInput('25/12/1950', true)).toBeUndefined() + }) + it('une année seule ne change pas', () => { + expect(dateToInputText('1950-01-01', true)).toBe('1950') + expect(parseDateInput('1950', true)).toBe('1950-01-01') + }) +})