Add locale-aware date formatting for UI dates#471
Conversation
src/utils/strings.ts
Outdated
| return format(date, 'MMM dd', { | ||
| locale: getSupportedLanguages().find((lang) => lang.code === todayTranslation.usedLng) | ||
| ?.dateLocale, | ||
| }); |
There was a problem hiding this comment.
I suppose we could just use new Intl.DateTimeFormat(todayTranslation.usedLng, {day: '2-digit', month: 'short'}).format(date)?
There was a problem hiding this comment.
I tried that, but this Intl API is kind of weird. At least for pt-BR and pt-PT, the results are awful, so I'm not sure what other languages would look like to native speakers.
pt-PT:
I don't know why it would format "short" month to "11" (we have "numeric" for that, right?).
pt-BR:
The word "de" here is just a preposition that we can totally ignore when we want it to be shorter.
Maybe we could have a specific function only for this expense date prefix and use the Intl for other places, which would probably make this look better:
There was a problem hiding this comment.
I'm not a native speaker in either of these, but maybe it is simply the proper way to format this in Portugal? For the brazilian version, yeah it looks kind of bad.
Personally I would prefer fixing locale specific edge cases rather than to import the bulky date-fns locale and pass it around.
For the expense list, we could format the day and month separately and remove dots for now?
new Intl.DateTimeFormat('pt-PT', {month: 'short'}).format(new Date()).replace('.', '')Actually returns the month name, so we could just built this in a consistent format. What do you think?
There was a problem hiding this comment.
Formatting day and month separately makes sense.
Do we need to care about the order, though? Some locales use MM/DD, others DD/MM.
There’s a way to detect that order if we really want to (expand for an example)
function formatDayMonth(locale: string, date: Date): string {
// Format day and short month separately
const day = new Intl.DateTimeFormat(locale, { day: 'numeric' }).format(date);
const monthName = new Intl.DateTimeFormat(locale, { month: 'short' }).format(date)
.replace('.', '');
// Detect whether day comes before month for this locale
const orderParts = new Intl.DateTimeFormat(locale, {
day: 'numeric',
month: 'numeric'
}).formatToParts(date);
const dayIndex = orderParts.findIndex(p => p.type === 'day');
const monthIndex = orderParts.findIndex(p => p.type === 'month');
const isDayFirst = dayIndex < monthIndex;
return isDayFirst ? `${day} ${monthName}` : `${monthName} ${day}`;
}
// Examples:
formatDayMonth('pt-BR', new Date('2025-10-11'));
// "11 out"
formatDayMonth('pt-PT', new Date('2025-10-11'));
// "11 out"
formatDayMonth('de', new Date('2025-10-11'));
// "11 Okt"
formatDayMonth('en-GB', new Date('2025-10-11'))
// "11 Oct"
formatDayMonth('en-US', new Date('2025-10-11'));
// "Oct 11"
formatDayMonth('en', new Date('2025-10-11'));
// "Oct 11"
formatDayMonth('zh', new Date('2025-10-11'))
// "10月 11日"Feels a bit over-engineered, but it works. In pt-BR I wouldn't mind if it stayed as "MM DD", since the layout stacks them on top of each other in the expense list. Not sure how that reads in other locales... |
What do you think? We just keep "MM DD" specifically in the expenses list for now? In that case, the following should be enough:
const day = new Intl.DateTimeFormat(todayTranslation.usedLng, { day: 'numeric' }).format(date);
const monthName = new Intl.DateTimeFormat(todayTranslation.usedLng, { month: 'short' }).format(date).replace('.', '');
return `${monthName} ${day}`;There was a problem hiding this comment.
Looks good to me. One change would be to change day to 2-digit maybe?
There was a problem hiding this comment.
Sorry, just had been busy lately. I'll try finishing this one.
There was a problem hiding this comment.
Would you prefer to make a separate date function just for the expenseList? That's fine by me since it is a bit hacky, while the toUIDate would rely on standard Intl API.
6365193 to
f26e3cd
Compare
f26e3cd to
5452154
Compare





Description
Adds locale-aware dates, fixes #470.
Demo
Checklist
CONTRIBUTING.mdin its entirety