Add translation keys for collections (#1496)

* Begin Work

* Add more common messages

* Work on modal

* Add more keys

* Add keys for icon buttons

* Add more keys

* Handle error keys

* Add more keys

* Add more keys

* Edit fields keys

* Finish (almost)

* Finish work for collection page

* Dashboard Nav stack & Format

* WIP

* Move some messages to common

* Finish work

* Format

* Reorganization

* Fix some mistake

* add common collections label

* Add collections label key to default layout

* Make title and description reactive (#8)

---------

Co-authored-by: Sasha Sorokin <10401817+brawaru@users.noreply.github.com>
This commit is contained in:
Mysterious_Dev
2024-01-27 18:24:19 +01:00
committed by GitHub
parent 0195e94aa7
commit 52b299315d
6 changed files with 375 additions and 67 deletions

View File

@@ -3,10 +3,10 @@
<ModalConfirm
v-if="auth.user && auth.user.id === creator.id"
ref="deleteModal"
title="Are you sure you want to delete this collection?"
description="This will remove this collection forever. This action cannot be undone."
:title="formatMessage(messages.deleteModalTitle)"
:description="formatMessage(messages.deleteModalDescription)"
:has-to-type="false"
proceed-label="Delete"
:proceed-label="formatMessage(commonMessages.deleteLabel)"
@proceed="deleteCollection()"
/>
<div class="normal-page">
@@ -16,16 +16,16 @@
<template v-if="canEdit && isEditing === false">
<Button @click="isEditing = true">
<EditIcon />
Edit
{{ formatMessage(commonMessages.editButton) }}
</Button>
<Button id="delete-collection" @click="() => $refs.deleteModal.show()">
<TrashIcon />
Delete
{{ formatMessage(commonMessages.deleteLabel) }}
</Button>
</template>
<template v-else-if="canEdit && isEditing === true">
<PopoutMenu class="btn" position="bottom" direction="right">
<EditIcon /> Edit icon
<EditIcon /> {{ formatMessage(messages.editIconButton) }}
<template #menu>
<span class="icon-edit-menu">
<FileInput
@@ -39,7 +39,7 @@
@change="showPreviewImage"
>
<UploadIcon />
Upload icon
{{ formatMessage(messages.uploadIconButton) }}
</FileInput>
<Button
v-if="!deletedIcon && (previewImage || collection.icon_url)"
@@ -53,7 +53,7 @@
"
>
<TrashIcon />
Delete icon
{{ formatMessage(messages.deleteIconButton) }}
</Button>
</span>
</template>
@@ -70,17 +70,21 @@
/>
</div>
<label for="collection-title">
<span class="label__title"> Title </span>
<span class="label__title"> {{ formatMessage(commonMessages.titleLabel) }} </span>
</label>
<input id="collection-title" v-model="name" maxlength="255" type="text" />
<label for="collection-description">
<span class="label__title"> Description </span>
<span class="label__title">
{{ formatMessage(commonMessages.descriptionLabel) }}
</span>
</label>
<div class="textarea-wrapper">
<textarea id="collection-description" v-model="summary" maxlength="255" />
</div>
<label for="visibility">
<span class="label__title"> Visibility </span>
<span class="label__title">
{{ formatMessage(commonMessages.visibilityLabel) }}
</span>
</label>
<DropdownSelect
id="visibility"
@@ -90,8 +94,8 @@
:multiple="false"
:display-name="
(s) => {
if (s === 'listed') return 'Public'
return capitalizeString(s)
if (s === 'listed') return formatMessage(commonMessages.publicLabel)
return formatMessage(commonMessages[`${s}Label`])
}
"
:searchable="false"
@@ -100,11 +104,11 @@
<div class="push-right input-group">
<Button @click="isEditing = false">
<XIcon />
Cancel
{{ formatMessage(commonMessages.cancelButton) }}
</Button>
<Button color="primary" @click="saveChanges()">
<SaveIcon />
Save
{{ formatMessage(commonMessages.saveButton) }}
</Button>
</div>
</template>
@@ -117,7 +121,9 @@
<h1 class="title">{{ collection.name }}</h1>
<div>
<span class="collection-label"><BoxIcon /> Collection</span>
<span class="collection-label">
<BoxIcon /> {{ formatMessage(messages.collectionLabel) }}
</span>
</div>
<div class="collection-info">
@@ -131,25 +137,25 @@
<template v-if="collection.status === 'listed'">
<WorldIcon class="primary-stat__icon" aria-hidden="true" />
<div class="primary-stat__text">
<strong> Public </strong>
<strong> {{ formatMessage(commonMessages.publicLabel) }} </strong>
</div>
</template>
<template v-else-if="collection.status === 'unlisted'">
<LinkIcon class="primary-stat__icon" aria-hidden="true" />
<div class="primary-stat__text">
<strong> Unlisted </strong>
<strong> {{ formatMessage(commonMessages.unlistedLabel) }} </strong>
</div>
</template>
<template v-else-if="collection.status === 'private'">
<LockIcon class="primary-stat__icon" aria-hidden="true" />
<div class="primary-stat__text">
<strong> Private </strong>
<strong> {{ formatMessage(commonMessages.privateLabel) }} </strong>
</div>
</template>
<template v-else-if="collection.status === 'rejected'">
<XIcon class="primary-stat__icon" aria-hidden="true" />
<div class="primary-stat__text">
<strong> Rejected </strong>
<strong> {{ formatMessage(commonMessages.rejectedLabel) }} </strong>
</div>
</template>
</div>
@@ -158,35 +164,57 @@
<div class="primary-stat">
<LibraryIcon class="primary-stat__icon" aria-hidden="true" />
<div v-if="projects" class="primary-stat__text">
<span class="primary-stat__counter">
{{ $formatNumber(projects?.length || 0) }}
</span>
project<span v-if="projects?.length !== 1">s</span>
<IntlFormatted
:message-id="messages.projectsCountLabel"
:values="{ count: formatCompactNumber(projects.length || 0) }"
>
<template #stat="{ children }">
<span class="primary-stat__counter">
<component :is="() => normalizeChildren(children)" />
</span>
</template>
</IntlFormatted>
</div>
</div>
<div class="metadata-item">
<div
v-tooltip="$dayjs(collection.created).format('MMMM D, YYYY [at] h:mm A')"
v-tooltip="
formatMessage(commonMessages.dateAtTimeTooltip, {
date: new Date(collection.created),
time: new Date(collection.created),
})
"
class="date"
>
<CalendarIcon />
<label>
Created
{{ fromNow(collection.created) }}
{{
formatMessage(messages.createdAtLabel, {
ago: formatRelativeTime(collection.created),
})
}}
</label>
</div>
</div>
<div v-if="collection.id !== 'following'" class="metadata-item">
<div
v-tooltip="$dayjs(collection.updated).format('MMMM D, YYYY [at] h:mm A')"
v-tooltip="
formatMessage(commonMessages.dateAtTimeTooltip, {
date: new Date(collection.updated),
time: new Date(collection.updated),
})
"
class="date"
>
<UpdatedIcon />
<label>
Updated
{{ fromNow(collection.updated) }}
{{
formatMessage(messages.updatedAtLabel, {
ago: formatRelativeTime(collection.updated),
})
}}
</label>
</div>
</div>
@@ -195,7 +223,7 @@
<hr class="card-divider" />
<div class="collection-info">
<h2 class="card-header">Curated by</h2>
<h2 class="card-header">{{ formatMessage(messages.curatedByLabel) }}</h2>
<div class="metadata-item">
<nuxt-link
class="team-member columns button-transparent"
@@ -205,7 +233,7 @@
<div class="member-info">
<p class="name">{{ creator.username }}</p>
<p class="role">Owner</p>
<p class="role">{{ formatMessage(messages.ownerLabel) }}</p>
</div>
</nuxt-link>
</div>
@@ -304,7 +332,7 @@
"
>
<TrashIcon />
Remove project
{{ formatMessage(messages.removeProjectButton) }}
</button>
<button
v-if="collection.id === 'following'"
@@ -312,18 +340,22 @@
@click="unfollowProject(project)"
>
<TrashIcon />
Unfollow project
{{ formatMessage(messages.unfollowProjectButton) }}
</button>
</ProjectCard>
</div>
<div v-else class="error">
<UpToDate class="icon" /><br />
<span v-if="auth.user && auth.user.id === creator.id" class="text">
You don't have any projects.<br />
Would you like to
<a class="link" @click.prevent="$router.push('/mods')"> add one</a>?
<span v-if="auth.user && auth.user.id === creator.id" class="preserve-lines text">
<IntlFormatted :message-id="messages.noProjectsAuthLabel">
<template #create-link="{ children }">
<a class="link" @click.prevent="$router.push('/mods')">
<component :is="() => children" />
</a>
</template>
</IntlFormatted>
</span>
<span v-else class="text">This collection has no projects!</span>
<span v-else class="text">{{ formatMessage(messages.noProjectsLabel) }}</span>
</div>
</div>
</div>
@@ -332,7 +364,6 @@
<script setup>
import {
capitalizeString,
Avatar,
Button,
CalendarIcon,
@@ -364,6 +395,89 @@ import ProjectCard from '~/components/ui/ProjectCard.vue'
const vintl = useVIntl()
const { formatMessage } = vintl
const formatRelativeTime = useRelativeTime()
const formatCompactNumber = useCompactNumber()
const messages = defineMessages({
collectionDescription: {
id: 'collection.description',
defaultMessage: '{description} - View the collection {name} by {username} on Modrinth',
},
collectionLabel: {
id: 'collection.label.collection',
defaultMessage: 'Collection',
},
collectionTitle: {
id: 'collection.title',
defaultMessage: '{name} - Collection',
},
editIconButton: {
id: 'collection.button.edit-icon',
defaultMessage: 'Edit icon',
},
deleteIconButton: {
id: 'collection.button.delete-icon',
defaultMessage: 'Delete icon',
},
createdAtLabel: {
id: 'collection.label.created-at',
defaultMessage: 'Created {ago}',
},
collectionNotFoundError: {
id: 'collection.error.not-found',
defaultMessage: 'Collection not found',
},
curatedByLabel: {
id: 'collection.label.curated-by',
defaultMessage: 'Curated by',
},
deleteModalDescription: {
id: 'collection.delete-modal.description',
defaultMessage: 'This will remove this collection forever. This action cannot be undone.',
},
deleteModalTitle: {
id: 'collection.delete-modal.title',
defaultMessage: 'Are you sure you want to delete this collection?',
},
followingCollectionDescription: {
id: 'collection.description.following',
defaultMessage: "Auto-generated collection of all the projects you're following.",
},
noProjectsLabel: {
id: 'collection.label.no-projects',
defaultMessage: 'This collection has no projects!',
},
noProjectsAuthLabel: {
id: 'collection.label.no-projects-auth',
defaultMessage:
"You don't have any projects.\nWould you like to <create-link>add one</create-link>?",
},
ownerLabel: {
id: 'collection.label.owner',
defaultMessage: 'Owner',
},
projectsCountLabel: {
id: 'collection.label.projects-count',
defaultMessage:
'{count, plural, one {<stat>{count}</stat> project} other {<stat>{count}</stat> projects}}',
},
removeProjectButton: {
id: 'collection.button.remove-project',
defaultMessage: 'Remove project',
},
unfollowProjectButton: {
id: 'collection.button.unfollow-project',
defaultMessage: 'Unfollow project',
},
updatedAtLabel: {
id: 'collection.label.updated-at',
defaultMessage: 'Updated {ago}',
},
uploadIconButton: {
id: 'collection.button.upload-icon',
defaultMessage: 'Upload icon',
},
})
const data = useNuxtApp()
const route = useRoute()
@@ -388,8 +502,8 @@ try {
collection = ref({
id: 'following',
icon_url: 'https://cdn.modrinth.com/follow-collection.png',
name: 'Followed projects',
description: "Auto-generated collection of all the projects you're following.",
name: formatMessage(commonMessages.followedProjectsLabel),
description: formatMessage(messages.followingCollectionDescription),
status: 'private',
user: auth.value.user.id,
created: auth.value.user.created,
@@ -426,7 +540,7 @@ try {
throw createError({
fatal: true,
statusCode: 404,
message: 'Collection not found',
message: formatMessage(messages.collectionNotFoundError),
})
}
@@ -434,16 +548,22 @@ if (!collection.value) {
throw createError({
fatal: true,
statusCode: 404,
message: 'Collection not found',
message: formatMessage(messages.collectionNotFoundError),
})
}
const title = `${collection.value.name} - Collection`
const description = `${collection.value.description} - View the collection ${collection.value.description} by ${creator.value.username} on Modrinth`
const title = computed(() =>
formatMessage(messages.collectionTitle, { name: collection.value.name })
)
useSeoMeta({
title,
description,
description: () =>
formatMessage(messages.collectionDescription, {
name: collection.value.name,
description: collection.value.description,
username: creator.value.username,
}),
ogTitle: title,
ogDescription: collection.value.description,
ogImage: collection.value.icon_url ?? 'https://cdn.modrinth.com/placeholder.png',
@@ -526,7 +646,7 @@ async function saveChanges() {
} catch (err) {
addNotification({
group: 'main',
title: 'An error occurred',
title: formatMessage(commonMessages.errorNotificationTitle),
text: err,
type: 'error',
})
@@ -546,7 +666,7 @@ async function deleteCollection() {
} catch (err) {
addNotification({
group: 'main',
title: 'An error occurred',
title: formatMessage(commonMessages.errorNotificationTitle),
text: err.data.description,
type: 'error',
})