Sort filters and add translations for servers (#5493)

* Translate and sort server filters

* Set team_members to unknown[]

* Additional fixes after merge

* Additional translations

* Replace "IP" with "server address"

* Prioritize English and user language
This commit is contained in:
Jerozgen
2026-03-17 22:56:01 +03:00
committed by GitHub
parent 900a4df1b7
commit 58c1e225c8
17 changed files with 666 additions and 244 deletions

View File

@@ -91,7 +91,7 @@
<ButtonStyled class="flex-1">
<button @click="hide">
<XIcon />
{{ formatMessage(messages.close) }}
{{ formatMessage(commonMessages.closeButton) }}
</button>
</ButtonStyled>
<ButtonStyled color="green" class="flex-1">
@@ -108,6 +108,7 @@
<script setup lang="ts">
import { CheckIcon, DownloadIcon, XIcon } from '@modrinth/assets'
import { commonMessages } from '@modrinth/ui'
import { computed, nextTick, onUnmounted, ref } from 'vue'
import { defineMessages, useVIntl } from '../../composables/i18n'
@@ -142,10 +143,6 @@ const messages = defineMessages({
id: 'modal.open-in-app.opening-automatically',
defaultMessage: 'The Modrinth App will open automatically...',
},
close: {
id: 'modal.open-in-app.close',
defaultMessage: 'Close',
},
getApp: {
id: 'modal.open-in-app.get-app',
defaultMessage: 'Get Modrinth App',

View File

@@ -14,7 +14,7 @@
{{ organization.name }}
</span>
<span class="text-secondary text-sm font-medium flex items-center gap-1"
><OrganizationIcon /> Organization</span
><OrganizationIcon /> {{ formatMessage(messages.organization) }}</span
>
</div>
</AutoLink>
@@ -117,5 +117,9 @@ const messages = defineMessages({
id: 'project.about.creators.owner',
defaultMessage: 'Project owner',
},
organization: {
id: 'project.about.creators.organization',
defaultMessage: 'Organization',
},
})
</script>

View File

@@ -4,7 +4,7 @@
<div
v-if="ipAddress"
v-tooltip="`Copy Java IP: ${ipAddress}`"
v-tooltip="formatMessage(messages.addressTooltip)"
class="bg-button-bg flex gap-2 justify-between rounded-2xl items-center px-3 pr-1.5 h-12 cursor-pointer hover:bg-button-bg-hover hover:brightness-125 transition-all active:scale-95"
@click="handleCopyIP"
>
@@ -17,7 +17,7 @@
</div>
<section v-if="requiredContent" class="flex flex-col gap-2">
<h3 class="text-primary text-base m-0">Required content</h3>
<h3 class="text-primary text-base m-0">{{ formatMessage(messages.requiredContent) }}</h3>
<ServerModpackContentCard
:name="requiredContent.name"
:version-number="requiredContent.versionNumber ?? ''"
@@ -29,14 +29,16 @@
/>
</section>
<section v-if="recommendedVersions.length" class="flex flex-col gap-2">
<h3 class="text-primary text-base m-0">Minecraft: Java Edition</h3>
<h3 class="text-primary text-base m-0">{{ formatMessage(messages.minecraftJava) }}</h3>
<div class="flex flex-wrap gap-1.5">
<TagItem
v-for="version in formatVersionsForDisplay(recommendedVersions, tags.gameVersions)"
:key="`recommended-tag-${version}`"
>
{{ version }}
<template v-if="supportedVersions.length > 0"> (Recommended) </template>
<template v-if="supportedVersions.length > 0">
{{ formatMessage(messages.recommendedVersion) }}
</template>
</TagItem>
<TagItem
v-for="version in formatVersionsForDisplay(supportedVersionsList, tags.gameVersions)"
@@ -56,7 +58,7 @@
</div>
</section>
<section v-if="props.ping !== undefined || region" class="flex flex-col gap-2">
<h3 class="text-primary text-base m-0">Region</h3>
<h3 class="text-primary text-base m-0">{{ formatMessage(messages.region) }}</h3>
<div class="flex flex-wrap gap-1.5 items-center">
<ServerPing
v-if="projectV3?.status !== 'draft'"
@@ -67,10 +69,10 @@
</div>
</section>
<section v-if="languages.length > 0" class="flex flex-col gap-2">
<h3 class="text-primary text-base m-0">Languages</h3>
<h3 class="text-primary text-base m-0">{{ formatMessage(messages.languages) }}</h3>
<div class="flex flex-wrap gap-1.5">
<TagItem v-for="language in languages" :key="`${language}`">
{{ languageDisplay.find((l) => l.value === language)?.label ?? language }}
{{ SERVER_LANGUAGES[language] ? formatMessage(SERVER_LANGUAGES[language]) : language }}
</TagItem>
</div>
</section>
@@ -79,6 +81,7 @@
<script setup lang="ts">
import type { Labrinth } from '@modrinth/api-client'
import { CopyIcon, getLoaderIcon } from '@modrinth/assets'
import { SERVER_LANGUAGES } from '@modrinth/ui'
import { formatVersionsForDisplay, type GameVersionTag, type PlatformTag } from '@modrinth/utils'
import { computed } from 'vue'
@@ -171,48 +174,6 @@ function handleCopyIP() {
})
}
const languageDisplay = [
{ value: 'en', label: 'English' },
{ value: 'es', label: 'Spanish' },
{ value: 'pt', label: 'Portuguese' },
{ value: 'fr', label: 'French' },
{ value: 'de', label: 'German' },
{ value: 'it', label: 'Italian' },
{ value: 'nl', label: 'Dutch' },
{ value: 'ru', label: 'Russian' },
{ value: 'uk', label: 'Ukrainian' },
{ value: 'pl', label: 'Polish' },
{ value: 'cs', label: 'Czech' },
{ value: 'sk', label: 'Slovak' },
{ value: 'hu', label: 'Hungarian' },
{ value: 'ro', label: 'Romanian' },
{ value: 'bg', label: 'Bulgarian' },
{ value: 'hr', label: 'Croatian' },
{ value: 'sr', label: 'Serbian' },
{ value: 'el', label: 'Greek' },
{ value: 'tr', label: 'Turkish' },
{ value: 'ar', label: 'Arabic' },
{ value: 'he', label: 'Hebrew' },
{ value: 'hi', label: 'Hindi' },
{ value: 'bn', label: 'Bengali' },
{ value: 'ur', label: 'Urdu' },
{ value: 'zh', label: 'Chinese' },
{ value: 'ja', label: 'Japanese' },
{ value: 'ko', label: 'Korean' },
{ value: 'th', label: 'Thai' },
{ value: 'vi', label: 'Vietnamese' },
{ value: 'id', label: 'Indonesian' },
{ value: 'ms', label: 'Malay' },
{ value: 'tl', label: 'Filipino' },
{ value: 'sv', label: 'Swedish' },
{ value: 'no', label: 'Norwegian' },
{ value: 'da', label: 'Danish' },
{ value: 'fi', label: 'Finnish' },
{ value: 'lt', label: 'Lithuanian' },
{ value: 'lv', label: 'Latvian' },
{ value: 'et', label: 'Estonian' },
]
const messages = defineMessages({
copied: {
id: `project.about.server.copied`,
@@ -220,15 +181,35 @@ const messages = defineMessages({
},
copiedText: {
id: `project.about.server.copiedText`,
defaultMessage: 'IP address copied to clipboard',
defaultMessage: 'Server address copied to clipboard',
},
title: {
id: `project.about.server.title`,
defaultMessage: 'Server details',
},
latency: {
id: `project.about.server.latency`,
defaultMessage: 'Latency',
addressTooltip: {
id: `project.about.server.address.tooltip`,
defaultMessage: 'Copy Java server address',
},
requiredContent: {
id: `project.about.server.requiredContent`,
defaultMessage: 'Required content',
},
minecraftJava: {
id: `project.about.compatibility.game.minecraftJava`,
defaultMessage: 'Minecraft: Java Edition',
},
recommendedVersion: {
id: `project.about.server.recommendedVersion`,
defaultMessage: '(Recommended)',
},
region: {
id: `project.about.server.region`,
defaultMessage: 'Region',
},
languages: {
id: `project.about.server.languages`,
defaultMessage: 'Languages',
},
})
</script>

View File

@@ -1,6 +1,6 @@
<template>
<div v-if="allTags.length > 0" class="flex flex-col gap-3">
<h2 class="text-lg m-0">Tags</h2>
<h2 class="text-lg m-0">{{ formatMessage(messages.title) }}</h2>
<div class="flex flex-wrap gap-1">
<TagItem
v-for="tag in allTags"
@@ -16,6 +16,7 @@
import { computed } from 'vue'
import { useRouter } from 'vue-router'
import { defineMessages, useVIntl } from '../../composables'
import FormattedTag from '../base/FormattedTag.vue'
import TagItem from '../base/TagItem.vue'
@@ -42,6 +43,15 @@ const props = defineProps<{
}
}>()
const { formatMessage } = useVIntl()
const messages = defineMessages({
title: {
id: 'project.about.tags.title',
defaultMessage: 'Tags',
},
})
const allTags = computed(() => [
...props.project.categories,
...props.project.additional_categories,

View File

@@ -1,6 +1,6 @@
<template>
<div
v-tooltip="showCustomModpackTooltip ? 'This project uses a custom modpack' : name"
v-tooltip="showCustomModpackTooltip ? formatMessage(messages.customModpackTooltip) : name"
class="flex gap-1.5 items-center flex-shrink overflow-hidden smart-clickable:allow-pointer-events"
:class="[onclick ? 'hover:underline cursor-pointer' : '']"
@click="onclick"
@@ -13,6 +13,7 @@
</template>
<script setup lang="ts">
import { defineMessages, useVIntl } from '../../../composables'
import Avatar from '../../base/Avatar.vue'
defineProps<{
@@ -21,4 +22,13 @@ defineProps<{
onclick?: () => void
showCustomModpackTooltip?: boolean
}>()
const { formatMessage } = useVIntl()
const messages = defineMessages({
customModpackTooltip: {
id: `project.server.customModpackTooltip`,
defaultMessage: 'This project uses a custom modpack',
},
})
</script>

View File

@@ -4,7 +4,7 @@
<Avatar :src="icon" size="34px" class="!rounded-xl !shadow-none" />
<div class="flex flex-col items-start overflow-hidden">
<div
v-tooltip="showCustomModpackTooltip ? 'This project uses a custom modpack' : name"
v-tooltip="showCustomModpackTooltip ? formatMessage(messages.customModpackTooltip) : name"
class="truncate font-semibold text-sm max-w-full"
:class="onclickName ? 'hover:underline cursor-pointer' : ''"
@click="onclickName"
@@ -22,7 +22,7 @@
</div>
</div>
<ButtonStyled v-if="onclickDownload" circular type="transparent">
<button v-tooltip="'Download modpack'" @click="onclickDownload">
<button v-tooltip="formatMessage(messages.downloadModpack)" @click="onclickDownload">
<DownloadIcon />
</button>
</ButtonStyled>
@@ -32,6 +32,7 @@
<script setup lang="ts">
import { DownloadIcon } from '@modrinth/assets/generated-icons'
import { defineMessages, useVIntl } from '../../../composables'
import Avatar from '../../base/Avatar.vue'
import ButtonStyled from '../../base/ButtonStyled.vue'
@@ -44,4 +45,17 @@ defineProps<{
onclickDownload?: () => void
showCustomModpackTooltip?: boolean
}>()
const { formatMessage } = useVIntl()
const messages = defineMessages({
customModpackTooltip: {
id: `project.server.customModpackTooltip`,
defaultMessage: 'This project uses a custom modpack',
},
downloadModpack: {
id: `project.about.server.downloadModpack`,
defaultMessage: 'Download modpack',
},
})
</script>

View File

@@ -2,7 +2,7 @@
import { SignalIcon } from '@modrinth/assets'
import { computed } from 'vue'
import { defineMessage, useVIntl } from '../../../composables'
import { defineMessages, useVIntl } from '../../../composables'
import { TagItem } from '../../base'
const props = defineProps<{
@@ -10,9 +10,23 @@ const props = defineProps<{
statusOnline?: boolean
}>()
const pingMessage = defineMessage({
id: 'project.server.ping.ms',
defaultMessage: '{ping, number} ms',
const messages = defineMessages({
ping: {
id: 'project.server.ping.ms',
defaultMessage: '{ping, number} ms',
},
online: {
id: 'project.server.status.online',
defaultMessage: 'Online',
},
offline: {
id: 'project.server.status.offline',
defaultMessage: 'Offline',
},
offlineTooltip: {
id: 'project.server.status.offline.tooltip',
defaultMessage: 'Server is offline',
},
})
const { formatMessage } = useVIntl()
@@ -37,19 +51,19 @@ const pingClass = computed(() => {
:class="pingClass"
>
<template v-if="ping !== undefined">
{{ formatMessage(pingMessage, { ping }) }}
{{ formatMessage(messages.ping, { ping }) }}
</template>
<template v-else>
<SignalIcon />
Online
{{ formatMessage(messages.online) }}
</template>
</TagItem>
<TagItem
v-else
v-tooltip="'Server is offline'"
v-tooltip="formatMessage(messages.offlineTooltip)"
class="border !border-solid border-red bg-highlight-red text-red smart-clickable:allow-pointer-events w-max"
>
<SignalIcon />
Offline
{{ formatMessage(messages.offline) }}
</TagItem>
</template>

View File

@@ -1,25 +1,28 @@
<script setup lang="ts">
import { computed } from 'vue'
import { defineMessage, useVIntl } from '../../../composables'
import { SERVER_REGIONS } from '../../../utils'
import { TagItem } from '../../base'
const { region } = defineProps<{
region: string
}>()
const regionNames: Record<string, string> = {
us_east: 'US East',
us_west: 'US West',
europe: 'Europe',
asia: 'Asia',
australia: 'Australia',
south_america: 'South America',
middle_east: 'Middle East',
russia: 'Russia',
}
const { formatMessage } = useVIntl()
const regionName = computed(() => regionNames[region] ?? region)
const tooltip = defineMessage({
id: 'project.server.region.tooltip',
defaultMessage: 'Server hosted in {regionName}',
})
const regionName = computed(() => {
const name = SERVER_REGIONS[region]
if (name) return formatMessage(name)
return region
})
</script>
<template>
<TagItem v-tooltip="`Server hosted in ${regionName}`">{{ regionName }}</TagItem>
<TagItem v-tooltip="formatMessage(tooltip, { regionName })">{{ regionName }}</TagItem>
</template>