Impove Intl formatting (#5372)

* Improve Intl formatting

* Additional fixes

* Fixed formatters were not updated on locale change

* Fixed formatNumber was not updated on locale change

* Additional formatting and fixes after merge

* Run prepr:frontend

* Remove `'` in icon map

* Run `pnpm install`

* fix: lint + import

* Additional fixes

---------

Co-authored-by: Calum H. <calum@modrinth.com>
Co-authored-by: Calum H. (IMB11) <contact@cal.engineer>
This commit is contained in:
Jerozgen
2026-03-10 00:29:32 +03:00
committed by GitHub
parent 9b2f0c88cd
commit f62c60a681
88 changed files with 839 additions and 621 deletions

View File

@@ -613,7 +613,7 @@
<IntlFormatted
:message-id="messages.serversPromoPricing"
:values="{
price: formatPrice(locale, 500, 'USD', true),
price: formatPrice(500, 'USD', true),
}"
>
<template #small="{ children }">
@@ -945,7 +945,7 @@
{{
capitalizeString(
formatMessage(commonMessages.projectFollowers, {
count: formatNumber(project.followers, false),
count: project.followers,
}),
)
}}
@@ -953,7 +953,7 @@
</div>
<div
v-if="project.approved"
v-tooltip="$dayjs(project.approved).format('MMMM D, YYYY [at] h:mm A')"
v-tooltip="formatDateTime(project.approved)"
class="details-list__item"
>
<CalendarIcon aria-hidden="true" />
@@ -968,11 +968,7 @@
</div>
</div>
<div
v-else
v-tooltip="$dayjs(project.published).format('MMMM D, YYYY [at] h:mm A')"
class="details-list__item"
>
<div v-else v-tooltip="formatDateTime(project.published)" class="details-list__item">
<CalendarIcon aria-hidden="true" />
<div>
{{
@@ -983,7 +979,7 @@
<div
v-if="project.status === 'processing' && project.queued"
v-tooltip="$dayjs(project.queued).format('MMMM D, YYYY [at] h:mm A')"
v-tooltip="formatDateTime(project.queued)"
class="details-list__item"
>
<ScaleIcon aria-hidden="true" />
@@ -1000,7 +996,7 @@
<div
v-if="versions.length > 0 && project.updated"
v-tooltip="$dayjs(project.updated).format('MMMM D, YYYY [at] h:mm A')"
v-tooltip="formatDateTime(project.updated)"
class="details-list__item"
>
<VersionIcon aria-hidden="true" />
@@ -1099,17 +1095,13 @@ import {
StyledInput,
TagItem,
useDebugLogger,
useFormatDateTime,
useFormatPrice,
useRelativeTime,
useVIntl,
} from '@modrinth/ui'
import VersionSummary from '@modrinth/ui/src/components/version/VersionSummary.vue'
import {
capitalizeString,
formatNumber,
formatPrice,
formatProjectType,
renderString,
} from '@modrinth/utils'
import { capitalizeString, formatProjectType, renderString } from '@modrinth/utils'
import { useMutation, useQuery, useQueryClient } from '@tanstack/vue-query'
import { useLocalStorage } from '@vueuse/core'
import dayjs from 'dayjs'
@@ -1150,7 +1142,12 @@ const tags = useGeneratedState()
const flags = useFeatureFlags()
const cosmetics = useCosmetics()
const { locale, formatMessage } = useVIntl()
const { formatMessage } = useVIntl()
const formatPrice = useFormatPrice()
const formatDateTime = useFormatDateTime({
timeStyle: 'short',
dateStyle: 'long',
})
const debug = useDebugLogger('DownloadModal')

View File

@@ -50,7 +50,7 @@
</span>
<span>
on
{{ $dayjs(version.date_published).format('MMM D, YYYY') }}</span
{{ formatDate(version.date_published) }}</span
>
</div>
<a
@@ -86,12 +86,23 @@
</template>
<script setup>
import { DownloadIcon, SpinnerIcon } from '@modrinth/assets'
import { injectModrinthClient, injectProjectPageContext, Pagination } from '@modrinth/ui'
import {
injectModrinthClient,
injectProjectPageContext,
Pagination,
useFormatDateTime,
} from '@modrinth/ui'
import VersionFilterControl from '@modrinth/ui/src/components/version/VersionFilterControl.vue'
import { renderHighlightedString } from '@modrinth/utils'
import { useQuery } from '@tanstack/vue-query'
import { onMounted } from 'vue'
const formatDate = useFormatDateTime({
month: 'short',
day: 'numeric',
year: 'numeric',
})
const { projectV2, versions, versionsLoading, loadVersions } = injectProjectPageContext()
// Load versions on mount (client-side)

View File

@@ -264,7 +264,7 @@
<div class="gallery-bottom">
<div class="gallery-created">
<CalendarIcon aria-hidden="true" aria-label="Date created" />
{{ $dayjs(item.created).format('MMMM D, YYYY') }}
{{ formatDate(item.created) }}
</div>
<div v-if="currentMember" class="gallery-buttons input-group">
<button
@@ -341,11 +341,18 @@ import {
injectProjectPageContext,
NewModal as Modal,
StyledInput,
useFormatDateTime,
} from '@modrinth/ui'
import { useEventListener, useLocalStorage } from '@vueuse/core'
import { isPermission } from '~/utils/permissions.ts'
const formatDate = useFormatDateTime({
year: 'numeric',
month: 'long',
day: 'numeric',
})
// Router
const router = useRouter()

View File

@@ -235,7 +235,7 @@
<div class="gallery-bottom">
<div class="gallery-created">
<CalendarIcon aria-hidden="true" aria-label="Date created" />
{{ $dayjs(item.created).format('MMMM D, YYYY') }}
{{ formatDate(item.created) }}
</div>
<div v-if="currentMember" class="gallery-buttons input-group">
<button
@@ -300,10 +300,17 @@ import {
injectProjectPageContext,
NewModal as Modal,
StyledInput,
useFormatDateTime,
} from '@modrinth/ui'
import { isPermission } from '~/utils/permissions.ts'
const formatDate = useFormatDateTime({
year: 'numeric',
month: 'long',
day: 'numeric',
})
const {
projectV2: project,
currentMember,

View File

@@ -368,7 +368,7 @@
<div v-if="!isEditing">
<h4>Publication date</h4>
<span>
{{ $dayjs(version.date_published).format('MMMM D, YYYY [at] h:mm A') }}
{{ formatDateTime(version.date_published) }}
</span>
</div>
<div v-if="!isEditing && version.author">
@@ -437,6 +437,7 @@ import {
injectNotificationManager,
injectProjectPageContext,
StyledInput,
useFormatDateTime,
} from '@modrinth/ui'
import { formatBytes, renderHighlightedString } from '@modrinth/utils'
import { Multiselect } from 'vue-multiselect'
@@ -460,6 +461,11 @@ const auth = await useAuth()
const tags = useGeneratedState()
const flags = useFeatureFlags()
const { addNotification } = injectNotificationManager()
const formatDateTime = useFormatDateTime({
timeStyle: 'short',
dateStyle: 'long',
})
const formatDate = useFormatDateTime({ dateStyle: 'medium' })
// Helper for accessing nuxt app $formatVersion
const formatVersionDisplay = (versions: string[]) => (data as any).$formatVersion(versions)
@@ -697,9 +703,9 @@ const description = computed(
version.value.loaders ?? []
)
.map((x: string) => x.charAt(0).toUpperCase() + x.slice(1))
.join(' & ')}. Published on ${data
.$dayjs(version.value.date_published)
.format('MMM D, YYYY')}. ${version.value.downloads} downloads.`,
.join(
' & ',
)}. Published on ${formatDate(version.value.date_published)}. ${version.value.downloads} downloads.`,
)
const usesFeaturedVersions = computed(() =>

View File

@@ -169,7 +169,7 @@
</span>
<div class="mb-4 mt-2 flex w-full items-center gap-1 text-sm text-secondary">
{{ capitalizeString(subscription.interval) }} ⋅ {{ subscription.status }} ⋅
{{ dayjs(subscription.created).format('MMMM D, YYYY [at] h:mma') }} ({{
{{ formatDateTime(subscription.created) }} ({{
formatRelativeTime(subscription.created)
}})
</div>
@@ -239,7 +239,7 @@
</span>
<template v-if="charge.status !== 'cancelled'">
{{ formatPrice(vintl.locale, charge.amount, charge.currency_code) }}
{{ formatPrice(charge.amount, charge.currency_code) }}
</template>
</span>
<span class="text-sm text-secondary">
@@ -252,13 +252,13 @@
<span v-else-if="charge.status === 'cancelled'" class="font-bold">Ends:</span>
<span v-else-if="charge.type === 'refund'" class="font-bold">Issued:</span>
<span v-else class="font-bold">Due:</span>
{{ dayjs(charge.due).format('MMMM D, YYYY [at] h:mma') }}
{{ formatDateTime(charge.due) }}
<span class="text-secondary">({{ formatRelativeTime(charge.due) }}) </span>
</span>
<span v-if="charge.last_attempt != null" class="text-sm text-secondary">
<span v-if="charge.status === 'failed'" class="font-bold">Last attempt:</span>
<span v-else class="font-bold">Charged:</span>
{{ dayjs(charge.last_attempt).format('MMMM D, YYYY [at] h:mma') }}
{{ formatDateTime(charge.last_attempt) }}
<span class="text-secondary"
>({{ formatRelativeTime(charge.last_attempt) }})
</span>
@@ -268,9 +268,9 @@
{{ charge.type }}
{{ formatPrice(vintl.locale, charge.amount, charge.currency_code) }}
{{ formatPrice(charge.amount, charge.currency_code) }}
{{ dayjs(charge.due).format('YYYY-MM-DD h:mma') }}
{{ formatDateTimeShort(charge.due) }}
<template v-if="charge.subscription_interval">
⋅ {{ charge.subscription_interval }}
</template>
@@ -332,16 +332,30 @@ import {
NewModal,
StyledInput,
Toggle,
useFormatDateTime,
useFormatPrice,
useRelativeTime,
useVIntl,
} from '@modrinth/ui'
import { capitalizeString, formatPrice } from '@modrinth/utils'
import { capitalizeString } from '@modrinth/utils'
import { DEFAULT_CREDIT_EMAIL_MESSAGE } from '@modrinth/utils/utils.ts'
import dayjs from 'dayjs'
import ModrinthServersIcon from '~/components/ui/servers/ModrinthServersIcon.vue'
const { addNotification } = injectNotificationManager()
const formatPrice = useFormatPrice()
const formatDateTime = useFormatDateTime({
timeStyle: 'short',
dateStyle: 'long',
})
const formatDateTimeShort = useFormatDateTime({
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: 'numeric',
minute: 'numeric',
})
const route = useRoute()
const vintl = useVIntl()

View File

@@ -159,16 +159,13 @@
</div>
<div class="text-sm">
<span v-if="notice.announce_at">
{{ dayjs(notice.announce_at).format('MMM D, YYYY [at] h:mm A') }}
{{ formatDateTimeShortMonth(notice.announce_at) }}
({{ formatRelativeTime(notice.announce_at) }})
</span>
<template v-else> Never begins </template>
</div>
<div class="text-sm">
<span
v-if="notice.expires"
v-tooltip="dayjs(notice.expires).format('MMMM D, YYYY [at] h:mm A')"
>
<span v-if="notice.expires" v-tooltip="formatDateTime(notice.expires)">
{{ formatRelativeTime(notice.expires) }}
</span>
<template v-else> Never expires </template>
@@ -276,6 +273,7 @@ import {
StyledInput,
TagItem,
Toggle,
useFormatDateTime,
useRelativeTime,
useVIntl,
} from '@modrinth/ui'
@@ -290,6 +288,14 @@ import { useServersFetch } from '~/composables/servers/servers-fetch.ts'
const { addNotification } = injectNotificationManager()
const { formatMessage } = useVIntl()
const formatRelativeTime = useRelativeTime()
const formatDateTime = useFormatDateTime({
timeStyle: 'short',
dateStyle: 'long',
})
const formatDateTimeShortMonth = useFormatDateTime({
timeStyle: 'short',
dateStyle: 'medium',
})
const notices = ref<ServerNoticeType[]>([])
const createNoticeModal = ref<InstanceType<typeof NewModal>>()

View File

@@ -76,11 +76,11 @@
</div>
</div>
<div class="flex flex-wrap items-center gap-x-4 gap-y-1 text-sm text-secondary">
<span v-tooltip="dayjs(batch.created_at).format('MMMM D, YYYY [at] h:mm A')">
<span v-tooltip="formatDateTime(batch.created_at)">
Created {{ formatRelativeTime(batch.created_at) }}
</span>
<span></span>
<span v-tooltip="dayjs(batch.scheduled_at).format('MMMM D, YYYY [at] h:mm A')">
<span v-tooltip="formatDateTime(batch.scheduled_at)">
Scheduled {{ formatRelativeTime(batch.scheduled_at) }}
</span>
<template v-if="batch.provision_options?.region">
@@ -116,6 +116,7 @@ import {
injectNotificationManager,
Pagination,
TagItem,
useFormatDateTime,
useRelativeTime,
} from '@modrinth/ui'
import type { User } from '@modrinth/utils'
@@ -127,6 +128,10 @@ import { useServersFetch } from '~/composables/servers/servers-fetch.ts'
const { addNotification } = injectNotificationManager()
const formatRelativeTime = useRelativeTime()
const formatDateTime = useFormatDateTime({
timeStyle: 'short',
dateStyle: 'long',
})
// Types
interface ProvisionOptions {

View File

@@ -180,9 +180,11 @@
count: formatCompactNumber(projects?.length || 0),
type: formatMessage(
commonProjectTypeSentenceMessages[
projectTypes.length === 1 ? projectTypes[0] : 'project'
projectTypes.length === 1 && projects?.length > 1
? projectTypes[0]
: 'project'
],
{ count: projects?.length || 0 },
{ count: formatCompactNumberPlural(projects?.length || 0) },
),
}"
>
@@ -247,7 +249,7 @@
>
<div class="flex flex-col gap-2">
<span
v-tooltip="dayjs(collection.created).format('MMMM D, YYYY [at] h:mm A')"
v-tooltip="formatDateTime(collection.created)"
class="flex w-fit items-center gap-2"
>
<CalendarIcon aria-hidden="true" />
@@ -259,7 +261,7 @@
</span>
<span
v-if="showUpdatedDate"
v-tooltip="dayjs(collection.updated).format('MMMM D, YYYY [at] h:mm A')"
v-tooltip="formatDateTime(collection.updated)"
class="flex w-fit items-center gap-2"
>
<UpdatedIcon aria-hidden="true" />
@@ -410,6 +412,8 @@ import {
RadioButtons,
SidebarCard,
StyledInput,
useCompactNumber,
useFormatDateTime,
useRelativeTime,
useSavable,
useVIntl,
@@ -425,7 +429,11 @@ const { handleError } = injectNotificationManager()
const api = injectModrinthClient()
const { formatMessage } = useVIntl()
const formatRelativeTime = useRelativeTime()
const formatCompactNumber = useCompactNumber()
const { formatCompactNumber, formatCompactNumberPlural } = useCompactNumber()
const formatDateTime = useFormatDateTime({
timeStyle: 'short',
dateStyle: 'long',
})
const route = useNativeRoute()
const router = useRouter()
@@ -491,8 +499,7 @@ const messages = defineMessages({
},
projectsCountLabel: {
id: 'collection.label.projects-count',
defaultMessage:
'{count, plural, =0 {No projects yet} one {<stat>{count}</stat> project} other {<stat>{count}</stat> {type}}}',
defaultMessage: '{count, plural, =0 {No projects yet} other {<stat>{count}</stat> {type}}}',
},
removeProjectButton: {
id: 'collection.button.remove-project',

View File

@@ -63,6 +63,7 @@
{{
formatMessage(messages.projectsCountLabel, {
count: formatCompactNumber(user ? user.follows.length : 0),
countPlural: formatCompactNumberPlural(user ? user.follows.length : 0),
})
}}
</div>
@@ -91,6 +92,7 @@
{{
formatMessage(messages.projectsCountLabel, {
count: formatCompactNumber(collection.projects?.length || 0),
countPlural: formatCompactNumberPlural(collection.projects?.length || 0),
})
}}
</div>
@@ -154,13 +156,14 @@ import {
defineMessages,
DropdownSelect,
StyledInput,
useCompactNumber,
useVIntl,
} from '@modrinth/ui'
import CollectionCreateModal from '~/components/ui/create/CollectionCreateModal.vue'
const { formatMessage } = useVIntl()
const formatCompactNumber = useCompactNumber()
const { formatCompactNumber, formatCompactNumberPlural } = useCompactNumber()
const messages = defineMessages({
createNewButton: {
@@ -177,7 +180,7 @@ const messages = defineMessages({
},
projectsCountLabel: {
id: 'dashboard.collections.label.projects-count',
defaultMessage: '{count, plural, one {{count} project} other {{count} projects}}',
defaultMessage: '{count} {countPlural, plural, one {project} other {projects}}',
},
searchInputLabel: {
id: 'dashboard.collections.label.search-input',

View File

@@ -67,7 +67,7 @@
></span>
{{
formatMessage(messages.estimatedWithDate, {
date: date.date ? dayjs(date.date).format('MMM D, YYYY') : '',
date: date.date ? formatDate(date.date) : '',
})
}}
<Tooltip theme="dismissable-prompt" :triggers="['hover', 'focus']" no-auto-focus>
@@ -260,8 +260,7 @@
<script setup lang="ts">
import { ArrowUpRightIcon, InProgressIcon, UnknownIcon } from '@modrinth/assets'
import { defineMessages, useVIntl } from '@modrinth/ui'
import { formatMoney } from '@modrinth/utils'
import { defineMessages, useFormatDateTime, useFormatMoney, useVIntl } from '@modrinth/ui'
import dayjs from 'dayjs'
import { Tooltip } from 'floating-vue'
@@ -271,6 +270,8 @@ import CreatorWithdrawModal from '~/components/ui/dashboard/CreatorWithdrawModal
import RevenueTransaction from '~/components/ui/dashboard/RevenueTransaction.vue'
const { formatMessage } = useVIntl()
const formatMoney = useFormatMoney()
const formatDate = useFormatDateTime({ dateStyle: 'medium' })
await useAuth()

View File

@@ -90,8 +90,15 @@ import {
GenericListIcon,
SpinnerIcon,
} from '@modrinth/assets'
import { ButtonStyled, Combobox, defineMessages, useVIntl } from '@modrinth/ui'
import { formatMoney } from '@modrinth/utils'
import {
ButtonStyled,
Combobox,
defineMessages,
useFormatDateTime,
useFormatMoney,
useVIntl,
} from '@modrinth/ui'
import { capitalizeString } from '@modrinth/utils'
import dayjs from 'dayjs'
import RevenueTransaction from '~/components/ui/dashboard/RevenueTransaction.vue'
@@ -99,6 +106,12 @@ import { useGeneratedState } from '~/composables/generated'
import { findRail } from '~/utils/muralpay-rails'
const { formatMessage } = useVIntl()
const formatMoney = useFormatMoney()
const formatMonth = useFormatDateTime({
year: 'numeric',
month: 'long',
})
const generatedState = useGeneratedState()
useHead({
@@ -152,7 +165,7 @@ function getPeriodLabel(date) {
} else if (txnDate.isSame(now.subtract(1, 'month'), 'month')) {
return 'Last month'
} else {
return txnDate.format('MMMM YYYY')
return capitalizeString(formatMonth(txnDate.toDate()))
}
}

View File

@@ -616,7 +616,7 @@
<p v-if="lowestPrice" class="m-0 text-sm">
{{
formatMessage(messages.startingAtPrice, {
price: formatPrice(locale, lowestPrice, selectedCurrency, true),
price: formatPrice(lowestPrice, selectedCurrency, true),
})
}}
</p>
@@ -644,10 +644,10 @@ import {
injectNotificationManager,
IntlFormatted,
ModrinthServersPurchaseModal,
useFormatPrice,
useVIntl,
} from '@modrinth/ui'
import { monthsInInterval } from '@modrinth/ui/src/utils/billing.ts'
import { formatPrice } from '@modrinth/utils'
import { computed } from 'vue'
import { useBaseFetch } from '@/composables/fetch.js'
@@ -678,7 +678,8 @@ if (affiliateCode.value) {
}
const { addNotification } = injectNotificationManager()
const { locale, formatMessage } = useVIntl()
const { formatMessage } = useVIntl()
const formatPrice = useFormatPrice()
const flags = useFeatureFlags()
const messages = defineMessages({

View File

@@ -75,7 +75,7 @@
<div class="section-header">
<div class="section-label green">{{ formatMessage(messages.forPlayersLabel) }}</div>
<h2 class="section-tagline">
{{ formatMessage(messages.discoverCreationsTagline, { count: formattedProjectCount }) }}
{{ formatMessage(messages.discoverCreationsTagline, { count: PROJECT_COUNT }) }}
</h2>
<p class="section-description">
{{ formatMessage(messages.playersDescription) }}
@@ -466,8 +466,6 @@ const searchQuery = ref('leave')
const sortType = ref('relevance')
const PROJECT_COUNT = 100000
const formatNumber = new Intl.NumberFormat().format
const formattedProjectCount = computed(() => formatNumber(PROJECT_COUNT))
const auth = await useAuth()
@@ -526,7 +524,7 @@ const messages = defineMessages({
},
discoverCreationsTagline: {
id: 'landing.section.for-players.tagline',
defaultMessage: 'Discover over {count} creations',
defaultMessage: 'Discover over {count, number} creations',
},
shareContentTagline: {
id: 'landing.section.for-creators.tagline',

View File

@@ -111,7 +111,7 @@
</tr>
<tr>
<td>End of the month</td>
<td>{{ formatDate(endOfMonthDate) }}</td>
<td>{{ formatDate(endOfMonthDate.toDate()) }}</td>
</tr>
<tr>
<td>NET 60 policy applied</td>
@@ -119,7 +119,7 @@
</tr>
<tr class="final-result">
<td>Available for withdrawal</td>
<td>{{ formatDate(withdrawalDate) }}</td>
<td>{{ formatDate(withdrawalDate.toDate()) }}</td>
</tr>
</tbody>
</table>
@@ -162,11 +162,17 @@
</template>
<script lang="ts" setup>
import { StyledInput } from '@modrinth/ui'
import { formatDate, formatMoney } from '@modrinth/utils'
import { StyledInput, useFormatDateTime, useFormatMoney } from '@modrinth/ui'
import dayjs from 'dayjs'
import { computed, ref } from 'vue'
const formatMoney = useFormatMoney()
const formatDate = useFormatDateTime({
month: 'long',
day: 'numeric',
year: 'numeric',
})
const description =
'Information about the Rewards Program of Modrinth, an open source modding platform focused on Minecraft.'

View File

@@ -1,7 +1,7 @@
<script setup lang="ts">
import { GitGraphIcon, RssIcon } from '@modrinth/assets'
import { articles as rawArticles } from '@modrinth/blog'
import { Avatar, ButtonStyled } from '@modrinth/ui'
import { Avatar, ButtonStyled, useFormatDateTime } from '@modrinth/ui'
import type { User } from '@modrinth/utils'
import dayjs from 'dayjs'
import { computed, onMounted } from 'vue'
@@ -12,6 +12,8 @@ import ShareArticleButtons from '~/components/ui/ShareArticleButtons.vue'
const config = useRuntimeConfig()
const route = useRoute()
const formatDate = useFormatDateTime({ dateStyle: 'long' })
const rawArticle = rawArticles.find((article) => article.slug === route.params.slug)
if (!rawArticle) {
@@ -157,10 +159,10 @@ onMounted(() => {
</nuxt-link>
</template>
<span class="hidden md:block"></span>
<span class="hidden md:block"> {{ dayjsDate.format('MMMM D, YYYY') }}</span>
<span class="hidden md:block"> {{ formatDate(dayjsDate.toDate()) }}</span>
</div>
<span class="text-sm text-secondary sm:text-base md:hidden">
Posted on {{ dayjsDate.format('MMMM D, YYYY') }}</span
Posted on {{ formatDate(dayjsDate.toDate()) }}</span
>
<ShareArticleButtons :title="article.title" :url="articleUrl" />
<img

View File

@@ -1,12 +1,13 @@
<script setup lang="ts">
import { ChevronRightIcon, GitGraphIcon, RssIcon } from '@modrinth/assets'
import { articles as rawArticles } from '@modrinth/blog'
import { ButtonStyled, NewsArticleCard } from '@modrinth/ui'
import dayjs from 'dayjs'
import { ButtonStyled, NewsArticleCard, useFormatDateTime } from '@modrinth/ui'
import { computed, ref } from 'vue'
import NewsletterButton from '~/components/ui/NewsletterButton.vue'
const formatDate = useFormatDateTime({ dateStyle: 'long' })
const articles = ref(
rawArticles
.map((article) => ({
@@ -83,7 +84,7 @@ useSeoMeta({
</h3>
<p class="m-0 text-lg leading-tight">{{ featuredArticle?.summary }}</p>
<div class="mt-auto text-secondary">
{{ dayjs(featuredArticle?.date).format('MMMM D, YYYY') }}
{{ formatDate(featuredArticle?.date) }}
</div>
</div>
</article>

View File

@@ -19,7 +19,7 @@
</nuxt-link>
</h2>
<span>
{{ formatNumber(acceptedMembers?.length || 0) }}
{{ formatCompactNumber(acceptedMembers?.length || 0) }}
member<template v-if="acceptedMembers?.length !== 1">s</template>
</span>
</div>
@@ -89,7 +89,7 @@
projects
</div>
<div
v-tooltip="sumDownloads.toLocaleString()"
v-tooltip="formatNumber(sumDownloads)"
class="flex items-center gap-2 font-semibold"
>
<DownloadIcon class="h-6 w-6 text-secondary" />
@@ -293,10 +293,11 @@ import {
OverflowMenu,
ProjectCard,
ProjectCardList,
useCompactNumber,
useFormatNumber,
useVIntl,
} from '@modrinth/ui'
import type { Organization, ProjectStatus, ProjectType } from '@modrinth/utils'
import { formatNumber } from '@modrinth/utils'
import UpToDate from '~/assets/images/illustrations/up_to_date.svg?component'
import AdPlaceholder from '~/components/ui/AdPlaceholder.vue'
@@ -318,7 +319,8 @@ type ProjectV3 = Labrinth.Projects.v3.Project & {
const vintl = useVIntl()
const { formatMessage } = vintl
const formatCompactNumber = useCompactNumber(true)
const formatNumber = useFormatNumber()
const { formatCompactNumber } = useCompactNumber()
const auth: { user: any } & any = await useAuth()
const user = await useUser()

View File

@@ -31,7 +31,7 @@
anytime.
</p>
<p class="m-0 text-[2rem] font-bold text-purple">
{{ formatPrice(vintl.locale, price.prices.intervals.monthly, price.currency_code) }}/mo
{{ formatPrice(price.prices.intervals.monthly, price.currency_code) }}/mo
</p>
<p class="m-0 mb-4 text-secondary">
or save
@@ -86,14 +86,15 @@
</template>
<script setup>
import { HeartIcon, ModrinthPlusIcon, SettingsIcon, SparklesIcon, StarIcon } from '@modrinth/assets'
import { injectNotificationManager, PurchaseModal, useVIntl } from '@modrinth/ui'
import { calculateSavings, formatPrice, getCurrency } from '@modrinth/utils'
import { injectNotificationManager, PurchaseModal, useFormatPrice } from '@modrinth/ui'
import { calculateSavings, getCurrency } from '@modrinth/utils'
import { useBaseFetch } from '@/composables/fetch.js'
import { isPermission } from '@/utils/permissions.ts'
import { products } from '~/generated/state.json'
const { addNotification } = injectNotificationManager()
const formatPrice = useFormatPrice()
const title = 'Subscribe to Modrinth Plus!'
const description =
@@ -116,8 +117,6 @@ useHead({
],
})
const vintl = useVIntl()
const config = useRuntimeConfig()
const auth = await useAuth()

View File

@@ -182,7 +182,7 @@
<div>
{{
formatMessage(messages.createdOn, {
date: new Date(app.created).toLocaleDateString(),
date: formatDate(new Date(app.created)),
})
}}
</div>
@@ -257,6 +257,7 @@ import {
IntlFormatted,
normalizeChildren,
StyledInput,
useFormatDateTime,
useVIntl,
} from '@modrinth/ui'
@@ -272,6 +273,7 @@ import {
const { addNotification } = injectNotificationManager()
const { formatMessage } = useVIntl()
const formatDate = useFormatDateTime()
definePageMeta({
middleware: 'auth',

View File

@@ -25,12 +25,12 @@
</template>
</span>
<span>{{ formatPrice(vintl.locale, charge.amount, charge.currency_code) }}</span>
<span>{{ formatPrice(charge.amount, charge.currency_code) }}</span>
</div>
<div class="flex items-center gap-1">
<Badge :color="charge.status === 'succeeded' ? 'green' : 'red'" :type="charge.status" />
{{ $dayjs(charge.due).format('YYYY-MM-DD') }}
{{ formatDate(charge.due) }}
</div>
</div>
</div>
@@ -38,8 +38,7 @@
</div>
</template>
<script setup>
import { Badge, Breadcrumbs, useVIntl } from '@modrinth/ui'
import { formatPrice } from '@modrinth/utils'
import { Badge, Breadcrumbs, useFormatDateTime, useFormatPrice } from '@modrinth/ui'
import { products } from '~/generated/state.json'
@@ -47,7 +46,12 @@ definePageMeta({
middleware: 'auth',
})
const vintl = useVIntl()
const formatPrice = useFormatPrice()
const formatDate = useFormatDateTime({
year: 'numeric',
month: '2-digit',
day: '2-digit',
})
const { data: charges } = await useAsyncData(
'billing/payments',

View File

@@ -51,7 +51,6 @@
<template v-if="midasCharge">
{{
formatPrice(
vintl.locale,
midasSubscriptionPrice.prices.intervals[midasSubscription.interval],
midasSubscriptionPrice.currency_code,
)
@@ -60,7 +59,7 @@
{{ midasSubscription.interval }}
</template>
<template v-else>
{{ formatPrice(vintl.locale, price.prices.intervals.monthly, price.currency_code) }}
{{ formatPrice(price.prices.intervals.monthly, price.currency_code) }}
/ month
</template>
</span>
@@ -77,7 +76,7 @@
>
<span class="opacity-70">Next:</span>
<span class="font-semibold text-contrast">
{{ formatPrice(vintl.locale, midasCharge.amount, midasCharge.currency_code) }}
{{ formatPrice(midasCharge.amount, midasCharge.currency_code) }}
</span>
<span>/{{ midasCharge.subscription_interval.replace('ly', '') }}</span>
</div>
@@ -90,21 +89,17 @@
>
Save
{{
formatPrice(
vintl.locale,
midasCharge.amount * 12 - oppositePrice,
midasCharge.currency_code,
)
formatPrice(midasCharge.amount * 12 - oppositePrice, midasCharge.currency_code)
}}/year by switching to yearly billing!
</span>
<span class="text-sm text-secondary">
Since {{ $dayjs(midasSubscription.created).format('MMMM D, YYYY') }}
Since {{ formatDate(midasSubscription.created) }}
</span>
<span v-if="midasCharge.status === 'open'" class="text-sm text-secondary">
Renews {{ $dayjs(midasCharge.due).format('MMMM D, YYYY') }}
Renews {{ formatDate(midasCharge.due) }}
</span>
<span v-else-if="midasCharge.status === 'cancelled'" class="text-sm text-secondary">
Expires {{ $dayjs(midasCharge.due).format('MMMM D, YYYY') }}
Expires {{ formatDate(midasCharge.due) }}
</span>
<span
v-if="
@@ -116,14 +111,13 @@
class="text-sm text-secondary"
>
Switches to {{ midasCharge.subscription_interval }} billing on
{{ $dayjs(midasCharge.due).format('MMMM D, YYYY') }}
{{ formatDate(midasCharge.due) }}
</span>
</template>
<span v-else class="text-sm text-secondary">
Or
{{ formatPrice(vintl.locale, price.prices.intervals.yearly, price.currency_code) }} /
year (save
{{ formatPrice(price.prices.intervals.yearly, price.currency_code) }} / year (save
{{
calculateSavings(price.prices.intervals.monthly, price.prices.intervals.yearly)
}}%)!
@@ -188,7 +182,6 @@
v-tooltip="
midasCharge.subscription_interval === 'yearly'
? `Monthly billing will cost you an additional ${formatPrice(
vintl.locale,
oppositePrice * 12 - midasCharge.amount,
midasCharge.currency_code,
)} per year`
@@ -307,7 +300,6 @@
<span class="text-contrast">
{{
formatPrice(
vintl.locale,
getProductPrice(getPyroProduct(subscription), subscription.interval)
.prices.intervals[subscription.interval],
getProductPrice(getPyroProduct(subscription), subscription.interval)
@@ -333,7 +325,6 @@
<span class="font-semibold text-contrast">
{{
formatPrice(
vintl.locale,
getPyroCharge(subscription).amount,
getPyroCharge(subscription).currency_code,
)
@@ -351,13 +342,13 @@
</div>
<div v-if="getPyroCharge(subscription)" class="mb-4 flex flex-col items-end">
<span class="text-sm text-secondary">
Since {{ $dayjs(subscription.created).format('MMMM D, YYYY') }}
Since {{ formatDate(subscription.created) }}
</span>
<span
v-if="getPyroCharge(subscription).status === 'open'"
class="text-sm text-secondary"
>
Renews {{ $dayjs(getPyroCharge(subscription).due).format('MMMM D, YYYY') }}
Renews {{ formatDate(getPyroCharge(subscription).due) }}
</span>
<span
v-if="
@@ -371,7 +362,7 @@
Switches to
{{ getPyroCharge(subscription).subscription_interval }}
billing on
{{ $dayjs(getPyroCharge(subscription).due).format('MMMM D, YYYY') }}
{{ formatDate(getPyroCharge(subscription).due) }}
</span>
<span
v-else-if="getPyroCharge(subscription).status === 'processing'"
@@ -384,7 +375,7 @@
v-else-if="getPyroCharge(subscription).status === 'cancelled'"
class="text-sm text-secondary"
>
Expires {{ $dayjs(getPyroCharge(subscription).due).format('MMMM D, YYYY') }}
Expires {{ formatDate(getPyroCharge(subscription).due) }}
</span>
<span
v-else-if="getPyroCharge(subscription).status === 'failed'"
@@ -624,9 +615,11 @@ import {
paymentMethodMessages,
PurchaseModal,
ServerListing,
useFormatDateTime,
useFormatPrice,
useVIntl,
} from '@modrinth/ui'
import { calculateSavings, formatPrice, getCurrency } from '@modrinth/utils'
import { calculateSavings, getCurrency } from '@modrinth/utils'
import { computed, ref } from 'vue'
import { useBaseFetch } from '@/composables/fetch.js'
@@ -655,8 +648,13 @@ useHead({
const config = useRuntimeConfig()
const vintl = useVIntl()
const { formatMessage } = vintl
const { formatMessage } = useVIntl()
const formatPrice = useFormatPrice()
const formatDate = useFormatDateTime({
year: 'numeric',
month: 'long',
day: 'numeric',
})
const deleteModalMessages = defineMessages({
title: {

View File

@@ -119,16 +119,7 @@
<CopyCode :text="pat.access_token" />
</template>
<template v-else>
<span
v-tooltip="
pat.last_used
? formatMessage(commonMessages.dateAtTimeTooltip, {
date: new Date(pat.last_used),
time: new Date(pat.last_used),
})
: null
"
>
<span v-tooltip="pat.last_used ? formatDateTime(pat.last_used) : null">
<template v-if="pat.last_used">
{{
formatMessage(tokenMessages.lastUsed, {
@@ -139,14 +130,7 @@
<template v-else>{{ formatMessage(tokenMessages.neverUsed) }}</template>
</span>
<span
v-tooltip="
formatMessage(commonMessages.dateAtTimeTooltip, {
date: new Date(pat.expires),
time: new Date(pat.expires),
})
"
>
<span v-tooltip="formatDateTime(pat.expires)">
<template v-if="new Date(pat.expires) > new Date()">
{{
formatMessage(tokenMessages.expiresIn, {
@@ -163,14 +147,7 @@
</template>
</span>
<span
v-tooltip="
formatMessage(commonMessages.dateAtTimeTooltip, {
date: new Date(pat.created),
time: new Date(pat.created),
})
"
>
<span v-tooltip="formatDateTime(pat.created)">
{{
formatMessage(commonMessages.createdAgoLabel, {
ago: formatRelativeTime(pat.created),
@@ -222,6 +199,7 @@ import {
injectNotificationManager,
IntlFormatted,
StyledInput,
useFormatDateTime,
useRelativeTime,
useVIntl,
} from '@modrinth/ui'
@@ -240,6 +218,10 @@ const { addNotification } = injectNotificationManager()
const { formatMessage } = useVIntl()
const formatRelativeTime = useRelativeTime()
const formatDateTime = useFormatDateTime({
timeStyle: 'short',
dateStyle: 'long',
})
const createModalMessages = defineMessages({
createTitle: {

View File

@@ -15,14 +15,7 @@
</div>
<div>
<template v-if="session.city">{{ session.city }}, {{ session.country }} </template>
<span
v-tooltip="
formatMessage(commonMessages.dateAtTimeTooltip, {
date: new Date(session.last_login),
time: new Date(session.last_login),
})
"
>
<span v-tooltip="formatDateTime(session.last_login)">
{{
formatMessage(messages.lastAccessedAgoLabel, {
ago: formatRelativeTime(session.last_login),
@@ -30,14 +23,7 @@
}}
</span>
<span
v-tooltip="
formatMessage(commonMessages.dateAtTimeTooltip, {
date: new Date(session.created),
time: new Date(session.created),
})
"
>
<span v-tooltip="formatDateTime(session.created)">
{{
formatMessage(messages.createdAgoLabel, {
ago: formatRelativeTime(session.created),
@@ -62,6 +48,7 @@ import {
commonSettingsMessages,
defineMessages,
injectNotificationManager,
useFormatDateTime,
useRelativeTime,
useVIntl,
} from '@modrinth/ui'
@@ -73,6 +60,10 @@ definePageMeta({
const { addNotification } = injectNotificationManager()
const { formatMessage } = useVIntl()
const formatRelativeTime = useRelativeTime()
const formatDateTime = useFormatDateTime({
timeStyle: 'short',
dateStyle: 'long',
})
const messages = defineMessages({
currentSessionLabel: {

View File

@@ -155,27 +155,24 @@
{{
formatMessage(messages.profileProjectsLabel, {
count: formatCompactNumber(projects?.length || 0),
countPlural: formatCompactNumberPlural(projects?.length || 0),
})
}}
</div>
<div
v-tooltip="sumDownloads.toLocaleString()"
v-tooltip="formatNumber(sumDownloads)"
class="flex items-center gap-2 border-0 border-r border-solid border-divider pr-4 font-semibold"
>
<DownloadIcon class="h-6 w-6 text-secondary" />
{{
formatMessage(messages.profileDownloadsLabel, {
count: formatCompactNumber(sumDownloads),
countPlural: formatCompactNumberPlural(sumDownloads),
})
}}
</div>
<div
v-tooltip="
formatMessage(commonMessages.dateAtTimeTooltip, {
date: new Date(user.created),
time: new Date(user.created),
})
"
v-tooltip="formatDateTime(user.created)"
class="flex items-center gap-2 font-semibold"
>
<CalendarIcon class="h-6 w-6 text-secondary" />
@@ -502,6 +499,9 @@ import {
ProjectCard,
ProjectCardList,
TagItem,
useCompactNumber,
useFormatDateTime,
useFormatNumber,
useRelativeTime,
useVIntl,
} from '@modrinth/ui'
@@ -528,12 +528,14 @@ const cosmetics = useCosmetics()
const tags = useGeneratedState()
const config = useRuntimeConfig()
const vintl = useVIntl()
const { formatMessage } = vintl
const formatCompactNumber = useCompactNumber(true)
const { formatMessage } = useVIntl()
const formatNumber = useFormatNumber()
const { formatCompactNumber, formatCompactNumberPlural } = useCompactNumber()
const formatRelativeTime = useRelativeTime()
const formatDateTime = useFormatDateTime({
timeStyle: 'short',
dateStyle: 'long',
})
const { addNotification } = injectNotificationManager()
@@ -542,11 +544,11 @@ const baseId = useId()
const messages = defineMessages({
profileProjectsLabel: {
id: 'profile.label.projects',
defaultMessage: '{count} {count, plural, one {project} other {projects}}',
defaultMessage: '{count} {countPlural, plural, one {project} other {projects}}',
},
profileDownloadsLabel: {
id: 'profile.label.downloads',
defaultMessage: '{count} {count, plural, one {download} other {downloads}}',
defaultMessage: '{count} {countPlural, plural, one {download} other {downloads}}',
},
profileJoinedLabel: {
id: 'profile.label.joined',