diff --git a/svelte-app/src/components/about/timeline-item.svelte b/svelte-app/src/components/about/timeline-item.svelte index a5a060fa7..1b94469ab 100644 --- a/svelte-app/src/components/about/timeline-item.svelte +++ b/svelte-app/src/components/about/timeline-item.svelte @@ -1,59 +1,102 @@ -
-
- - -

{title}

-

{date}

-
+
+ +
+

{title}

+

+ {$displayRange(range.start, range.end)} • {$displayMonthDuration( + range.start, + range.end + )} +

+ {#if body} +
+ +
+ {/if}
- - {#if body} -
- -
- {/if}
diff --git a/svelte-app/src/languages/en.json b/svelte-app/src/languages/en.json index 9beaab911..32bcb7210 100644 --- a/svelte-app/src/languages/en.json +++ b/svelte-app/src/languages/en.json @@ -57,6 +57,10 @@ "present": "present", "refresh the page": "refresh the page", "{length} min read": "{length} min read", + "{months} months": "{months} months", + "{month} month": "{month} month", "{stars} stars": "{stars} stars", - "{views} views": "{views} views" + "{views} views": "{views} views", + "{years} years": "{years} years", + "{year} year": "{year} year" } \ No newline at end of file diff --git a/svelte-app/src/languages/fr.json b/svelte-app/src/languages/fr.json index 49d914387..3a6bbbb08 100644 --- a/svelte-app/src/languages/fr.json +++ b/svelte-app/src/languages/fr.json @@ -57,6 +57,10 @@ "present": "présent", "refresh the page": "rafraîchir la page", "{length} min read": "{length} minute de lecture", + "{month} month": "{month} mois", + "{month} months": "{month} mois", "{stars} stars": "{stars} étoiles", - "{views} views": "{views} vues" + "{views} views": "{views} vues", + "{year} year": "{year} an", + "{year} years": "{year} ans" } \ No newline at end of file diff --git a/svelte-app/src/lib/date.ts b/svelte-app/src/lib/date.ts new file mode 100644 index 000000000..0c333e8f0 --- /dev/null +++ b/svelte-app/src/lib/date.ts @@ -0,0 +1,83 @@ +/* eslint-disable func-call-spacing */ +import { derived } from 'svelte/store'; + +import { currentLang, t } from '$i18n'; + +export const displayRange = derived([currentLang, t], ([currentLang, t]) => { + return (start: string, end: string | undefined) => { + try { + const startDate = new Date(start), + endDate = end ? new Date(end) : undefined; + + if (!endDate) { + return `${new Intl.DateTimeFormat(currentLang, { + month: 'short', + year: 'numeric' + }).format(startDate)} - ${t('present')}`; + } + + return `${new Intl.DateTimeFormat(currentLang, { + month: 'short', + year: 'numeric' + }).format(startDate)} - ${new Intl.DateTimeFormat(currentLang, { + month: 'short', + year: 'numeric' + }).format(endDate)}`; + } catch (_) { + return t('Invalid date'); + } + }; +}); + +export const displayMonthDuration = derived< + typeof t, + (startDate?: string | Date, endDate?: string | Date) => string +>(t, (t) => { + return (startDate?: string | Date, endDate?: string | Date) => { + if (!startDate) { + return t('{month} month', { month: 1 }); + } + + const start = new Date(startDate), + end = endDate ? new Date(endDate) : new Date(); + + let years = end.getFullYear() - start.getFullYear(); + let months = end.getMonth() - start.getMonth(); + + // Adjust for cases where the end month is earlier in the year than the start month + if (months < 0) { + years--; + months += 12; // Add the months that have passed in the current year + } + + // Consolidate total duration into years and months + years += Math.floor(months / 12); + months %= 12; + + let yearString = '', + monthString = ''; + + if (years > 0) { + if (years === 1) { + yearString = t('{year} year', { year: years }); + } else { + yearString = t('{year} years', { year: years }); + } + } + + if (months > 0) { + if (months === 1) { + monthString = t('{month} month', { month: months }); + } else { + monthString = t('{month} months', { month: months }); + } + } + + // Handle the case where the difference is less than one month + if (years > 0 && months > 0) { + monthString = t('{month} month', { month: 1 }); + } + + return `${yearString}${yearString && monthString ? ' ' : ''}${monthString}`; + }; +});