Merge pull request #36 from AlexisNP/features/i18n

This commit is contained in:
AlexisNP
2024-09-08 18:00:23 +02:00
committed by GitHub
32 changed files with 1312 additions and 268 deletions

View File

@@ -5,14 +5,10 @@ useHead({
titleTemplate: (titleChunk) => { titleTemplate: (titleChunk) => {
return titleChunk ? `${titleChunk} — TTTools` : "TTTools"; return titleChunk ? `${titleChunk} — TTTools` : "TTTools";
}, },
htmlAttrs: {
lang: "fr"
},
meta: [ meta: [
{ name: "charset", content: "UTF-8" }, { name: "charset", content: "UTF-8" },
{ name: "viewport", content: "width=device-width, initial-scale=1.0" }, { name: "viewport", content: "width=device-width, initial-scale=1.0" },
{ name: "author", content: "Alexis Pelé" }, { name: "author", content: "Alexis Pelé" },
{ name: "description", content: "Tools destined to players and game master, helping them visualize their worlds better." },
{ name: "generator", content: "Nuxt" }, { name: "generator", content: "Nuxt" },
{ name: "msapplication-TileColor", content: "00aba9" }, { name: "msapplication-TileColor", content: "00aba9" },
{ name: "theme-color", content: "00aba9" }, { name: "theme-color", content: "00aba9" },

View File

@@ -29,10 +29,8 @@ const eventDetails = ref<HTMLElement>()
const emit = defineEmits(["query:close-popover"]) const emit = defineEmits(["query:close-popover"])
const dateDifference: string = getRelativeString(defaultDate, props.event.startDate) const dateDifference = computed<string>(() => getRelativeString(defaultDate, props.event.startDate))
const dateDuration: string | null = props.event.endDate const dateDuration = computed<string | null>(() => props.event.endDate ? getRelativeString(props.event.startDate, props.event.endDate, "compact") : null)
? getRelativeString(props.event.startDate, props.event.endDate, "compact")
: null
function handleJumpToDate(date: RPGDate) { function handleJumpToDate(date: RPGDate) {
jumpToDate(date) jumpToDate(date)
@@ -87,8 +85,14 @@ function deployDeleteModal() {
</template> </template>
<template v-else> <template v-else>
<p class="font-semibold"> <p class="font-semibold">
Du {{ getFormattedDateTitle(event.startDate, true) }} au {{
{{ getFormattedDateTitle(event.endDate, true) }} $t('entity.calendar.date.fromTo',
{
startDate: getFormattedDateTitle(event.startDate, true),
endDate: getFormattedDateTitle(event.endDate, true)
}
)
}}
</p> </p>
</template> </template>
</div> </div>
@@ -105,7 +109,8 @@ function deployDeleteModal() {
</p> </p>
<template v-if="dateDuration"> <template v-if="dateDuration">
<p class="text-sm italic dark:opacity-75 flex items-center gap-1"> <p class="text-sm italic dark:opacity-75 flex items-center gap-1">
<PhHourglassMedium size="16" weight="fill" /> Pendant {{ dateDuration }} <PhHourglassMedium size="16" weight="fill" />
{{ $t('entity.calendar.date.while', { duration: dateDuration } )}}
</p> </p>
</template> </template>
</div> </div>
@@ -146,8 +151,8 @@ function deployDeleteModal() {
<UiCommand> <UiCommand>
<UiCommandList> <UiCommandList>
<UiCommandGroup> <UiCommandGroup>
<UiCommandItem value="edit-event" @select="deployEditModal"> Modifier </UiCommandItem> <UiCommandItem value="edit-event" @select="deployEditModal"> {{ $t('ui.action.edit') }} </UiCommandItem>
<UiCommandItem value="delete-event" @select="deployDeleteModal"> Supprimer </UiCommandItem> <UiCommandItem value="delete-event" @select="deployDeleteModal"> {{ $t('ui.action.delete') }} </UiCommandItem>
</UiCommandGroup> </UiCommandGroup>
</UiCommandList> </UiCommandList>
</UiCommand> </UiCommand>
@@ -162,7 +167,7 @@ function deployDeleteModal() {
title="Naviguer au début" title="Naviguer au début"
@click="handleJumpToDate(event.startDate!)" @click="handleJumpToDate(event.startDate!)"
> >
<PhHourglassHigh size="16" weight="fill" /> Début <PhHourglassHigh size="16" weight="fill" /> {{ $t('entity.calendar.event.isStart') }}
</button> </button>
</UiBadge> </UiBadge>
<UiBadge class="hover:bg-indigo-400 dark:hover:bg-slate-300" as-child title="Naviguer à la fin"> <UiBadge class="hover:bg-indigo-400 dark:hover:bg-slate-300" as-child title="Naviguer à la fin">
@@ -171,7 +176,7 @@ function deployDeleteModal() {
title="Naviguer à la fin" title="Naviguer à la fin"
@click="handleJumpToDate(event.endDate!)" @click="handleJumpToDate(event.endDate!)"
> >
<PhHourglassLow size="16" weight="fill" /> Fin <PhHourglassLow size="16" weight="fill" /> {{ $t('entity.calendar.event.isEnd') }}
</button> </button>
</UiBadge> </UiBadge>
</nav> </nav>
@@ -180,11 +185,13 @@ function deployDeleteModal() {
<UiTooltip> <UiTooltip>
<UiTooltipTrigger as-child> <UiTooltipTrigger as-child>
<UiBadge class="absolute -top-2 right-2 flex gap-1"> <UiBadge class="absolute -top-2 right-2 flex gap-1">
<PhEye size="16" weight="fill" /> Évènement privé <PhEye size="16" weight="fill" /> {{ $t('entity.calendar.event.isHidden') }}
</UiBadge> </UiBadge>
</UiTooltipTrigger> </UiTooltipTrigger>
<UiTooltipContent> <UiTooltipContent>
<p>Cet évènement est uniquement visible pour vous</p> <p>
{{ $t('entity.calendar.event.hiddenTooltip') }}
</p>
</UiTooltipContent> </UiTooltipContent>
</UiTooltip> </UiTooltip>
</UiTooltipProvider> </UiTooltipProvider>

View File

@@ -25,7 +25,7 @@ const { revealAdvancedSearch } = useCalendar()
<li> <li>
<UiButton search-slash @click="revealAdvancedSearch()"> <UiButton search-slash @click="revealAdvancedSearch()">
<PhMagnifyingGlass size="20" weight="light" /> <PhMagnifyingGlass size="20" weight="light" />
Recherche avancée {{ $t('entity.advancedSearch.title') }}
</UiButton> </UiButton>
</li> </li>
<li> <li>

View File

@@ -9,6 +9,8 @@ import {
PhCaretRight PhCaretRight
} from "@phosphor-icons/vue" } from "@phosphor-icons/vue"
const { t } = useI18n()
interface DirectionLabels { interface DirectionLabels {
pastFar: string pastFar: string
pastNear: string pastNear: string
@@ -23,35 +25,35 @@ const activeDirectionLabels: ComputedRef<DirectionLabels> = computed(() => {
switch (currentConfig.viewType) { switch (currentConfig.viewType) {
case "month": case "month":
return { return {
pastFar: "Année précédente", pastFar: t("entity.calendar.years.prevSingular"),
pastNear: "Mois précédent", pastNear: t("entity.calendar.months.prevSingular"),
futureNear: "Mois suivant", futureNear: t("entity.calendar.months.nextSingular"),
futureFar: "Année suivante" futureFar: t("entity.calendar.years.nextSingular")
} }
case "year": case "year":
return { return {
pastFar: "Décennie précédente", pastFar: t("entity.calendar.decades.prevSingular"),
pastNear: "Année précédente", pastNear: t("entity.calendar.years.prevSingular"),
futureNear: "Année suivante", futureNear: t("entity.calendar.years.nextSingular"),
futureFar: "Décennie suivante" futureFar: t("entity.calendar.decades.nextSingular")
} }
case "decade": case "decade":
return { return {
pastFar: "Siècle précédent", pastFar: t("entity.calendar.centuries.prevSingular"),
pastNear: "Décennie précédente", pastNear: t("entity.calendar.decades.prevSingular"),
futureNear: "Décennie suivante", futureNear: t("entity.calendar.decades.nextSingular"),
futureFar: "Siècle suivant" futureFar: t("entity.calendar.centuries.nextSingular")
} }
case "century": case "century":
default: default:
return { return {
pastFar: "Millénaire précédent", pastFar: t("entity.calendar.millenias.prevSingular"),
pastNear: "Siècle précédent", pastNear: t("entity.calendar.centuries.prevSingular"),
futureNear: "Siècle suivant", futureNear: t("entity.calendar.centuries.nextSingular"),
futureFar: "Millénaire suivant" futureFar: t("entity.calendar.millenias.nextSingular")
} }
} }
}) })

View File

@@ -87,7 +87,9 @@ function handleGotoPreviousEventPage(position: "next" | "prev" = "next") {
</UiButton> </UiButton>
</UiTooltipTrigger> </UiTooltipTrigger>
<UiTooltipContent> <UiTooltipContent>
<p>Précédente page à évènements</p> <p>
{{ $t('entity.calendar.event.prevPage') }}
</p>
</UiTooltipContent> </UiTooltipContent>
</UiTooltip> </UiTooltip>
</UiTooltipProvider> </UiTooltipProvider>
@@ -106,7 +108,9 @@ function handleGotoPreviousEventPage(position: "next" | "prev" = "next") {
</UiButton> </UiButton>
</UiTooltipTrigger> </UiTooltipTrigger>
<UiTooltipContent> <UiTooltipContent>
<p>Prochaine page à évènements</p> <p>
{{ $t('entity.calendar.event.nextPage') }}
</p>
</UiTooltipContent> </UiTooltipContent>
</UiTooltip> </UiTooltip>
</UiTooltipProvider> </UiTooltipProvider>

View File

@@ -14,17 +14,9 @@ const isDefaultDate: ComputedRef<boolean> = computed<boolean>(() => areDatesIden
<UiTooltipProvider :delay-duration="250"> <UiTooltipProvider :delay-duration="250">
<UiTooltip> <UiTooltip>
<UiTooltipTrigger as-child> <UiTooltipTrigger as-child>
<ClientOnly> <UiButton size="sm" :disabled="isDefaultDate" @click="jumpToDefaultDate">
<UiButton size="sm" :disabled="isDefaultDate" @click="jumpToDefaultDate"> {{ $t('entity.calendar.date.today') }}
Aujourd'hui </UiButton>
</UiButton>
<template #fallback>
<UiButton size="sm">
Aujourd'hui
</UiButton>
</template>
</ClientOnly>
</UiTooltipTrigger> </UiTooltipTrigger>
<UiTooltipContent> <UiTooltipContent>
<p>{{ defaultDateFormatted }}</p> <p>{{ defaultDateFormatted }}</p>

View File

@@ -18,7 +18,9 @@ const viewTypeTitle = computed(() => getViewTypeTitle(currentConfig.viewType))
</UiButton> </UiButton>
</UiDropdownMenuTrigger> </UiDropdownMenuTrigger>
<UiDropdownMenuContent :side="'bottom'" :collision-padding="30"> <UiDropdownMenuContent :side="'bottom'" :collision-padding="30">
<UiDropdownMenuLabel>Mode d'affichage</UiDropdownMenuLabel> <UiDropdownMenuLabel>
{{ $t('ui.displayMode') }}
</UiDropdownMenuLabel>
<UiDropdownMenuSeparator /> <UiDropdownMenuSeparator />
<UiDropdownMenuItem <UiDropdownMenuItem
v-for="option in viewTypeOptions" v-for="option in viewTypeOptions"

View File

@@ -30,7 +30,9 @@ function handleClose() {
<span v-if="calendarSkeletonName"> <span v-if="calendarSkeletonName">
{{ calendarSkeletonName }} {{ calendarSkeletonName }}
</span> </span>
<span v-else>Nouveau calendrier</span> <span v-else>
{{ $t('entity.calendar.createDialog.title') }}
</span>
</span> </span>
</UiAlertDialogTitle> </UiAlertDialogTitle>

View File

@@ -53,10 +53,12 @@ function handleClosing() {
@interact-outside="handleClosing" @interact-outside="handleClosing"
@pointer-down-outside="handleClosing" @pointer-down-outside="handleClosing"
> >
<UiAlertDialogTitle> Êtes-vous sûr de supprimer ce calendrier ?</UiAlertDialogTitle> <UiAlertDialogTitle>
{{ $t('entity.calendar.deleteDialog.title') }}
</UiAlertDialogTitle>
<UiAlertDialogDescription> <UiAlertDialogDescription>
Les évènements ne seront plus accessibles et vous ne pourrez plus récupérer les données ! {{ $t('entity.calendar.deleteDialog.subtitle') }}
</UiAlertDialogDescription> </UiAlertDialogDescription>
<form @submit.prevent="handleAction"> <form @submit.prevent="handleAction">
@@ -70,13 +72,13 @@ function handleClosing() {
<footer class="flex gap-2 justify-between"> <footer class="flex gap-2 justify-between">
<UiButton type="button" size="sm" variant="outline" @click="handleClosing"> <UiButton type="button" size="sm" variant="outline" @click="handleClosing">
Retour {{ $t('ui.action.back') }}
</UiButton> </UiButton>
<div class="flex gap-2 justify-end"> <div class="flex gap-2 justify-end">
<Transition name="fade-delay"> <Transition name="fade-delay">
<UiButton v-if="isLoading" type="button" size="sm" variant="destructive"> <UiButton v-if="isLoading" type="button" size="sm" variant="destructive">
Annuler {{ $t('ui.action.cancel') }}
</UiButton> </UiButton>
</Transition> </Transition>
@@ -85,7 +87,7 @@ function handleClosing() {
<PhCircleNotch v-if="isLoading" size="20" class="animate-spin"/> <PhCircleNotch v-if="isLoading" size="20" class="animate-spin"/>
</Transition> </Transition>
Supprimer "{{ calendar?.name }}" {{ $t('entity.deleteOne', { entity: calendar?.name }) }}
</UiButton> </UiButton>
</div> </div>
</footer> </footer>

View File

@@ -78,19 +78,22 @@ function handleFormCancel() {
<UiTabsTrigger value="global" class="font-bold"> <UiTabsTrigger value="global" class="font-bold">
<div class="flex items-center gap-1"> <div class="flex items-center gap-1">
<PhWrench size="18" weight="fill" /> <PhWrench size="18" weight="fill" />
Général
{{ $t('entity.calendar.createDialog.tabs.general.title') }}
</div> </div>
</UiTabsTrigger> </UiTabsTrigger>
<UiTabsTrigger value="months" class="font-bold"> <UiTabsTrigger value="months" class="font-bold">
<div class="flex items-center gap-1"> <div class="flex items-center gap-1">
<PhCalendarDots size="18" weight="fill" /> <PhCalendarDots size="18" weight="fill" />
Mois de l'année
{{ $t('entity.calendar.createDialog.tabs.months.title') }}
</div> </div>
</UiTabsTrigger> </UiTabsTrigger>
<UiTabsTrigger value="today" class="font-bold"> <UiTabsTrigger value="today" class="font-bold">
<div class="flex items-center gap-1"> <div class="flex items-center gap-1">
<PhAlarm size="18" weight="fill" /> <PhAlarm size="18" weight="fill" />
Aujourd'hui
{{ $t('entity.calendar.createDialog.tabs.today.title') }}
</div> </div>
</UiTabsTrigger> </UiTabsTrigger>
</UiTabsList> </UiTabsList>
@@ -116,7 +119,7 @@ function handleFormCancel() {
<footer class="flex justify-end gap-2 mt-6"> <footer class="flex justify-end gap-2 mt-6">
<UiButton type="button" variant="destructive" @click="handleFormCancel"> <UiButton type="button" variant="destructive" @click="handleFormCancel">
Annuler {{ $t('ui.action.cancel') }}
</UiButton> </UiButton>
<UiButton type="submit" :disabled="!validSkeleton || isCreatingCalendar"> <UiButton type="submit" :disabled="!validSkeleton || isCreatingCalendar">
@@ -124,7 +127,7 @@ function handleFormCancel() {
<PhCircleNotch v-if="isCreatingCalendar" size="20" class="opacity-50 animate-spin"/> <PhCircleNotch v-if="isCreatingCalendar" size="20" class="opacity-50 animate-spin"/>
</Transition> </Transition>
Créer {{ $t('ui.action.save') }}
</UiButton> </UiButton>
</footer> </footer>
</form> </form>

View File

@@ -103,7 +103,7 @@ function handleCancel() {
type="text" type="text"
name="new-event-title" name="new-event-title"
required required
placeholder="Titre de l'évènement" :placeholder="$t('entity.calendar.event.title')"
class="w-full -my-1 py-1 -mx-1 px-1 text-lg border-b-[1px] bg-transparent focus-visible:outline-none focus-visible:border-blue-600" class="w-full -my-1 py-1 -mx-1 px-1 text-lg border-b-[1px] bg-transparent focus-visible:outline-none focus-visible:border-blue-600"
> >
</div> </div>
@@ -113,8 +113,7 @@ function handleCancel() {
id="new-event-description" id="new-event-description"
v-model="eventSkeleton.description" v-model="eventSkeleton.description"
name="new-event-description" name="new-event-description"
placeholder="Ajouter une description" :placeholder="$t('entity.addDescription')" class="w-full -my-1 py-1 -mx-1 px-1 min-h-24 max-h-36 text-sm border-b-[1px] bg-transparent focus-visible:outline-none focus-visible:border-blue-600"
class="w-full -my-1 py-1 -mx-1 px-1 min-h-24 max-h-36 text-sm border-b-[1px] bg-transparent focus-visible:outline-none focus-visible:border-blue-600"
/> />
</div> </div>
@@ -124,7 +123,7 @@ function handleCancel() {
<CalendarInputRPGDate <CalendarInputRPGDate
v-model:model-value="eventSkeleton.startDate" v-model:model-value="eventSkeleton.startDate"
placeholder="Date de début" :placeholder="$t('entity.calendar.date.start')"
:initial-date="props.date" :initial-date="props.date"
:required="true" :required="true"
/> />
@@ -133,7 +132,7 @@ function handleCancel() {
<CalendarInputRPGDate <CalendarInputRPGDate
v-model:model-value="eventSkeleton.endDate" v-model:model-value="eventSkeleton.endDate"
placeholder="Date de fin" :placeholder="$t('entity.calendar.date.end')"
:initial-date="props.date" :initial-date="props.date"
/> />
</div> </div>
@@ -143,7 +142,7 @@ function handleCancel() {
<div class="flex items-center gap-4"> <div class="flex items-center gap-4">
<PhTag size="18" weight="fill" /> <PhTag size="18" weight="fill" />
<CalendarInputEventCategory v-model="eventSkeleton.category" placeholder="Ajouter une catégorie principale" /> <CalendarInputEventCategory v-model="eventSkeleton.category" :placeholder="$t('entity.category.addPrimary')" />
</div> </div>
</div> </div>
@@ -151,7 +150,7 @@ function handleCancel() {
<div class="flex items-center gap-4"> <div class="flex items-center gap-4">
<PhTag size="18" weight="fill" /> <PhTag size="18" weight="fill" />
<CalendarInputEventCategories v-model="eventSkeleton.secondaryCategories" placeholder="Ajouter des catégories secondaires" /> <CalendarInputEventCategories v-model="eventSkeleton.secondaryCategories" :placeholder="$t('entity.category.addSecondaries')" />
</div> </div>
</div> --> </div> -->
@@ -164,7 +163,7 @@ function handleCancel() {
v-model="eventSkeleton.location" v-model="eventSkeleton.location"
type="text" type="text"
name="new-event-location" name="new-event-location"
placeholder="Ajouter un endroit" :placeholder="$t('entity.calendar.event.addLocation')"
class="w-full -my-1 py-2 px-2 text-sm border-b-[1px] bg-transparent focus-visible:outline-none focus-visible:border-blue-600"> class="w-full -my-1 py-2 px-2 text-sm border-b-[1px] bg-transparent focus-visible:outline-none focus-visible:border-blue-600">
</div> </div>
</div> </div>
@@ -178,10 +177,10 @@ function handleCancel() {
<UiSwitch id="new-event-visibility" v-model:checked="eventSkeleton.hidden" /> <UiSwitch id="new-event-visibility" v-model:checked="eventSkeleton.hidden" />
<UiLabel for="new-event-visibility"> <UiLabel for="new-event-visibility">
<template v-if="!eventSkeleton.hidden"> <template v-if="!eventSkeleton.hidden">
Évènement visible {{ $t('entity.calendar.event.isPublic') }}
</template> </template>
<template v-else> <template v-else>
Évènement caché {{ $t('entity.calendar.event.isHidden') }}
</template> </template>
</UiLabel> </UiLabel>
</div> </div>
@@ -197,7 +196,7 @@ function handleCancel() {
<div class="flex gap-2 justify-end"> <div class="flex gap-2 justify-end">
<Transition name="fade-delay"> <Transition name="fade-delay">
<UiButton v-if="isLoading" type="button" size="sm" variant="destructive" @click.prevent="handleCancel"> <UiButton v-if="isLoading" type="button" size="sm" variant="destructive" @click.prevent="handleCancel">
Annuler {{ $t('ui.action.cancel') }}
</UiButton> </UiButton>
</Transition> </Transition>
@@ -206,7 +205,7 @@ function handleCancel() {
<PhCircleNotch v-if="isLoading" size="20" class="opacity-50 animate-spin"/> <PhCircleNotch v-if="isLoading" size="20" class="opacity-50 animate-spin"/>
</Transition> </Transition>
Sauvegarder {{ $t('ui.action.save') }}
</UiButton> </UiButton>
</div> </div>
</div> </div>

View File

@@ -69,10 +69,10 @@ function handleCancel(): void {
@interact-outside="handleClosing" @interact-outside="handleClosing"
@pointer-down-outside="handleClosing" @pointer-down-outside="handleClosing"
> >
<UiAlertDialogTitle> Supprimer l'évènement</UiAlertDialogTitle> <UiAlertDialogTitle>{{ $t('entity.calendar.event.deleteDialog.title') }}</UiAlertDialogTitle>
<UiAlertDialogDescription> <UiAlertDialogDescription>
Les données associés à cet évènement seront supprimées et vous ne pourrez plus les récupérer ! {{ $t('entity.calendar.event.deleteDialog.subtitle') }}
</UiAlertDialogDescription> </UiAlertDialogDescription>
<form @submit.prevent="handleAction"> <form @submit.prevent="handleAction">
@@ -86,13 +86,13 @@ function handleCancel(): void {
<footer class="flex gap-2 justify-between"> <footer class="flex gap-2 justify-between">
<UiButton type="button" size="sm" variant="outline" @click="() => isDeleteEventModalOpen = false"> <UiButton type="button" size="sm" variant="outline" @click="() => isDeleteEventModalOpen = false">
Retour {{ $t('ui.action.back') }}
</UiButton> </UiButton>
<div class="flex gap-2 justify-end"> <div class="flex gap-2 justify-end">
<Transition name="fade-delay"> <Transition name="fade-delay">
<UiButton v-if="isLoading" type="button" size="sm" variant="destructive" @click.prevent="handleCancel"> <UiButton v-if="isLoading" type="button" size="sm" variant="destructive" @click.prevent="handleCancel">
Annuler {{ $t('ui.action.cancel') }}
</UiButton> </UiButton>
</Transition> </Transition>
@@ -101,7 +101,7 @@ function handleCancel(): void {
<PhCircleNotch v-if="isLoading" size="20" class="animate-spin"/> <PhCircleNotch v-if="isLoading" size="20" class="animate-spin"/>
</Transition> </Transition>
Supprimer {{ $t('ui.action.delete') }}
</UiButton> </UiButton>
</div> </div>
</footer> </footer>

View File

@@ -70,10 +70,12 @@ function handleCancel() {
@pointer-down-outside="(e) => e.preventDefault()" @pointer-down-outside="(e) => e.preventDefault()"
> >
<header class="pl-8 grid gap-y-2"> <header class="pl-8 grid gap-y-2">
<UiDialogTitle> Modifier l'évènement</UiDialogTitle> <UiDialogTitle>
{{ $t('entity.calendar.event.editDialog.title') }}
</UiDialogTitle>
<UiDialogDescription> <UiDialogDescription>
Mettre à jour les données de l'évènement {{ $t('entity.calendar.event.editDialog.subtitle') }}
</UiDialogDescription> </UiDialogDescription>
</header> </header>
@@ -89,7 +91,7 @@ function handleCancel() {
type="text" type="text"
name="new-event-title" name="new-event-title"
required required
placeholder="Titre de l'évènement" :placeholder="$t('entity.calendar.event.title')"
class="w-full -my-1 py-1 -mx-1 px-1 text-lg border-b-[1px] bg-transparent focus-visible:outline-none focus-visible:border-blue-600" class="w-full -my-1 py-1 -mx-1 px-1 text-lg border-b-[1px] bg-transparent focus-visible:outline-none focus-visible:border-blue-600"
> >
</div> </div>
@@ -100,7 +102,7 @@ function handleCancel() {
id="new-event-description" id="new-event-description"
v-model="eventSkeleton.description" v-model="eventSkeleton.description"
name="new-event-description" name="new-event-description"
placeholder="Ajouter une description" :placeholder="$t('entity.addDescription')"
class="w-full -my-1 py-1 -mx-1 px-1 min-h-24 max-h-36 text-sm border-b-[1px] bg-transparent focus-visible:outline-none focus-visible:border-blue-600" class="w-full -my-1 py-1 -mx-1 px-1 min-h-24 max-h-36 text-sm border-b-[1px] bg-transparent focus-visible:outline-none focus-visible:border-blue-600"
/> />
</div> </div>
@@ -111,7 +113,7 @@ function handleCancel() {
<CalendarInputRPGDate <CalendarInputRPGDate
v-model:model-value="eventSkeleton.startDate" v-model:model-value="eventSkeleton.startDate"
placeholder="Date de début" :placeholder="$t('entity.calendar.date.start')"
:initial-date="lastActiveEvent?.startDate" :initial-date="lastActiveEvent?.startDate"
:required="true" :required="true"
/> />
@@ -120,7 +122,7 @@ function handleCancel() {
<CalendarInputRPGDate <CalendarInputRPGDate
v-model:model-value="eventSkeleton.endDate" v-model:model-value="eventSkeleton.endDate"
placeholder="Date de fin" :placeholder="$t('entity.calendar.date.end')"
:initial-date="lastActiveEvent?.endDate" :initial-date="lastActiveEvent?.endDate"
/> />
</div> </div>
@@ -141,7 +143,7 @@ function handleCancel() {
<PhTag size="18" weight="fill" /> <PhTag size="18" weight="fill" />
<div class="w-1/2"> <div class="w-1/2">
<CalendarInputEventCategories v-model="eventSkeleton.secondaryCategories" placeholder="Ajouter des catégories secondaires" /> <CalendarInputEventCategories v-model="eventSkeleton.secondaryCategories" :placeholder="Ajouter des catégories secondaires" />
</div> </div>
</div> </div>
</div> --> </div> -->
@@ -155,7 +157,7 @@ function handleCancel() {
v-model="eventSkeleton.location" v-model="eventSkeleton.location"
type="text" type="text"
name="new-event-location" name="new-event-location"
placeholder="Ajouter un endroit" :placeholder="$t('entity.calendar.event.addLocation')"
class="w-full -my-1 py-2 px-2 text-sm border-b-[1px] bg-transparent focus-visible:outline-none focus-visible:border-blue-600"> class="w-full -my-1 py-2 px-2 text-sm border-b-[1px] bg-transparent focus-visible:outline-none focus-visible:border-blue-600">
</div> </div>
</div> </div>
@@ -169,10 +171,10 @@ function handleCancel() {
<UiSwitch id="new-event-visibility" v-model:checked="eventSkeleton.hidden" /> <UiSwitch id="new-event-visibility" v-model:checked="eventSkeleton.hidden" />
<UiLabel for="new-event-visibility"> <UiLabel for="new-event-visibility">
<template v-if="!eventSkeleton.hidden"> <template v-if="!eventSkeleton.hidden">
Évènement visible par tous {{ $t('entity.calendar.event.isPublic') }}
</template> </template>
<template v-else> <template v-else>
Évènement caché {{ $t('entity.calendar.event.isHidden') }}
</template> </template>
</UiLabel> </UiLabel>
</div> </div>
@@ -189,7 +191,7 @@ function handleCancel() {
<footer class="flex gap-2 justify-end"> <footer class="flex gap-2 justify-end">
<Transition name="fade-delay"> <Transition name="fade-delay">
<UiButton v-if="isLoading" type="button" size="sm" variant="destructive" @click.prevent="handleCancel"> <UiButton v-if="isLoading" type="button" size="sm" variant="destructive" @click.prevent="handleCancel">
Annuler {{ $t('ui.action.cancel') }}
</UiButton> </UiButton>
</Transition> </Transition>
@@ -198,7 +200,7 @@ function handleCancel() {
<PhCircleNotch v-if="isLoading" size="20" class="animate-spin"/> <PhCircleNotch v-if="isLoading" size="20" class="animate-spin"/>
</Transition> </Transition>
Enregistrer {{ $t('ui.action.save') }}
</UiButton> </UiButton>
</footer> </footer>
</form> </form>

View File

@@ -60,8 +60,8 @@ const filteredCategories = computed(() =>
class="w-fit h-[33vh] p-0" class="w-fit h-[33vh] p-0"
> >
<UiCommand v-model="modelBuffer" v-model:searchTerm="searchTerm" :multiple="true"> <UiCommand v-model="modelBuffer" v-model:searchTerm="searchTerm" :multiple="true">
<UiCommandInput placeholder="Rechercher les catégories" /> <UiCommandInput :placeholder="$t('entity.category.search')" />
<UiCommandEmpty>Aucune catégorie trouvée.</UiCommandEmpty> <UiCommandEmpty>{{ $t('entity.category.notFoundAny') }}</UiCommandEmpty>
<UiCommandList> <UiCommandList>
<UiCommandGroup> <UiCommandGroup>
<UiCommandItem <UiCommandItem

View File

@@ -52,8 +52,8 @@ const filteredCategories = computed(() =>
class="w-fit h-[33vh] p-0" class="w-fit h-[33vh] p-0"
> >
<UiCommand v-model="model" v-model:searchTerm="searchTerm"> <UiCommand v-model="model" v-model:searchTerm="searchTerm">
<UiCommandInput placeholder="Rechercher les catégories" /> <UiCommandInput :placeholder="$t('entity.category.search')" />
<UiCommandEmpty>Aucune catégorie trouvée.</UiCommandEmpty> <UiCommandEmpty>{{ $t('entity.category.notFoundAny') }}</UiCommandEmpty>
<UiCommandList> <UiCommandList>
<UiCommandGroup> <UiCommandGroup>
<UiCommandItem <UiCommandItem

View File

@@ -67,10 +67,28 @@ useSortable(monthSortableList, model.value, { animation: 150, handle: ".handle"
<template> <template>
<div class="grid md:grid-cols-12 gap-4 items-center"> <div class="grid md:grid-cols-12 gap-4 items-center">
<div class="md:col-start-2 md:col-span-5"> <div class="md:col-start-2 md:col-span-5">
<UiInput id="new-month-name" ref="monthNameRef" v-model="monthName" type="text" name="newMonthName" placeholder="Nom du mois" :class="cn({ 'border-red-600': monthNameIsTaken })"/> <UiInput
id="new-month-name"
ref="monthNameRef"
v-model="monthName"
type="text"
name="newMonthName"
:placeholder="$t('entity.calendar.months.inputName')"
:class="cn({ 'border-red-600': monthNameIsTaken })"
/>
</div> </div>
<div class="md:col-span-5"> <div class="md:col-span-5">
<UiInput id="new-month-days" ref="monthDaysRef" v-model="monthDays" type="number" name="newMonthName" placeholder="Nombre de jours" min="0" step="1" class="invalid:border-red-600" /> <UiInput
id="new-month-days"
ref="monthDaysRef"
v-model="monthDays"
type="number"
name="newMonthName"
:placeholder="$t('entity.calendar.months.daysNb')"
min="0"
step="1"
class="invalid:border-red-600"
/>
</div> </div>
<div class="md:col-span-1"> <div class="md:col-span-1">
<UiButton size="icon" class="rounded-full h-8 w-8" :disabled="!validNewMonth" @click.prevent="addMonthToModel"> <UiButton size="icon" class="rounded-full h-8 w-8" :disabled="!validNewMonth" @click.prevent="addMonthToModel">
@@ -78,7 +96,10 @@ useSortable(monthSortableList, model.value, { animation: 150, handle: ".handle"
</UiButton> </UiButton>
</div> </div>
<div class="md:col-span-full"> <div class="md:col-span-full">
<div class="border-[1px] dark:border-slate-800 p-4 rounded-sm max-h-80 overflow-y-auto" :class="model.length ? 'md:grid md:grid-cols-12 md:gap-4 md:items-center' : ''"> <div
class="border-[1px] dark:border-slate-800 p-4 rounded-sm max-h-80 overflow-y-auto"
:class="model.length ? 'md:grid md:grid-cols-12 md:gap-4 md:items-center' : ''"
>
<div v-if="model.length" class="hidden md:block col-span-1"> <div v-if="model.length" class="hidden md:block col-span-1">
<ul class="grid gap-y-4 justify-center"> <ul class="grid gap-y-4 justify-center">
<li v-for="(m, i) in model" :key="`num-${m.name}`"> <li v-for="(m, i) in model" :key="`num-${m.name}`">
@@ -88,6 +109,7 @@ useSortable(monthSortableList, model.value, { animation: 150, handle: ".handle"
</li> </li>
</ul> </ul>
</div> </div>
<div class="md:col-span-11"> <div class="md:col-span-11">
<ul ref="monthSortableList" class="grid gap-y-2" :class="model.length ? 'visible' : 'absolute invisible'"> <ul ref="monthSortableList" class="grid gap-y-2" :class="model.length ? 'visible' : 'absolute invisible'">
<template v-if="model.length"> <template v-if="model.length">
@@ -102,12 +124,23 @@ useSortable(monthSortableList, model.value, { animation: 150, handle: ".handle"
{{ m.name }} {{ m.name }}
</div> </div>
</div> </div>
<div class="md:col-span-2"> <div class="md:col-span-2">
<UiInput :id="`month-days-n${i}`" v-model="m.days" class="bg-transparent border-none" type="number" :name="`monthDays-n${i}`" placeholder="Nombre de jours" min="0" /> <UiInput
:id="`month-days-n${i}`"
v-model="m.days"
class="bg-transparent border-none"
type="number"
:name="`monthDays-n${i}`"
:placeholder="$t('entity.calendar.months.daysNb')"
min="0"
/>
</div> </div>
<div class="md:col-span-1"> <div class="md:col-span-1">
jour(s) {{ $t('entity.calendar.months.daysMaybePlural') }}
</div> </div>
<div class="md:col-start-12"> <div class="md:col-start-12">
<UiTooltipProvider> <UiTooltipProvider>
<UiTooltip> <UiTooltip>
@@ -117,7 +150,9 @@ useSortable(monthSortableList, model.value, { animation: 150, handle: ".handle"
</UiButton> </UiButton>
</UiTooltipTrigger> </UiTooltipTrigger>
<UiTooltipContent> <UiTooltipContent>
<p>Supprimer {{ m.name }} du calendrier</p> <p>
{{ $t('entity.calendar.months.deleteOne', { month: m.name }) }}
</p>
</UiTooltipContent> </UiTooltipContent>
</UiTooltip> </UiTooltip>
</UiTooltipProvider> </UiTooltipProvider>
@@ -127,7 +162,9 @@ useSortable(monthSortableList, model.value, { animation: 150, handle: ".handle"
</ul> </ul>
</div> </div>
<template v-if="!model.length"> <template v-if="!model.length">
<p class="col-span-12 text-lg text-center italic opacity-50">Aucun mois pour l'instant</p> <p class="col-span-12 text-lg text-center italic opacity-50">
{{ $t('entity.calendar.months.none') }}
</p>
</template> </template>
</div> </div>
</div> </div>

View File

@@ -33,7 +33,7 @@ watch(model.value, (n, _o) => {
<template> <template>
<div class="flex gap-2 items-stretch"> <div class="flex gap-2 items-stretch">
<UiInput id="new-month-current-day" v-model="model.day" type="number" name="newMonthCurrentDay" placeholder="Jour" min="1" step="1" class="invalid:border-red-600" /> <UiInput id="new-month-current-day" v-model="model.day" type="number" name="newMonthCurrentDay" :placeholder="$t('entity.calendar.months.daysNameSingular')" min="1" step="1" class="invalid:border-red-600" />
<UiDropdownMenu> <UiDropdownMenu>
<UiDropdownMenuTrigger as-child :disabled="props.availableMonths.length < 1"> <UiDropdownMenuTrigger as-child :disabled="props.availableMonths.length < 1">
@@ -41,18 +41,20 @@ watch(model.value, (n, _o) => {
<PhCalendarBlank size="18" weight="fill" /> <PhCalendarBlank size="18" weight="fill" />
<template v-if="props.availableMonths.length < 1"> <template v-if="props.availableMonths.length < 1">
Aucun mois disponible {{ $t('entity.calendar.months.noneAvailable') }}
</template> </template>
<template v-else-if="model.month && typeof model.month === 'number'"> <template v-else-if="model.month && typeof model.month === 'number'">
{{ props.availableMonths[model.month - 1].name }} {{ props.availableMonths[model.month - 1].name }}
</template> </template>
<template v-else> <template v-else>
Choisir le mois {{ $t('entity.calendar.months.chooseOne') }}
</template> </template>
</UiButton> </UiButton>
</UiDropdownMenuTrigger> </UiDropdownMenuTrigger>
<UiDropdownMenuContent :side="'bottom'" :collision-padding="30"> <UiDropdownMenuContent :side="'bottom'" :collision-padding="30">
<UiDropdownMenuLabel>Mois disponibles</UiDropdownMenuLabel> <UiDropdownMenuLabel>
{{ $t('entity.calendar.months.available') }}
</UiDropdownMenuLabel>
<UiDropdownMenuSeparator /> <UiDropdownMenuSeparator />
<UiDropdownMenuItem <UiDropdownMenuItem
v-for="m in props.availableMonths" v-for="m in props.availableMonths"
@@ -64,6 +66,6 @@ watch(model.value, (n, _o) => {
</UiDropdownMenuContent> </UiDropdownMenuContent>
</UiDropdownMenu> </UiDropdownMenu>
<UiInput id="new-month-current-day" v-model="model.year" type="number" name="newMonthCurrentYear" placeholder="Année" step="1" class="invalid:border-red-600" /> <UiInput id="new-month-current-day" v-model="model.year" type="number" name="newMonthCurrentYear" :placeholder="$t('entity.calendar.years.nameSingular')" step="1" class="invalid:border-red-600" />
</div> </div>
</template> </template>

View File

@@ -241,11 +241,11 @@ function handleCategorySelect(e: (Category)) {
}" }"
> >
<VisuallyHidden> <VisuallyHidden>
<UiDialogTitle> Recherche avancée </UiDialogTitle> <UiDialogTitle> {{ $t('entity.advancedSearch.title') }} </UiDialogTitle>
</VisuallyHidden> </VisuallyHidden>
<VisuallyHidden> <VisuallyHidden>
<UiDialogDescription> <UiDialogDescription>
Rechercher les données disponibles sur le calendrier {{ $t('entity.advancedSearch.title') }}
</UiDialogDescription> </UiDialogDescription>
</VisuallyHidden> </VisuallyHidden>
@@ -256,7 +256,7 @@ function handleCategorySelect(e: (Category)) {
id="search" id="search"
v-model:model-value="searchQuery" v-model:model-value="searchQuery"
type="text" type="text"
placeholder="Rechercher le calendrier" :placeholder="$t('entity.advancedSearch.ctaPlaceholder')"
class="pl-10 py-6 text-lg" class="pl-10 py-6 text-lg"
autocomplete="off" autocomplete="off"
/> />
@@ -274,10 +274,10 @@ function handleCategorySelect(e: (Category)) {
@update:model-value="handleEntitySwitch()" @update:model-value="handleEntitySwitch()"
> >
<UiToggleGroupItem value="events" aria-label="Uniquement les évènements"> <UiToggleGroupItem value="events" aria-label="Uniquement les évènements">
Évènements {{ $t('entity.calendar.event.namePlural') }}
</UiToggleGroupItem> </UiToggleGroupItem>
<UiToggleGroupItem value="characters" aria-label="Uniquement les personnages"> <UiToggleGroupItem value="characters" aria-label="Uniquement les personnages">
Personnages {{ $t('entity.character.namePlural') }}
</UiToggleGroupItem> </UiToggleGroupItem>
</UiToggleGroup> </UiToggleGroup>
</div> </div>
@@ -298,7 +298,7 @@ function handleCategorySelect(e: (Category)) {
class="w-full" class="w-full"
> >
<ComboboxAnchor as-child> <ComboboxAnchor as-child>
<ComboboxInput placeholder="Catégories" as-child> <ComboboxInput :placeholder="$t('entity.category.namePlural')" as-child>
<UiTagsInputInput <UiTagsInputInput
class="w-full px-3" class="w-full px-3"
:class="selectedCategories.length > 0 ? 'mt-2' : ''" :class="selectedCategories.length > 0 ? 'mt-2' : ''"
@@ -341,7 +341,7 @@ function handleCategorySelect(e: (Category)) {
</UiButton> </UiButton>
</UiTooltipTrigger> </UiTooltipTrigger>
<UiTooltipContent> <UiTooltipContent>
<p>Plus ancien</p> <p>{{ $t('entity.advancedSearch.older') }}</p>
</UiTooltipContent> </UiTooltipContent>
</UiTooltip> </UiTooltip>
</UiTooltipProvider> </UiTooltipProvider>
@@ -358,7 +358,7 @@ function handleCategorySelect(e: (Category)) {
</UiButton> </UiButton>
</UiTooltipTrigger> </UiTooltipTrigger>
<UiTooltipContent> <UiTooltipContent>
<p>Plus récent</p> <p>{{ $t('entity.advancedSearch.newer') }}</p>
</UiTooltipContent> </UiTooltipContent>
</UiTooltip> </UiTooltip>
</UiTooltipProvider> </UiTooltipProvider>

View File

@@ -16,9 +16,7 @@ defineEmits<{
const { getRelativeString, defaultDate, getFormattedDateTitle } = useCalendar() const { getRelativeString, defaultDate, getFormattedDateTitle } = useCalendar()
const dateDifference: string = getRelativeString(defaultDate, props.event.startDate) const dateDifference: string = getRelativeString(defaultDate, props.event.startDate)
const dateDuration: string | null = props.event.endDate const dateDuration = computed<string | null>(() => props.event.endDate ? getRelativeString(props.event.startDate, props.event.endDate, "compact") : null)
? getRelativeString(props.event.startDate, props.event.endDate, "compact")
: null
</script> </script>
<template> <template>
@@ -53,8 +51,14 @@ const dateDuration: string | null = props.event.endDate
</template> </template>
<template v-else> <template v-else>
<p class="font-semibold text-sm opacity-75"> <p class="font-semibold text-sm opacity-75">
Du {{ getFormattedDateTitle(event.startDate, true) }} au {{
{{ getFormattedDateTitle(event.endDate, true) }} $t('entity.calendar.date.fromTo',
{
startDate: getFormattedDateTitle(event.startDate, true),
endDate: getFormattedDateTitle(event.endDate, true)
}
)
}}
</p> </p>
</template> </template>
<div v-if="event.hidden" class="flex justify-end"> <div v-if="event.hidden" class="flex justify-end">
@@ -62,11 +66,13 @@ const dateDuration: string | null = props.event.endDate
<UiTooltip> <UiTooltip>
<UiTooltipTrigger as-child> <UiTooltipTrigger as-child>
<UiBadge class="flex gap-1"> <UiBadge class="flex gap-1">
<PhEye size="16" weight="fill" /> Évènement privé <PhEye size="16" weight="fill" /> {{ $t('entity.calendar.event.isHidden') }}
</UiBadge> </UiBadge>
</UiTooltipTrigger> </UiTooltipTrigger>
<UiTooltipContent> <UiTooltipContent>
<p>Cet évènement est uniquement visible pour vous</p> <p>
{{ $t('entity.calendar.event.hiddenTooltip') }}
</p>
</UiTooltipContent> </UiTooltipContent>
</UiTooltip> </UiTooltip>
</UiTooltipProvider> </UiTooltipProvider>
@@ -84,7 +90,7 @@ const dateDuration: string | null = props.event.endDate
</p> </p>
<template v-if="dateDuration"> <template v-if="dateDuration">
<p class="w-fit text-sm italic opacity-75 flex items-center gap-1"> <p class="w-fit text-sm italic opacity-75 flex items-center gap-1">
<PhHourglassMedium size="16" weight="fill" /> Pendant {{ dateDuration }} <PhHourglassMedium size="16" weight="fill" /> {{ $t('entity.calendar.date.while', { duration: dateDuration } )}}
</p> </p>
</template> </template>
</div> </div>

View File

@@ -34,7 +34,9 @@ function handleMenuItemAction(actionType: SidebarMenuActionType) {
</UiButton> </UiButton>
</UiTooltipTrigger> </UiTooltipTrigger>
<UiTooltipContent :side="'right'" :side-offset="6"> <UiTooltipContent :side="'right'" :side-offset="6">
<p>Mondes</p> <p>
{{ $t('entity.world.namePlural') }}
</p>
</UiTooltipContent> </UiTooltipContent>
</UiTooltip> </UiTooltip>
</UiTooltipProvider> </UiTooltipProvider>

View File

@@ -11,6 +11,8 @@ const user = useSupabaseUser()
const userMeta = computed(() => user.value?.user_metadata) const userMeta = computed(() => user.value?.user_metadata)
const profileUrl: string = `${useRequestURL().origin}/my/` const profileUrl: string = `${useRequestURL().origin}/my/`
const { locale, setLocale } = useI18n()
const menuOpened = ref<boolean>(false) const menuOpened = ref<boolean>(false)
function closeMenu() { function closeMenu() {
@@ -64,103 +66,140 @@ function pushRoute(to: AvailableRoutes) {
:alt="userMeta?.full_name" :alt="userMeta?.full_name"
referrerpolicy="no-referrer" referrerpolicy="no-referrer"
/> />
<UiAvatarFallback>Me</UiAvatarFallback> <UiAvatarFallback>
{{ $t('ui.sidebarMenu.avatarFallback') }}
</UiAvatarFallback>
</UiAvatar> </UiAvatar>
<UiButton v-else variant="outline" size="icon"> <UiButton v-else variant="outline" size="icon">
<PhUserCircle size="18" /> <PhUserCircle size="18" />
</UiButton> </UiButton>
</UiDropdownMenuTrigger> </UiDropdownMenuTrigger>
<UiDropdownMenuContent class="w-fit p-0" :align="'start'" :side="'top'" :side-offset="10" :align-offset="25" :collision-padding="40">
<UiDropdownMenuContent class="w-72 p-0" :align="'start'" :side="'top'" :side-offset="10" :align-offset="25" :collision-padding="40">
<template v-if="user"> <template v-if="user">
<p class="p-2 text-xs opacity-75">Connecté en tant que {{ user?.email }}</p> <p class="p-2 text-[.7em] opacity-75">
{{ $t('ui.greeting', { user: user?.email }) }}
</p>
<UiDropdownMenuItem class="flex gap-[.5ch] items-center rounded-none" @click="pushRoute('/my')"> <UiDropdownMenuItem class="flex gap-[.5ch] items-center rounded-none" @click="pushRoute('/my')">
<PhGlobeHemisphereWest size="20" weight="fill" /> <PhGlobeHemisphereWest size="20" weight="fill" />
<span>Mondes</span> <span>
{{ $t('entity.world.namePlural') }}
</span>
</UiDropdownMenuItem> </UiDropdownMenuItem>
<UiDropdownMenuSeparator /> <UiDropdownMenuSeparator />
</template>
<UiDropdownMenuSub> <UiDropdownMenuSub>
<UiDropdownMenuSubTrigger class="p-0 rounded-none"> <UiDropdownMenuSubTrigger class="p-0 rounded-none">
<UiDropdownMenuItem class="flex gap-[.5ch] items-center pointer-events-none"> <UiDropdownMenuItem class="flex gap-[.5ch] items-center pointer-events-none">
<PhPalette size="20" weight="fill" /> <PhPalette size="20" weight="fill" />
<span>Apparence</span> <span>
{{ $t('ui.sidebarMenu.appearance') }}
</span>
</UiDropdownMenuItem>
</UiDropdownMenuSubTrigger>
<UiDropdownMenuPortal>
<UiDropdownMenuSubContent>
<UiDropdownMenuItem
class="flex gap-[.5ch] items-center rounded-none transition-colors"
:class="cn({ 'text-emerald-600': $colorMode.preference === 'dark' })"
@select.prevent="$colorMode.preference = 'dark'"
>
<PhCheckCircle v-if="$colorMode.preference === 'dark'" size="20" weight="fill" />
<PhMoon v-else size="20" />
<span>
{{ $t('ui.dark') }}
</span>
</UiDropdownMenuItem> </UiDropdownMenuItem>
</UiDropdownMenuSubTrigger> <UiDropdownMenuItem
<UiDropdownMenuPortal> class="flex gap-[.5ch] items-center rounded-none transition-colors"
:class="cn({ 'text-emerald-600': $colorMode.preference === 'light' })"
@select.prevent="$colorMode.preference = 'light'"
>
<PhCheckCircle v-if="$colorMode.preference === 'light'" size="20" weight="fill" />
<PhSun v-else size="20" />
<span>
{{ $t('ui.light') }}
</span>
</UiDropdownMenuItem>
<UiDropdownMenuItem
class="flex gap-[.5ch] items-center rounded-none transition-colors"
:class="cn({ 'text-emerald-600': $colorMode.preference === 'system' })"
@select.prevent="$colorMode.preference = 'system'"
>
<PhCheckCircle v-if="$colorMode.preference === 'system'" size="20" weight="fill" />
<PhLaptop v-else size="20" />
<span>
{{ $t('ui.system') }}
</span>
</UiDropdownMenuItem>
</UiDropdownMenuSubContent>
</UiDropdownMenuPortal>
</UiDropdownMenuSub>
<UiDropdownMenuSub>
<UiDropdownMenuSubTrigger class="p-0 rounded-none">
<UiDropdownMenuItem class="flex gap-[.5ch] items-center pointer-events-none">
<PhTranslate size="20" />
<span>
{{ $t('ui.sidebarMenu.language') }}
</span>
</UiDropdownMenuItem>
</UiDropdownMenuSubTrigger>
<UiDropdownMenuPortal>
<UiDropdownMenuSubContent>
<UiDropdownMenuSubContent> <UiDropdownMenuSubContent>
<UiDropdownMenuItem <UiDropdownMenuItem
class="flex gap-[.5ch] items-center rounded-none" class="flex gap-[.5ch] items-center rounded-none transition-colors"
:class="cn({ 'text-emerald-600': $colorMode.preference === 'dark' })" :class="cn({ 'text-emerald-600': locale === 'fr' })"
@select.prevent="$colorMode.preference = 'dark'" @select.prevent="setLocale('fr')"
> >
<PhCheckCircle v-if="$colorMode.preference === 'dark'" size="20" weight="fill" /> <PhCheckCircle v-if="locale === 'fr'" size="20" weight="fill" />
<PhMoon v-else size="20" /> <img v-else src="/images/flag-fr.png" width="20" alt="" loading="eager">
<span>Sombre</span> <span>Français</span>
</UiDropdownMenuItem> </UiDropdownMenuItem>
<UiDropdownMenuItem <UiDropdownMenuItem
class="flex gap-[.5ch] items-center rounded-none" class="flex gap-[.5ch] items-center rounded-none transition-colors"
:class="cn({ 'text-emerald-600': $colorMode.preference === 'light' })" :class="cn({ 'text-emerald-600': locale === 'en' })"
@select.prevent="$colorMode.preference = 'light'" @select.prevent="setLocale('en')"
> >
<PhCheckCircle v-if="$colorMode.preference === 'light'" size="20" weight="fill" /> <PhCheckCircle v-if="locale === 'en'" size="20" weight="fill" />
<PhSun v-else size="20" /> <img v-else src="/images/flag-uk.png" width="20" alt="" loading="eager">
<span>Clair</span> <span>English</span>
</UiDropdownMenuItem>
<UiDropdownMenuItem
class="flex gap-[.5ch] items-center rounded-none"
:class="cn({ 'text-emerald-600': $colorMode.preference === 'system' })"
@select.prevent="$colorMode.preference = 'system'"
>
<PhCheckCircle v-if="$colorMode.preference === 'system'" size="20" weight="fill" />
<PhLaptop v-else size="20" />
<span>Système</span>
</UiDropdownMenuItem> </UiDropdownMenuItem>
</UiDropdownMenuSubContent> </UiDropdownMenuSubContent>
</UiDropdownMenuPortal> </UiDropdownMenuSubContent>
</UiDropdownMenuSub> </UiDropdownMenuPortal>
</UiDropdownMenuSub>
<UiDropdownMenuSub> <UiDropdownMenuSeparator />
<UiDropdownMenuSubTrigger class="p-0 rounded-none">
<UiDropdownMenuItem class="flex gap-[.5ch] items-center pointer-events-none">
<PhTranslate size="20" />
<span>Langage</span>
</UiDropdownMenuItem>
</UiDropdownMenuSubTrigger>
<UiDropdownMenuPortal>
<UiDropdownMenuSubContent>
<UiDropdownMenuSubContent>
<UiDropdownMenuItem class="flex gap-[.5ch] items-center rounded-none">
<span>Français</span>
</UiDropdownMenuItem>
<UiDropdownMenuItem class="flex gap-[.5ch] items-center rounded-none">
<span>English</span>
</UiDropdownMenuItem>
</UiDropdownMenuSubContent>
</UiDropdownMenuSubContent>
</UiDropdownMenuPortal>
</UiDropdownMenuSub>
<UiDropdownMenuSeparator />
<template v-if="user">
<UiDropdownMenuItem class="flex gap-[.5ch] items-center rounded-none" @click="pushRoute('/my/settings')"> <UiDropdownMenuItem class="flex gap-[.5ch] items-center rounded-none" @click="pushRoute('/my/settings')">
<PhGear size="20" weight="fill" /> <PhGear size="20" weight="fill" />
<span>Compte</span> <span>
{{ $t('ui.sidebarMenu.account') }}
</span>
</UiDropdownMenuItem> </UiDropdownMenuItem>
<UiDropdownMenuItem class="flex gap-[.5ch] items-center rounded-none" @click="handleLogout"> <UiDropdownMenuItem class="flex gap-[.5ch] items-center rounded-none" @click="handleLogout">
<PhSignOut size="20" weight="fill" /> <PhSignOut size="20" weight="fill" />
<span>Déconnexion</span> <span>
{{ $t('ui.sidebarMenu.logout') }}
</span>
</UiDropdownMenuItem> </UiDropdownMenuItem>
</template> </template>
<template v-else> <UiDropdownMenuItem v-if="!user" class="flex gap-[.5ch] items-center rounded-none" @click="handleGoogleLogin">
<UiDropdownMenuItem class="flex gap-[.5ch] items-center rounded-none" @click="handleGoogleLogin"> <PhGear size="20" weight="fill" />
<PhGear size="20" weight="fill" /> <span>
<span>Connexion</span> {{ $t('ui.sidebarMenu.login') }}
</UiDropdownMenuItem> </span>
</template> </UiDropdownMenuItem>
</UiDropdownMenuContent> </UiDropdownMenuContent>
</UiDropdownMenu> </UiDropdownMenu>
</ClientOnly> </ClientOnly>

340
i18n.config.ts Normal file
View File

@@ -0,0 +1,340 @@
export default defineI18nConfig(() => ({
legacy: false,
availableLocales: ["fr", "en"],
fallbackLocale: "fr",
messages: {
en: {
head: {
title: "Fantasy calendars for TTRPGs",
description: "Tools for players and game masters to help them visualize fantasy worlds better.",
},
ui: {
action: {
back: "Back",
cancel: "Cancel",
save: "Save",
delete: "Delete",
edit: "Edit",
},
greeting: "Connected as {user}",
sidebarMenu: {
appearance: "Appearance",
language: "Language",
account: "Account",
login: "Log in",
logout: "Log out",
avatarFallback: "Profile",
},
dark: "Dark",
light: "Light",
system: "System",
displayMode: "Display mode"
},
common: {
title: "Title"
},
entity: {
category: {
nameSingular: "Category",
namePlural: "Categories",
search: "Search categories",
notFoundAny: "No categories found.",
addPrimary: "Add a primary category",
addSecondaries: "Add secondary categories"
},
addDescription: "Add a description",
deleteOne: "Delete \"{entity}\"",
advancedSearch: {
title: "Advanced search",
subtitle: "Search through calendar and world data",
older: "Older",
newer: "Newer",
ctaPlaceholder: "Search an event, characters…"
},
world: {
nameSingular: "World",
namePlural: "Worlds",
},
calendar: {
nameSingular: "Calendar",
namePlural: "Calendars",
addSingle: "Add a calendar",
notFoundForWorld: "No calendar for this world… yet !",
isLoading: "Calendar is loading…",
date: {
start: "Start date",
end: "End date",
afterTomorrow: "In 2 days",
tomorrow: "Tomorrow",
today: "Today",
yesterday: "Yesterday",
beforeYesterday: "2 days ago",
fromTo: "From {startDate} to {endDate}",
while: "During {duration}",
xDaysNext: "In {days} days",
xMonthsNext: "In {months} month(s)",
xYearsNext: "In {years} year(s)",
xYearsAndMonthsNext: "In {years} year(s) and {months} month(s)",
xDaysNextCompact: "{days} days",
xMonthsNextCompact: "{months} month(s)",
xYearsNextCompact: "{years} year(s)",
xYearsAndMonthsNextCompact: "{years} year(s) and {months} month(s)",
xDaysAgo: "{days} days ago",
xMonthsAgo: "{months} month(s) ago",
xYearsAgo: "{years} year(s) ago",
xYearsAndMonthsAgo: "{years} year(s) and {months} month(s) ago",
xDaysAgoCompact: "{days} days",
xMonthsAgoCompact: "{months} month(s)",
xYearsAgoCompact: "{years} year(s)",
xYearsAndMonthsAgoCompact: "{years} year(s) and {months} month(s)",
},
event: {
nameSingular: "Event",
namePlural: "Events",
title: "Event title",
isStart: "Start",
isEnd: "End",
isHidden: "Hidden event",
isPublic: "Public event",
hiddenTooltip: "This event is visible only to game masters.",
addLocation: "Add a place",
prevPage: "Previous page with events",
nextPage: "Next page with events",
editDialog: {
title: "Edit event",
subtitle: "Update event data",
},
deleteDialog: {
title: "Delete this event",
subtitle: "Data associated with this event will be lost and you won't be able to retrieve it !",
}
},
createDialog: {
title: "New calendar",
tabs: {
general: {
title: "General",
},
months: {
title: "Months",
},
today: {
title: "Today",
},
}
},
deleteDialog: {
title: "Are you sure you want to delete this calendar ?",
subtitle: "Its events won't be accessible anymore and you won't be able to retrieve the deleted data !",
},
millennia: {
nameSingular: "Millennia",
nextSingular: "Next millennia",
prevSingular: "Last millennia",
},
centuries: {
nameSingular: "Century",
nextSingular: "Next century",
prevSingular: "Last century",
},
decades: {
nameSingular: "Decade",
nextSingular: "Next decade",
prevSingular: "Last decade",
},
years: {
nameSingular: "Year",
nextSingular: "Next year",
prevSingular: "Last year",
},
months: {
nameSingular: "Month",
nextSingular: "Next month",
prevSingular: "Last month",
inputName: "Month's name",
daysNb: "Number of days",
daysNameSingular: "Day",
daysNamePlural: "Days",
daysMaybePlural: "day(s)",
available: "Months available",
none: "No months for now",
noneAvailable: "No months available",
chooseOne: "Choose a month",
deleteOne: "Remove {month} from this calendar"
}
},
character: {
nameSingular: "Character",
namePlural: "Characters",
}
}
},
fr: {
head: {
title: "Calendriers fantasies pour JDR",
description: "Outils destinés aux joueurs et maîtres de jeux pour visualiser plus facilement leurs univers.",
},
ui: {
action: {
back: "Retour",
cancel: "Annuler",
save: "Sauvegarder",
delete: "Supprimer",
edit: "Modifier",
},
greeting: "Connecté en tant que {user}",
sidebarMenu: {
appearance: "Apparence",
language: "Langue",
account: "Compte",
login: "Connexion",
logout: "Déconnexion",
avatarFallback: "Profil",
},
dark: "Sombre",
light: "Clair",
system: "Système",
displayMode: "Mode d'affichage",
},
common: {
title: "Titre"
},
entity: {
category: {
nameSingular: "Catégorie",
namePlural: "Catégories",
search: "Rechercher les catégories",
notFoundAny: "Aucune catégorie trouvée.",
addPrimary: "Ajouter une catégorie principale",
addSecondaries: "Ajouter des catégories secondaires"
},
addDescription: "Ajouter une description",
deleteOne: "Supprimer \"{entity}\"",
advancedSearch: {
title: "Recherche avancée",
subtitle: "Rechercher les données disponibles sur le calendrier",
older: "Plus ancien",
newer: "Plus récent",
ctaPlaceholder: "Rechercher un évènement, personnage…"
},
world: {
nameSingular: "Monde",
namePlural: "Mondes",
},
calendar: {
nameSingular: "Calendriers",
namePlural: "Calendrier",
addSingle: "Ajouter un calendrier",
notFoundForWorld: "Aucun calendrier pour ce monde… pour l'instant !",
isLoading: "Chargement du calendrier…",
date: {
start: "Date de début",
end: "Date de fin",
afterTomorrow: "Après-demain",
tomorrow: "Demain",
today: "Aujourd'hui",
yesterday: "Hier",
beforeYesterday: "Avant-hier",
fromTo: "Du {startDate} au {endDate}",
while: "Pendant {duration}",
xDaysNext: "Dans {days} jours",
xMonthsNext: "Dans {months} mois",
xYearsNext: "Dans {years} an(s)",
xYearsAndMonthsNext: "Dans {years} an(s) et {months} mois",
xDaysNextCompact: "{days} jours",
xMonthsNextCompact: "{months} mois",
xYearsNextCompact: "{years} an(s)",
xYearsAndMonthsNextCompact: "{years} an(s) et {months} mois",
xDaysAgo: "Il y a {days} jours",
xMonthsAgo: "Il y a {months} mois",
xYearsAgo: "Il y a {years} an(s)",
xYearsAndMonthsAgo: "Il y a {years} an(s) et {months} mois",
xDaysAgoCompact: "{days} jours",
xMonthsAgoCompact: "{months} mois",
xYearsAgoCompact: "{years} an(s)",
xYearsAndMonthsAgoCompact: "{years} an(s) et {months} mois",
},
event: {
nameSingular: "Évènement",
namePlural: "Évènements",
title: "Titre de l'évènement",
isStart: "Début",
isEnd: "Fin",
isHidden: "Évènement privé",
isPublic: "Évènement public",
hiddenTooltip: "Cet évènement est uniquement visible pour les maîtres du jeu.",
addLocation: "Ajouter un endroit",
prevPage: "Précédente page à évènements",
nextPage: "Prochaine page à évènements",
editDialog: {
title: "Modifier l'évènement",
subtitle: "Mettre à jour les données de l'évènement",
},
deleteDialog: {
title: "Supprimer l'évènement",
subtitle: "Les données associés à cet évènement seront supprimées et vous ne pourrez plus les récupérer !",
}
},
createDialog: {
title: "Nouveau calendrier",
tabs: {
general: {
title: "Général",
},
months: {
title: "Mois de l'année",
},
today: {
title: "Aujourd'hui",
},
}
},
deleteDialog: {
title: "Êtes-vous sûr de supprimer ce calendrier ?",
subtitle: "Les évènements ne seront plus accessibles et vous ne pourrez plus récupérer les données !",
},
millennia: {
nameSingular: "Millénaire",
nextSingular: "Millénaire suivant",
prevSingular: "Millénaire précédent",
},
centuries: {
nameSingular: "Siècle",
nextSingular: "Siècle suivant",
prevSingular: "Siècle précédent",
},
decades: {
nameSingular: "Décennie",
nextSingular: "Décennie suivante",
prevSingular: "Décennie précédente",
},
years: {
nameSingular: "Année",
nextSingular: "Année suivante",
prevSingular: "Année précédente",
},
months: {
nameSingular: "Mois",
nextSingular: "Mois suivant",
prevSingular: "Mois précédent",
inputName: "Nom du mois",
daysNb: "Nombre de jours",
daysNameSingular: "Jour",
daysNamePlural: "Jours",
daysMaybePlural: "jour(s)",
Singular: "Année",
available: "Mois disponibles",
none: "Aucun mois pour l'instant",
noneAvailable: "Aucun mois disponible",
chooseOne: "Choisir un mois",
deleteOne: "Supprimer {month} du calendrier"
}
},
character: {
nameSingular: "Personnage",
namePlural: "Personnages",
}
}
}
}
}))

View File

@@ -9,10 +9,21 @@ export default defineNuxtConfig({
"@nuxtjs/tailwindcss", "@nuxtjs/tailwindcss",
"@nuxtjs/color-mode", "@nuxtjs/color-mode",
"@nuxt/eslint", "@nuxt/eslint",
"shadcn-nuxt" "shadcn-nuxt",
"@nuxtjs/i18n"
], ],
css: ["~/assets/main.css"], css: ["~/assets/main.css"],
i18n: {
vueI18n: "./i18n.config.ts",
detectBrowserLanguage: {
useCookie: true,
cookieKey: "tttools_lang",
redirectOn: "all",
alwaysRedirect: true,
}
},
supabase: { supabase: {
redirectOptions: { redirectOptions: {
login: "/", login: "/",

View File

@@ -14,6 +14,7 @@
}, },
"dependencies": { "dependencies": {
"@nuxt/eslint": "^0.3.12", "@nuxt/eslint": "^0.3.12",
"@nuxtjs/i18n": "^8.5.1",
"@phosphor-icons/vue": "^2.2.1", "@phosphor-icons/vue": "^2.2.1",
"@pinia/nuxt": "^0.5.1", "@pinia/nuxt": "^0.5.1",
"@vueuse/core": "^10.9.0", "@vueuse/core": "^10.9.0",

View File

@@ -1,12 +1,18 @@
<script lang="ts" setup> <script lang="ts" setup>
useHead({ useHead({
title: "TTTools — Calendriers de JDR",
titleTemplate: null titleTemplate: null
}) })
</script> </script>
<template> <template>
<main class="p-8"> <main class="p-8">
<Head>
<Title>
TTTools {{ $t('head.title') }}
</Title>
<Meta name="description" :content="$t('head.description')" />
</Head>
<Heading>TTTools</Heading> <Heading>TTTools</Heading>
</main> </main>
</template> </template>

View File

@@ -23,27 +23,27 @@ const { data: catData, pending: catPending } = await useLazyFetch("/api/calendar
const cal = computed<Calendar>(() => calendarData?.value?.data as Calendar) const cal = computed<Calendar>(() => calendarData?.value?.data as Calendar)
const categories = computed<Category[]>(() => catData?.value?.data as Category[]) const categories = computed<Category[]>(() => catData?.value?.data as Category[])
if (cal.value) {
useHead({
title: cal.value.name
})
}
watch(cal, (n) => {
useHead({
title: n.name
})
})
</script> </script>
<template> <template>
<div v-if="calPending || catPending" class="h-full w-full grid place-items-center"> <div v-if="calPending || catPending" class="h-full w-full grid place-items-center">
<Head>
<Title>{{ $t("entity.calendar.nameSingular") }}</Title>
</Head>
<div class="grid gap-2 justify-items-center opacity-50"> <div class="grid gap-2 justify-items-center opacity-50">
<p>Chargement du calendrier</p> <p>
{{ $t('entity.calendar.isLoading') }}
</p>
<PhCircleNotch size="50" class="animate-spin"/> <PhCircleNotch size="50" class="animate-spin"/>
</div> </div>
</div> </div>
<Calendar v-else-if="cal && categories" :calendar-data="cal" :categories /> <div v-else-if="cal && categories" class="h-full w-full">
<Head>
<Title>{{ cal.name }}</Title>
</Head>
<Calendar :calendar-data="cal" :categories />
</div>
</template> </template>

View File

@@ -2,10 +2,6 @@
import type { RealtimeChannel } from "@supabase/supabase-js" import type { RealtimeChannel } from "@supabase/supabase-js"
import type { World } from "~/models/World"; import type { World } from "~/models/World";
useHead({
title: "Mondes"
})
const supabase = useSupabaseClient() const supabase = useSupabaseClient()
const { data: res } = await useFetch("/api/worlds/query") const { data: res } = await useFetch("/api/worlds/query")
@@ -85,12 +81,16 @@ onUnmounted(() => {
</script> </script>
<template> <template>
<main class="p-8 after:fill-red-400"> <main class="p-8 after:fill-red-400">
<Head>
<Title>{{ $t("entity.world.namePlural") }}</Title>
</Head>
<Heading>{{ user?.user_metadata.full_name }}</Heading> <Heading>{{ user?.user_metadata.full_name }}</Heading>
<section v-if="worlds" class="mt-4"> <section v-if="worlds" class="mt-4">
<h2 class="mb-4 text-lg font-bold"> <h2 class="mb-4 text-lg font-bold">
Mondes {{ $t('entity.world.namePlural') }}
</h2> </h2>
<ul class="grid lg:grid-cols-2 gap-2"> <ul class="grid lg:grid-cols-2 gap-2">

View File

@@ -12,20 +12,6 @@ const { data: res, pending } = await useFetch("/api/worlds/query", { query: { id
const world = ref<World>(res.value?.data as World) const world = ref<World>(res.value?.data as World)
if (world.value) {
useHead({
title: world.value.name
})
}
watch(world, (n) => {
if (n) {
useHead({
title: n.name
})
}
}, { deep: true })
definePageMeta({ definePageMeta({
middleware: ["auth-guard"] middleware: ["auth-guard"]
}) })
@@ -116,9 +102,17 @@ function hideDeleteModal() {
<template> <template>
<main class="p-8"> <main class="p-8">
<template v-if="pending"> <template v-if="pending">
<Head>
<Title>{{ $t("entity.world.namePlural") }}</Title>
</Head>
<Heading>Chargement...</Heading> <Heading>Chargement...</Heading>
</template> </template>
<template v-else-if="world"> <template v-else-if="world">
<Head>
<Title>{{ world.name }}</Title>
</Head>
<header class="lg:w-1/2 mb-8"> <header class="lg:w-1/2 mb-8">
<Spacing> <Spacing>
<Heading>{{ world.name }}</Heading> <Heading>{{ world.name }}</Heading>
@@ -130,7 +124,9 @@ function hideDeleteModal() {
<section> <section>
<Spacing size="lg"> <Spacing size="lg">
<div class="flex items-center gap-3"> <div class="flex items-center gap-3">
<Heading>Calendriers</Heading> <Heading>
{{ $t('entity.calendar.namePlural') }}
</Heading>
<UiTooltipProvider :delay-duration="250"> <UiTooltipProvider :delay-duration="250">
<UiTooltip> <UiTooltip>
@@ -140,7 +136,9 @@ function hideDeleteModal() {
</UiButton> </UiButton>
</UiTooltipTrigger> </UiTooltipTrigger>
<UiTooltipContent :side-offset="10"> <UiTooltipContent :side-offset="10">
<p>Ajouter un calendrier</p> <p>
{{ $t('entity.calendar.addSingle') }}
</p>
</UiTooltipContent> </UiTooltipContent>
</UiTooltip> </UiTooltip>
</UiTooltipProvider> </UiTooltipProvider>
@@ -168,7 +166,7 @@ function hideDeleteModal() {
</ul> </ul>
<template v-else> <template v-else>
<p class="pl-6 opacity-75 italic"> <p class="pl-6 opacity-75 italic">
Aucun calendrier pour ce monde {{ $t('entity.calendar.notFoundForWorld') }}
</p> </p>
</template> </template>
</Spacing> </Spacing>

599
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

BIN
public/images/flag-fr.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

BIN
public/images/flag-uk.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

@@ -24,7 +24,16 @@ type CalendarCurrentDate = {
currentDateTitle: ComputedRef<string> currentDateTitle: ComputedRef<string>
} }
type DateDirectionTranslationKeys = {
days: string
months: string
years: string
yearsAndMonths: string
}
export const useCalendar = defineStore("calendar", () => { export const useCalendar = defineStore("calendar", () => {
const { t } = useI18n()
/** /**
* Static calendar config * Static calendar config
*/ */
@@ -326,7 +335,7 @@ export const useCalendar = defineStore("calendar", () => {
function getViewTypeTitle(viewType: CalendarViewType): string { function getViewTypeTitle(viewType: CalendarViewType): string {
switch (viewType) { switch (viewType) {
case "year": case "year":
return "Année" return t("entity.calendar.years.nameSingular")
case "decade": case "decade":
return "Décennie" return "Décennie"
@@ -336,7 +345,7 @@ export const useCalendar = defineStore("calendar", () => {
case "month": case "month":
default: default:
return "Mois" return t("entity.calendar.months.nameSingular")
} }
} }
@@ -481,7 +490,31 @@ export const useCalendar = defineStore("calendar", () => {
const differenceInDays: number = getDifferenceInDays(baseDate, relativeDate) const differenceInDays: number = getDifferenceInDays(baseDate, relativeDate)
let output: string = "" let output: string = ""
let direction: "past" | "present" | "future" = "present" let direction: "past" | "present" | "future" = "present"
let directionPrefix: string = "" const futureKeys: DateDirectionTranslationKeys = {
days: "xDaysNext",
months: "xMonthsNext",
years: "xYearsNext",
yearsAndMonths: "xYearsAndMonthsNext",
}
const futureKeysCompact: DateDirectionTranslationKeys = {
days: "xDaysNextCompact",
months: "xMonthsNextCompact",
years: "xYearsNextCompact",
yearsAndMonths: "xYearsAndMonthsNextCompact",
}
const pastKeys: DateDirectionTranslationKeys = {
days: "xDaysAgo",
months: "xMonthsAgo",
years: "xYearsAgo",
yearsAndMonths: "xYearsAndMonthsAgo",
}
const pastKeysCompact: DateDirectionTranslationKeys = {
days: "xDaysAgoCompact",
months: "xMonthsAgoCompact",
years: "xYearsAgoCompact",
yearsAndMonths: "xYearsAndMonthsAgoCompact",
}
let directionKeys: DateDirectionTranslationKeys = pastKeys
// Check whether it's a past or future date // Check whether it's a past or future date
if (differenceInDays > 0) { if (differenceInDays > 0) {
@@ -491,31 +524,36 @@ export const useCalendar = defineStore("calendar", () => {
} }
if (formatting === "complex") { if (formatting === "complex") {
// Handle if it's the same date // Handle if it's the same date
if (direction === "present") { if (direction === "present") {
return "Aujourd'hui" return t("entity.calendar.date.today")
} }
if (differenceInDays === -2) { if (differenceInDays === -2) {
return "Avant-hier" return t("entity.calendar.date.beforeYesterday")
} }
if (differenceInDays === -1) { if (differenceInDays === -1) {
return "Hier" return t("entity.calendar.date.yesterday")
} }
if (differenceInDays === 1) { if (differenceInDays === 1) {
return "Demain" return t("entity.calendar.date.tomorrow")
} }
if (differenceInDays === 2) { if (differenceInDays === 2) {
return "Après-demain" return t("entity.calendar.date.afterTomorrow")
} }
// Get relevant prefix for the string // Get relevant prefix for the string
if (direction === "future") { if (direction === "future") {
directionPrefix = "Dans " directionKeys = futureKeys
} else if (direction === "past") { } else {
directionPrefix = "Il y a " directionKeys = pastKeys
}
} else {
// Get relevant prefix for the string (without the prefix)
if (direction === "future") {
directionKeys = futureKeysCompact
} else {
directionKeys = pastKeysCompact
} }
output += directionPrefix
} }
const isSameMonth = baseDate.month === relativeDate.month const isSameMonth = baseDate.month === relativeDate.month
@@ -544,11 +582,7 @@ export const useCalendar = defineStore("calendar", () => {
if (isSameMonth && isSameYear) { if (isSameMonth && isSameYear) {
dateAcc.day = futureDate.day - datePivot.day dateAcc.day = futureDate.day - datePivot.day
if (dateAcc.day === 1) { output = t(`entity.calendar.date.${directionKeys.days}`, { days: dateAcc.day })
output += ` ${dateAcc.day} jour`
} else {
output += ` ${dateAcc.day} jours`
}
return output return output
} }
@@ -557,7 +591,7 @@ export const useCalendar = defineStore("calendar", () => {
else if (isSameYear) { else if (isSameYear) {
dateAcc.month = futureDate.month - datePivot.month dateAcc.month = futureDate.month - datePivot.month
output += ` ${dateAcc.month} mois` output = t(`entity.calendar.date.${directionKeys.months}`, { months: dateAcc.month })
return output return output
} }
@@ -600,13 +634,13 @@ export const useCalendar = defineStore("calendar", () => {
const remainderMonths = dateAcc.month % monthsPerYear.value const remainderMonths = dateAcc.month % monthsPerYear.value
if (computedYear >= 1 && remainderMonths) { if (computedYear >= 1 && remainderMonths) {
output += ` ${computedYear} an(s) et ${remainderMonths} mois` output = t(`entity.calendar.date.${directionKeys.yearsAndMonths}`, { years: computedYear, months: remainderMonths })
} }
else if (computedYear >= 1 && !remainderMonths) { else if (computedYear >= 1 && !remainderMonths) {
output += ` ${computedYear} an(s)`; output = t(`entity.calendar.date.${directionKeys.years}`, { years: computedYear })
} }
else { else {
output += ` ${remainderMonths} mois` output = t(`entity.calendar.date.${directionKeys.months}`, { months: remainderMonths })
} }
return output return output