You've already forked AstralRinth
forked from didirus/AstralRinth
Add translation keys for Pats page (#1590)
* Begin Work * More work * Fix lint error * More work on label * Fix mistake * Finish adding delete pat modal keys * More label and button * More label keys * Fix lint error * Description key * Finish page * Forgot this * Fix lint error * Add to navstack * Apply suggestions from brawaru * Normalization * Re-organize PATs page messages (#10) - Group messages by their usage - Fix spelling mistakes in some of the property names and keys - Change some of the keys to conform to keying conventions - Change variable name in token.expires-in message to inTime * Regenrate index.json --------- Co-authored-by: Sasha Sorokin <10401817+brawaru@users.noreply.github.com>
This commit is contained in:
@@ -167,6 +167,9 @@
|
|||||||
"button.save": {
|
"button.save": {
|
||||||
"message": "Save"
|
"message": "Save"
|
||||||
},
|
},
|
||||||
|
"button.save-changes": {
|
||||||
|
"message": "Save changes"
|
||||||
|
},
|
||||||
"collection.button.delete-icon": {
|
"collection.button.delete-icon": {
|
||||||
"message": "Delete icon"
|
"message": "Delete icon"
|
||||||
},
|
},
|
||||||
@@ -266,6 +269,9 @@
|
|||||||
"label.collections": {
|
"label.collections": {
|
||||||
"message": "Collections"
|
"message": "Collections"
|
||||||
},
|
},
|
||||||
|
"label.created-ago": {
|
||||||
|
"message": "Created {ago}"
|
||||||
|
},
|
||||||
"label.delete": {
|
"label.delete": {
|
||||||
"message": "Delete"
|
"message": "Delete"
|
||||||
},
|
},
|
||||||
@@ -284,6 +290,9 @@
|
|||||||
"label.rejected": {
|
"label.rejected": {
|
||||||
"message": "Rejected"
|
"message": "Rejected"
|
||||||
},
|
},
|
||||||
|
"label.scopes": {
|
||||||
|
"message": "Scopes"
|
||||||
|
},
|
||||||
"label.title": {
|
"label.title": {
|
||||||
"message": "Title"
|
"message": "Title"
|
||||||
},
|
},
|
||||||
@@ -695,6 +704,63 @@
|
|||||||
"settings.language.title": {
|
"settings.language.title": {
|
||||||
"message": "Language"
|
"message": "Language"
|
||||||
},
|
},
|
||||||
|
"settings.pats.action.create": {
|
||||||
|
"message": "Create a PAT"
|
||||||
|
},
|
||||||
|
"settings.pats.description": {
|
||||||
|
"message": "PATs can be used to access Modrinth's API. For more information, see <doc-link>Modrinth's API documentation</doc-link>. They can be created and revoked at any time."
|
||||||
|
},
|
||||||
|
"settings.pats.modal.create.action": {
|
||||||
|
"message": "Create PAT"
|
||||||
|
},
|
||||||
|
"settings.pats.modal.create.expires.label": {
|
||||||
|
"message": "Expires"
|
||||||
|
},
|
||||||
|
"settings.pats.modal.create.name.label": {
|
||||||
|
"message": "Name"
|
||||||
|
},
|
||||||
|
"settings.pats.modal.create.name.placeholder": {
|
||||||
|
"message": "Enter the PAT's name..."
|
||||||
|
},
|
||||||
|
"settings.pats.modal.create.title": {
|
||||||
|
"message": "Create personal access token"
|
||||||
|
},
|
||||||
|
"settings.pats.modal.delete.action": {
|
||||||
|
"message": "Delete this token"
|
||||||
|
},
|
||||||
|
"settings.pats.modal.delete.description": {
|
||||||
|
"message": "This will remove this token forever (like really forever)."
|
||||||
|
},
|
||||||
|
"settings.pats.modal.delete.title": {
|
||||||
|
"message": "Are you sure you want to delete this token?"
|
||||||
|
},
|
||||||
|
"settings.pats.modal.edit.title": {
|
||||||
|
"message": "Edit personal access token"
|
||||||
|
},
|
||||||
|
"settings.pats.title": {
|
||||||
|
"message": "PATs"
|
||||||
|
},
|
||||||
|
"settings.pats.title.long": {
|
||||||
|
"message": "Personal Access Tokens"
|
||||||
|
},
|
||||||
|
"settings.pats.token.action.edit": {
|
||||||
|
"message": "Edit token"
|
||||||
|
},
|
||||||
|
"settings.pats.token.action.revoke": {
|
||||||
|
"message": "Revoke token"
|
||||||
|
},
|
||||||
|
"settings.pats.token.expired-ago": {
|
||||||
|
"message": "Expired {ago}"
|
||||||
|
},
|
||||||
|
"settings.pats.token.expires-in": {
|
||||||
|
"message": "Expires {inTime}"
|
||||||
|
},
|
||||||
|
"settings.pats.token.last-used": {
|
||||||
|
"message": "Last used {ago}"
|
||||||
|
},
|
||||||
|
"settings.pats.token.never-used": {
|
||||||
|
"message": "Never used"
|
||||||
|
},
|
||||||
"settings.sessions.action.revoke-session": {
|
"settings.sessions.action.revoke-session": {
|
||||||
"message": "Revoke session"
|
"message": "Revoke session"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -24,7 +24,7 @@
|
|||||||
</template>
|
</template>
|
||||||
<template v-if="auth.user">
|
<template v-if="auth.user">
|
||||||
<h3>Developer Settings</h3>
|
<h3>Developer Settings</h3>
|
||||||
<NavStackItem link="/settings/pats" label="PATs">
|
<NavStackItem link="/settings/pats" :label="formatMessage(messages.patsTitle)">
|
||||||
<KeyIcon />
|
<KeyIcon />
|
||||||
</NavStackItem>
|
</NavStackItem>
|
||||||
<NavStackItem link="/settings/applications" label="Applications">
|
<NavStackItem link="/settings/applications" label="Applications">
|
||||||
@@ -57,6 +57,10 @@ const messages = defineMessages({
|
|||||||
id: 'settings.sessions.title',
|
id: 'settings.sessions.title',
|
||||||
defaultMessage: 'Sessions',
|
defaultMessage: 'Sessions',
|
||||||
},
|
},
|
||||||
|
patsTitle: {
|
||||||
|
id: 'settings.pats.title',
|
||||||
|
defaultMessage: 'PATs',
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
|
|||||||
@@ -2,25 +2,33 @@
|
|||||||
<div class="universal-card">
|
<div class="universal-card">
|
||||||
<ConfirmModal
|
<ConfirmModal
|
||||||
ref="modal_confirm"
|
ref="modal_confirm"
|
||||||
title="Are you sure you want to delete this token?"
|
:title="formatMessage(deleteModalMessages.title)"
|
||||||
description="This will remove this token forever (like really forever)."
|
:description="formatMessage(deleteModalMessages.description)"
|
||||||
proceed-label="Delete this token"
|
:proceed-label="formatMessage(deleteModalMessages.action)"
|
||||||
@proceed="removePat(deletePatIndex)"
|
@proceed="removePat(deletePatIndex)"
|
||||||
/>
|
/>
|
||||||
<Modal
|
<Modal
|
||||||
ref="patModal"
|
ref="patModal"
|
||||||
:header="`${editPatIndex !== null ? 'Edit' : 'Create'} personal access token`"
|
:header="
|
||||||
|
editPatIndex !== null
|
||||||
|
? formatMessage(createModalMessages.editTitle)
|
||||||
|
: formatMessage(createModalMessages.createTitle)
|
||||||
|
"
|
||||||
>
|
>
|
||||||
<div class="universal-modal">
|
<div class="universal-modal">
|
||||||
<label for="pat-name"><span class="label__title">Name</span> </label>
|
<label for="pat-name">
|
||||||
|
<span class="label__title">{{ formatMessage(createModalMessages.nameLabel) }}</span>
|
||||||
|
</label>
|
||||||
<input
|
<input
|
||||||
id="pat-name"
|
id="pat-name"
|
||||||
v-model="name"
|
v-model="name"
|
||||||
maxlength="2048"
|
maxlength="2048"
|
||||||
type="email"
|
type="email"
|
||||||
placeholder="Enter the PAT's name..."
|
:placeholder="formatMessage(createModalMessages.namePlaceholder)"
|
||||||
/>
|
/>
|
||||||
<label for="pat-scopes"><span class="label__title">Scopes</span> </label>
|
<label for="pat-scopes">
|
||||||
|
<span class="label__title">{{ formatMessage(commonMessages.scopesLabel) }}</span>
|
||||||
|
</label>
|
||||||
<div id="pat-scopes" class="checkboxes">
|
<div id="pat-scopes" class="checkboxes">
|
||||||
<Checkbox
|
<Checkbox
|
||||||
v-for="scope in scopeList"
|
v-for="scope in scopeList"
|
||||||
@@ -30,13 +38,15 @@
|
|||||||
@update:model-value="scopesVal = toggleScope(scopesVal, scope)"
|
@update:model-value="scopesVal = toggleScope(scopesVal, scope)"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<label for="pat-name"><span class="label__title">Expires</span> </label>
|
<label for="pat-name">
|
||||||
|
<span class="label__title">{{ formatMessage(createModalMessages.expiresLabel) }}</span>
|
||||||
|
</label>
|
||||||
<input id="pat-name" v-model="expires" type="date" />
|
<input id="pat-name" v-model="expires" type="date" />
|
||||||
<p></p>
|
<p></p>
|
||||||
<div class="input-group push-right">
|
<div class="input-group push-right">
|
||||||
<button class="iconified-button" @click="$refs.patModal.hide()">
|
<button class="iconified-button" @click="$refs.patModal.hide()">
|
||||||
<XIcon />
|
<XIcon />
|
||||||
Cancel
|
{{ formatMessage(commonMessages.cancelButton) }}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
v-if="editPatIndex !== null"
|
v-if="editPatIndex !== null"
|
||||||
@@ -46,7 +56,7 @@
|
|||||||
@click="editPat"
|
@click="editPat"
|
||||||
>
|
>
|
||||||
<SaveIcon />
|
<SaveIcon />
|
||||||
Save changes
|
{{ formatMessage(commonMessages.saveChangesButton) }}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
v-else
|
v-else
|
||||||
@@ -56,7 +66,7 @@
|
|||||||
@click="createPat"
|
@click="createPat"
|
||||||
>
|
>
|
||||||
<PlusIcon />
|
<PlusIcon />
|
||||||
Create PAT
|
{{ formatMessage(createModalMessages.action) }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -64,7 +74,7 @@
|
|||||||
|
|
||||||
<div class="header__row">
|
<div class="header__row">
|
||||||
<div class="header__title">
|
<div class="header__title">
|
||||||
<h2>Personal Access Tokens</h2>
|
<h2>{{ formatMessage(messages.longTitle) }}</h2>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
class="btn btn-primary"
|
class="btn btn-primary"
|
||||||
@@ -78,13 +88,17 @@
|
|||||||
}
|
}
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
<PlusIcon /> Create a PAT
|
<PlusIcon /> {{ formatMessage(messages.create) }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<p>
|
<p>
|
||||||
PATs can be used to access Modrinth's API. For more information, see
|
<IntlFormatted :message-id="messages.description">
|
||||||
<a class="text-link" href="https://docs.modrinth.com">Modrinth's API documentation</a>. They
|
<template #doc-link="{ children }">
|
||||||
can be created and revoked at any time.
|
<a class="text-link" href="https://docs.modrinth.com">
|
||||||
|
<component :is="() => children" />
|
||||||
|
</a>
|
||||||
|
</template>
|
||||||
|
</IntlFormatted>
|
||||||
</p>
|
</p>
|
||||||
<div v-for="(pat, index) in pats" :key="pat.id" class="universal-card recessed token">
|
<div v-for="(pat, index) in pats" :key="pat.id" class="universal-card recessed token">
|
||||||
<div>
|
<div>
|
||||||
@@ -98,22 +112,61 @@
|
|||||||
<template v-else>
|
<template v-else>
|
||||||
<span
|
<span
|
||||||
v-tooltip="
|
v-tooltip="
|
||||||
pat.last_used ? $dayjs(pat.last_login).format('MMMM D, YYYY [at] h:mm A') : null
|
pat.last_used
|
||||||
|
? formatMessage(commonMessages.dateAtTimeTooltip, {
|
||||||
|
date: new Date(pat.last_used),
|
||||||
|
time: new Date(pat.last_used),
|
||||||
|
})
|
||||||
|
: null
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
<template v-if="pat.last_used">Last used {{ fromNow(pat.last_used) }}</template>
|
<template v-if="pat.last_used">
|
||||||
<template v-else>Never used</template>
|
{{
|
||||||
</span>
|
formatMessage(tokenMessages.lastUsed, {
|
||||||
⋅
|
ago: formatRelativeTime(pat.last_used),
|
||||||
<span v-tooltip="$dayjs(pat.expires).format('MMMM D, YYYY [at] h:mm A')">
|
})
|
||||||
<template v-if="new Date(pat.expires) > new Date()">
|
}}
|
||||||
Expires {{ fromNow(pat.expires) }}
|
|
||||||
</template>
|
</template>
|
||||||
<template v-else> Expired {{ fromNow(pat.expires) }} </template>
|
<template v-else>{{ formatMessage(tokenMessages.neverUsed) }}</template>
|
||||||
</span>
|
</span>
|
||||||
⋅
|
⋅
|
||||||
<span v-tooltip="$dayjs(pat.created).format('MMMM D, YYYY [at] h:mm A')">
|
<span
|
||||||
Created {{ fromNow(pat.created) }}
|
v-tooltip="
|
||||||
|
formatMessage(commonMessages.dateAtTimeTooltip, {
|
||||||
|
date: new Date(pat.expires),
|
||||||
|
time: new Date(pat.expires),
|
||||||
|
})
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<template v-if="new Date(pat.expires) > new Date()">
|
||||||
|
{{
|
||||||
|
formatMessage(tokenMessages.expiresIn, {
|
||||||
|
inTime: formatRelativeTime(pat.expires),
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
{{
|
||||||
|
formatMessage(tokenMessages.expiredAgo, {
|
||||||
|
ago: formatRelativeTime(pat.expires),
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
</template>
|
||||||
|
</span>
|
||||||
|
⋅
|
||||||
|
<span
|
||||||
|
v-tooltip="
|
||||||
|
formatMessage(commonMessages.dateAtTimeTooltip, {
|
||||||
|
date: new Date(pat.created),
|
||||||
|
time: new Date(pat.created),
|
||||||
|
})
|
||||||
|
"
|
||||||
|
>
|
||||||
|
{{
|
||||||
|
formatMessage(commonMessages.createdAgoLabel, {
|
||||||
|
ago: formatRelativeTime(pat.created),
|
||||||
|
})
|
||||||
|
}}
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
@@ -131,7 +184,7 @@
|
|||||||
}
|
}
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
<EditIcon /> Edit token
|
<EditIcon /> {{ formatMessage(tokenMessages.edit) }}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
class="iconified-button raised-button"
|
class="iconified-button raised-button"
|
||||||
@@ -142,7 +195,7 @@
|
|||||||
}
|
}
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
<TrashIcon /> Revoke token
|
<TrashIcon /> {{ formatMessage(tokenMessages.revoke) }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -162,12 +215,105 @@ import {
|
|||||||
import CopyCode from '~/components/ui/CopyCode.vue'
|
import CopyCode from '~/components/ui/CopyCode.vue'
|
||||||
import Modal from '~/components/ui/Modal.vue'
|
import Modal from '~/components/ui/Modal.vue'
|
||||||
|
|
||||||
|
const { formatMessage } = useVIntl()
|
||||||
|
|
||||||
|
const formatRelativeTime = useRelativeTime()
|
||||||
|
|
||||||
|
const createModalMessages = defineMessages({
|
||||||
|
createTitle: {
|
||||||
|
id: 'settings.pats.modal.create.title',
|
||||||
|
defaultMessage: 'Create personal access token',
|
||||||
|
},
|
||||||
|
editTitle: {
|
||||||
|
id: 'settings.pats.modal.edit.title',
|
||||||
|
defaultMessage: 'Edit personal access token',
|
||||||
|
},
|
||||||
|
nameLabel: {
|
||||||
|
id: 'settings.pats.modal.create.name.label',
|
||||||
|
defaultMessage: 'Name',
|
||||||
|
},
|
||||||
|
namePlaceholder: {
|
||||||
|
id: 'settings.pats.modal.create.name.placeholder',
|
||||||
|
defaultMessage: "Enter the PAT's name...",
|
||||||
|
},
|
||||||
|
expiresLabel: {
|
||||||
|
id: 'settings.pats.modal.create.expires.label',
|
||||||
|
defaultMessage: 'Expires',
|
||||||
|
},
|
||||||
|
action: {
|
||||||
|
id: 'settings.pats.modal.create.action',
|
||||||
|
defaultMessage: 'Create PAT',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const deleteModalMessages = defineMessages({
|
||||||
|
title: {
|
||||||
|
id: 'settings.pats.modal.delete.title',
|
||||||
|
defaultMessage: 'Are you sure you want to delete this token?',
|
||||||
|
},
|
||||||
|
description: {
|
||||||
|
id: 'settings.pats.modal.delete.description',
|
||||||
|
defaultMessage: 'This will remove this token forever (like really forever).',
|
||||||
|
},
|
||||||
|
action: {
|
||||||
|
id: 'settings.pats.modal.delete.action',
|
||||||
|
defaultMessage: 'Delete this token',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const messages = defineMessages({
|
||||||
|
title: {
|
||||||
|
id: 'settings.pats.title',
|
||||||
|
defaultMessage: 'PATs',
|
||||||
|
},
|
||||||
|
longTitle: {
|
||||||
|
id: 'settings.pats.title.long',
|
||||||
|
defaultMessage: 'Personal Access Tokens',
|
||||||
|
},
|
||||||
|
description: {
|
||||||
|
id: 'settings.pats.description',
|
||||||
|
defaultMessage:
|
||||||
|
"PATs can be used to access Modrinth's API. For more information, see <doc-link>Modrinth's API documentation</doc-link>. They can be created and revoked at any time.",
|
||||||
|
},
|
||||||
|
create: {
|
||||||
|
id: 'settings.pats.action.create',
|
||||||
|
defaultMessage: 'Create a PAT',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const tokenMessages = defineMessages({
|
||||||
|
edit: {
|
||||||
|
id: 'settings.pats.token.action.edit',
|
||||||
|
defaultMessage: 'Edit token',
|
||||||
|
},
|
||||||
|
revoke: {
|
||||||
|
id: 'settings.pats.token.action.revoke',
|
||||||
|
defaultMessage: 'Revoke token',
|
||||||
|
},
|
||||||
|
lastUsed: {
|
||||||
|
id: 'settings.pats.token.last-used',
|
||||||
|
defaultMessage: 'Last used {ago}',
|
||||||
|
},
|
||||||
|
neverUsed: {
|
||||||
|
id: 'settings.pats.token.never-used',
|
||||||
|
defaultMessage: 'Never used',
|
||||||
|
},
|
||||||
|
expiresIn: {
|
||||||
|
id: 'settings.pats.token.expires-in',
|
||||||
|
defaultMessage: 'Expires {inTime}',
|
||||||
|
},
|
||||||
|
expiredAgo: {
|
||||||
|
id: 'settings.pats.token.expired-ago',
|
||||||
|
defaultMessage: 'Expired {ago}',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
definePageMeta({
|
definePageMeta({
|
||||||
middleware: 'auth',
|
middleware: 'auth',
|
||||||
})
|
})
|
||||||
|
|
||||||
useHead({
|
useHead({
|
||||||
title: 'PATs - Modrinth',
|
title: `${formatMessage(messages.title)} - Modrinth`,
|
||||||
})
|
})
|
||||||
|
|
||||||
const data = useNuxtApp()
|
const data = useNuxtApp()
|
||||||
@@ -203,7 +349,7 @@ async function createPat() {
|
|||||||
} catch (err) {
|
} catch (err) {
|
||||||
data.$notify({
|
data.$notify({
|
||||||
group: 'main',
|
group: 'main',
|
||||||
title: 'An error occurred',
|
title: formatMessage(commonMessages.errorNotificationTitle),
|
||||||
text: err.data ? err.data.description : err,
|
text: err.data ? err.data.description : err,
|
||||||
type: 'error',
|
type: 'error',
|
||||||
})
|
})
|
||||||
@@ -229,7 +375,7 @@ async function editPat() {
|
|||||||
} catch (err) {
|
} catch (err) {
|
||||||
data.$notify({
|
data.$notify({
|
||||||
group: 'main',
|
group: 'main',
|
||||||
title: 'An error occurred',
|
title: formatMessage(commonMessages.errorNotificationTitle),
|
||||||
text: err.data ? err.data.description : err,
|
text: err.data ? err.data.description : err,
|
||||||
type: 'error',
|
type: 'error',
|
||||||
})
|
})
|
||||||
@@ -249,7 +395,7 @@ async function removePat(id) {
|
|||||||
} catch (err) {
|
} catch (err) {
|
||||||
data.$notify({
|
data.$notify({
|
||||||
group: 'main',
|
group: 'main',
|
||||||
title: 'An error occurred',
|
title: formatMessage(commonMessages.errorNotificationTitle),
|
||||||
text: err.data ? err.data.description : err,
|
text: err.data ? err.data.description : err,
|
||||||
type: 'error',
|
type: 'error',
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -15,6 +15,10 @@ export const commonMessages = defineMessages({
|
|||||||
id: 'button.continue',
|
id: 'button.continue',
|
||||||
defaultMessage: 'Continue',
|
defaultMessage: 'Continue',
|
||||||
},
|
},
|
||||||
|
createdAgoLabel: {
|
||||||
|
id: 'label.created-ago',
|
||||||
|
defaultMessage: 'Created {ago}',
|
||||||
|
},
|
||||||
dateAtTimeTooltip: {
|
dateAtTimeTooltip: {
|
||||||
id: 'tooltip.date-at-time',
|
id: 'tooltip.date-at-time',
|
||||||
defaultMessage: '{date, date, long} at {time, time, short}',
|
defaultMessage: '{date, date, long} at {time, time, short}',
|
||||||
@@ -71,6 +75,14 @@ export const commonMessages = defineMessages({
|
|||||||
id: 'button.save',
|
id: 'button.save',
|
||||||
defaultMessage: 'Save',
|
defaultMessage: 'Save',
|
||||||
},
|
},
|
||||||
|
saveChangesButton: {
|
||||||
|
id: 'button.save-changes',
|
||||||
|
defaultMessage: 'Save changes',
|
||||||
|
},
|
||||||
|
scopesLabel: {
|
||||||
|
id: 'label.scopes',
|
||||||
|
defaultMessage: 'Scopes',
|
||||||
|
},
|
||||||
titleLabel: {
|
titleLabel: {
|
||||||
id: 'label.title',
|
id: 'label.title',
|
||||||
defaultMessage: 'Title',
|
defaultMessage: 'Title',
|
||||||
|
|||||||
Reference in New Issue
Block a user