From 28f33da6d0b3aa4bf1ab5c7264e372d12602d0e9 Mon Sep 17 00:00:00 2001 From: MartinBraquet Date: Wed, 18 Feb 2026 21:02:26 +0100 Subject: [PATCH] Enhance event creation with locale-aware date formatting and improved translations --- web/components/events/create-event-modal.tsx | 32 ++++--- web/lib/dayjs.ts | 89 ++++++++++++++++++++ web/messages/de.json | 2 + web/messages/fr.json | 6 +- web/pages/_app.tsx | 3 +- 5 files changed, 116 insertions(+), 16 deletions(-) diff --git a/web/components/events/create-event-modal.tsx b/web/components/events/create-event-modal.tsx index 48453466..3dbea0d7 100644 --- a/web/components/events/create-event-modal.tsx +++ b/web/components/events/create-event-modal.tsx @@ -8,7 +8,7 @@ import {api} from 'web/lib/api' import {APIError} from "common/api/utils"; import clsx from "clsx"; import {Col} from "web/components/layout/col"; -import {useT} from 'web/lib/locale'; +import {useLocale, useT} from 'web/lib/locale'; import {Event} from 'web/hooks/use-events' @@ -22,6 +22,7 @@ export function CreateEventModal(props: { const {open, setOpen, onClose, onSuccess, event} = props const isEditing = !!event const t = useT() + const {locale} = useLocale() const [loading, setLoading] = useState(false) const [error, setError] = useState(null) @@ -76,8 +77,8 @@ export function CreateEventModal(props: { title: formData.title, description: formData.description || undefined, locationType: formData.locationType, - locationAddress: formData.locationAddress || undefined, - locationUrl: formData.locationUrl || undefined, + locationAddress: formData.locationType === 'in_person' && formData.locationAddress || undefined, + locationUrl: formData.locationType === 'online' && formData.locationUrl || undefined, eventStartTime: formData.eventStartTime!.toISOString(), eventEndTime: formData.eventEndTime ? formData.eventEndTime.toISOString() @@ -91,8 +92,8 @@ export function CreateEventModal(props: { title: formData.title, description: formData.description || undefined, locationType: formData.locationType, - locationAddress: formData.locationAddress || undefined, - locationUrl: formData.locationUrl || undefined, + locationAddress: formData.locationType === 'in_person' && formData.locationAddress || undefined, + locationUrl: formData.locationType === 'online' && formData.locationUrl || undefined, eventStartTime: formData.eventStartTime!.toISOString(), eventEndTime: formData.eventEndTime ? formData.eventEndTime.toISOString() @@ -136,6 +137,9 @@ export function CreateEventModal(props: { setFormData((prev) => ({...prev, [name]: value})) } + const dateFormat = locale === 'en' ? "MMM d, yyyy h:mm aa" : "dd MMM yyyy, HH:mm" + const timeFormat = "HH:mm" + return ( @@ -231,10 +235,11 @@ export function CreateEventModal(props: {
{ if (!date) return setFormData((prev) => { @@ -245,21 +250,22 @@ export function CreateEventModal(props: { }) }} showTimeSelect - timeFormat="HH:mm" + timeFormat={timeFormat} timeIntervals={15} - dateFormat="MMM d, yyyy h:mm aa" + dateFormat={dateFormat} minDate={new Date()} required - placeholderText="Select date and time" + placeholderText={t('events.select_start_datetime', 'Select date and time')} className="bg-canvas-50 border-canvas-300 focus:border-primary-500 focus:ring-primary-500 w-full rounded-md border px-3 py-2" />
{ setFormData((prev) => { const startTime = prev.eventStartTime @@ -271,11 +277,11 @@ export function CreateEventModal(props: { }) }} showTimeSelect - timeFormat="HH:mm" + timeFormat={timeFormat} timeIntervals={15} - dateFormat="MMM d, yyyy h:mm aa" + dateFormat={dateFormat} minDate={formData.eventStartTime || new Date()} - placeholderText="Select end time (optional)" + placeholderText={t('events.select_end_datetime', 'Select end time (optional)')} className="bg-canvas-50 border-canvas-300 focus:border-primary-500 focus:ring-primary-500 w-full rounded-md border px-3 py-2" />
diff --git a/web/lib/dayjs.ts b/web/lib/dayjs.ts index 4e64815e..0b2c3a69 100644 --- a/web/lib/dayjs.ts +++ b/web/lib/dayjs.ts @@ -6,6 +6,94 @@ import {getLocale} from "web/lib/locale-cookie"; dayjs.extend(relativeTime) dayjs.extend(localizedFormat) +export const DATEPICKER_LOCALE_IMPORTS: Record Promise> = { + af: () => import('date-fns/locale/af'), + ar: () => import('date-fns/locale/ar'), + 'ar-dz': () => import('date-fns/locale/ar-DZ'), + 'ar-ma': () => import('date-fns/locale/ar-MA'), + 'ar-sa': () => import('date-fns/locale/ar-SA'), + 'ar-tn': () => import('date-fns/locale/ar-TN'), + az: () => import('date-fns/locale/az'), + be: () => import('date-fns/locale/be'), + bg: () => import('date-fns/locale/bg'), + bn: () => import('date-fns/locale/bn'), + bs: () => import('date-fns/locale/bs'), + ca: () => import('date-fns/locale/ca'), + cs: () => import('date-fns/locale/cs'), + cy: () => import('date-fns/locale/cy'), + da: () => import('date-fns/locale/da'), + de: () => import('date-fns/locale/de'), + 'de-at': () => import('date-fns/locale/de-AT'), + el: () => import('date-fns/locale/el'), + en: () => import('date-fns/locale/en-US'), + 'en-au': () => import('date-fns/locale/en-AU'), + 'en-ca': () => import('date-fns/locale/en-CA'), + 'en-gb': () => import('date-fns/locale/en-GB'), + 'en-ie': () => import('date-fns/locale/en-IE'), + 'en-in': () => import('date-fns/locale/en-IN'), + 'en-nz': () => import('date-fns/locale/en-NZ'), + eo: () => import('date-fns/locale/eo'), + es: () => import('date-fns/locale/es'), + et: () => import('date-fns/locale/et'), + eu: () => import('date-fns/locale/eu'), + fi: () => import('date-fns/locale/fi'), + fr: () => import('date-fns/locale/fr'), + 'fr-ca': () => import('date-fns/locale/fr-CA'), + 'fr-ch': () => import('date-fns/locale/fr-CH'), + fy: () => import('date-fns/locale/fy'), + gd: () => import('date-fns/locale/gd'), + gl: () => import('date-fns/locale/gl'), + gu: () => import('date-fns/locale/gu'), + he: () => import('date-fns/locale/he'), + hi: () => import('date-fns/locale/hi'), + hr: () => import('date-fns/locale/hr'), + ht: () => import('date-fns/locale/ht'), + hu: () => import('date-fns/locale/hu'), + id: () => import('date-fns/locale/id'), + is: () => import('date-fns/locale/is'), + it: () => import('date-fns/locale/it'), + 'it-ch': () => import('date-fns/locale/it-CH'), + ja: () => import('date-fns/locale/ja'), + ka: () => import('date-fns/locale/ka'), + kk: () => import('date-fns/locale/kk'), + km: () => import('date-fns/locale/km'), + kn: () => import('date-fns/locale/kn'), + ko: () => import('date-fns/locale/ko'), + lb: () => import('date-fns/locale/lb'), + lt: () => import('date-fns/locale/lt'), + lv: () => import('date-fns/locale/lv'), + mk: () => import('date-fns/locale/mk'), + mn: () => import('date-fns/locale/mn'), + ms: () => import('date-fns/locale/ms'), + mt: () => import('date-fns/locale/mt'), + nb: () => import('date-fns/locale/nb'), + nl: () => import('date-fns/locale/nl'), + 'nl-be': () => import('date-fns/locale/nl-BE'), + nn: () => import('date-fns/locale/nn'), + pl: () => import('date-fns/locale/pl'), + pt: () => import('date-fns/locale/pt'), + 'pt-br': () => import('date-fns/locale/pt-BR'), + ro: () => import('date-fns/locale/ro'), + ru: () => import('date-fns/locale/ru'), + sk: () => import('date-fns/locale/sk'), + sl: () => import('date-fns/locale/sl'), + sq: () => import('date-fns/locale/sq'), + sr: () => import('date-fns/locale/sr'), + sv: () => import('date-fns/locale/sv'), + ta: () => import('date-fns/locale/ta'), + te: () => import('date-fns/locale/te'), + th: () => import('date-fns/locale/th'), + tr: () => import('date-fns/locale/tr'), + uk: () => import('date-fns/locale/uk'), + uz: () => import('date-fns/locale/uz'), + vi: () => import('date-fns/locale/vi'), + zh: () => import('date-fns/locale/zh-CN'), + 'zh-cn': () => import('date-fns/locale/zh-CN'), + 'zh-hk': () => import('date-fns/locale/zh-HK'), + 'zh-tw': () => import('date-fns/locale/zh-TW'), + zh_tw: () => import('date-fns/locale/zh-TW'), +} + export const DAYJS_LOCALE_IMPORTS: Record Promise> = { af: () => import('dayjs/locale/af'), am: () => import('dayjs/locale/am'), @@ -154,5 +242,6 @@ export const DAYJS_LOCALE_IMPORTS: Record Promise> = { } DAYJS_LOCALE_IMPORTS[getLocale()]?.() +DATEPICKER_LOCALE_IMPORTS[getLocale()]?.() export default dayjs diff --git a/web/messages/de.json b/web/messages/de.json index ed9cb97d..db2ff536 100644 --- a/web/messages/de.json +++ b/web/messages/de.json @@ -556,6 +556,8 @@ "events.location_url": "Orts-URL", "events.start_time": "Startzeit", "events.end_time": "Endzeit", + "events.select_start_datetime": "Datum und Uhrzeit auswählen", + "events.select_end_datetime": "Endzeit auswählen (optional)", "events.max_participants": "Max. Teilnehmer (optional)", "events.leave_empty": "Leer lassen für unbegrenzt", "events.in_person": "In Person", diff --git a/web/messages/fr.json b/web/messages/fr.json index a6371434..62635374 100644 --- a/web/messages/fr.json +++ b/web/messages/fr.json @@ -554,8 +554,10 @@ "events.location_type": "Type de lieu", "events.location_address": "Adresse du lieu", "events.location_url": "URL du lieu", - "events.start_time": "Heure de début", - "events.end_time": "Heure de fin", + "events.start_time": "Début", + "events.end_time": "Fin", + "events.select_start_datetime": "Sélectionner la date", + "events.select_end_datetime": "Sélectionner (optionnel)", "events.max_participants": "Participants max (optionnel)", "events.leave_empty": "Laisser vide pour illimité", "events.in_person": "En personne", diff --git a/web/pages/_app.tsx b/web/pages/_app.tsx index 033476c9..4d71222f 100644 --- a/web/pages/_app.tsx +++ b/web/pages/_app.tsx @@ -24,7 +24,7 @@ import {I18nContext} from "web/lib/locale" import {HiddenProfilesProvider} from 'web/hooks/use-hidden-profiles' import {updateStatusBar} from "web/hooks/use-theme" import {useFontPreferenceManager} from "web/hooks/use-font-preference" -import {DAYJS_LOCALE_IMPORTS} from "web/lib/dayjs"; +import {DATEPICKER_LOCALE_IMPORTS, DAYJS_LOCALE_IMPORTS} from "web/lib/dayjs"; import 'web/lib/dayjs' if (Capacitor.isNativePlatform()) { @@ -104,6 +104,7 @@ function MyApp(props: AppProps) { setLocaleState(newLocale) resetCachedLocale() DAYJS_LOCALE_IMPORTS[newLocale]?.() + DATEPICKER_LOCALE_IMPORTS[newLocale]?.() } useEffect(() => {