Compare commits

..
Author SHA1 Message Date
Calum H. (IMB11) 69156d149b fix: underscored users not searchable 2026-06-11 13:20:08 +01:00
630 changed files with 4629 additions and 32605 deletions
+1 -1
View File
@@ -20,7 +20,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: taiki-e/install-action@fd2f5e3d644b484055ebf4268f474c565f148f25 # v2.81.9
- uses: taiki-e/install-action@b5fddbb5361bce8a06fb168c9d403a6cc552b084 # v2.75.29
with:
tool: tombi
- run: tombi lint
+5 -15
View File
@@ -20,14 +20,6 @@ on:
required: true
type: string
description: 'The environment to deploy to (staging-preview or production-preview)'
head_sha:
required: false
type: string
description: 'The commit SHA to deploy (defaults to github.sha)'
head_ref:
required: false
type: string
description: 'The branch name to deploy (defaults to github.ref_name)'
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}-${{ inputs.environment || 'push' }}
@@ -48,14 +40,12 @@ jobs:
- name: Configure environment
id: meta
env:
ENV_INPUT: ${{ inputs.environment }}
REF: ${{ github.ref }}
DEPLOY_SHA: ${{ inputs.head_sha || github.sha }}
run: |
echo "cmd=deploy" >> $GITHUB_OUTPUT
SHA_SHORT="${DEPLOY_SHA::8}"
ENV_INPUT="${{ inputs.environment }}"
REF="${{ github.ref }}"
SHA_SHORT="${GITHUB_SHA::8}"
if [ "$ENV_INPUT" = "staging-preview" ]; then
echo "env=staging" >> $GITHUB_OUTPUT
@@ -116,8 +106,8 @@ jobs:
working-directory: ./apps/frontend
run: pnpm build
env:
CF_PAGES_BRANCH: ${{ inputs.head_ref || github.ref_name }}
CF_PAGES_COMMIT_SHA: ${{ inputs.head_sha || github.sha }}
CF_PAGES_BRANCH: ${{ github.ref_name }}
CF_PAGES_COMMIT_SHA: ${{ github.sha }}
CF_PAGES_URL: ${{ steps.meta.outputs.url }}
BUILD_ENV: ${{ steps.meta.outputs.env }}
PREVIEW: ${{ inputs.environment != '' && 'true' || 'false' }}
+3 -9
View File
@@ -24,8 +24,6 @@ jobs:
environment: [staging-preview, production-preview]
with:
environment: ${{ matrix.environment }}
head_sha: ${{ github.event.pull_request.head.sha }}
head_ref: ${{ github.event.pull_request.head.ref }}
deploy-storybook:
if: github.repository_owner == 'modrinth' && github.event.pull_request.head.repo.full_name == github.repository
@@ -70,9 +68,7 @@ jobs:
- name: Configure short SHA
id: meta
env:
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
run: echo "sha_short=${HEAD_SHA::8}" >> $GITHUB_OUTPUT
run: echo "sha_short=${GITHUB_SHA::8}" >> $GITHUB_OUTPUT
- name: Deploy Storybook preview
uses: cloudflare/wrangler-action@9acf94ace14e7dc412b076f2c5c20b8ce93c79cd # v3.15.0
@@ -97,8 +93,6 @@ jobs:
- name: Read deployment URLs
id: urls
env:
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
run: |
STAGING_PREVIEW_URL=$(cat deployment-url-staging-preview.txt)
PRODUCTION_PREVIEW_URL=$(cat deployment-url-production-preview.txt)
@@ -108,7 +102,7 @@ jobs:
echo "staging-preview-url=$STAGING_PREVIEW_URL" >> $GITHUB_OUTPUT
echo "production-preview-url=$PRODUCTION_PREVIEW_URL" >> $GITHUB_OUTPUT
echo "storybook-preview-url=https://git-${HEAD_SHA::8}-storybook.modrinth.workers.dev" >> $GITHUB_OUTPUT
echo "storybook-preview-url=https://git-${GITHUB_SHA::8}-storybook.modrinth.workers.dev" >> $GITHUB_OUTPUT
- name: Find comment
if: github.event_name == 'pull_request'
@@ -129,7 +123,7 @@ jobs:
body: |
## Frontend previews
Last deployed commit is [${{ github.event.pull_request.head.sha }}](${{ github.event.pull_request.head.repo.html_url }}/commit/${{ github.event.pull_request.head.sha }})
Last deployed commit is [${{ github.sha }}](${{ github.event.pull_request.head.repo.html_url }}/commit/${{ github.sha }})
| Environment | URL |
|-------------|-----|
-6
View File
@@ -99,12 +99,6 @@ jobs:
- name: Setup mold
uses: rui314/setup-mold@9c9c13bf4c3f1adef0cc596abc155580bcb04444 # v1 / Mold 2.41.0
- name: Install build dependencies
uses: awalsh128/cache-apt-pkgs-action@acb598e5ddbc6f68a970c5da0688d2f3a9f04d05 # v1.6.0
with:
packages: cmake libcurl4-openssl-dev
version: v2 # cache key
- name: Cache Cargo registry and index
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
+3 -3
View File
@@ -99,8 +99,8 @@ jobs:
- name: Install build dependencies
uses: awalsh128/cache-apt-pkgs-action@acb598e5ddbc6f68a970c5da0688d2f3a9f04d05 # v1.6.0
with:
packages: cmake libcurl4-openssl-dev libwebkit2gtk-4.1-dev libayatana-appindicator3-dev librsvg2-dev
version: v2 # cache key
packages: libwebkit2gtk-4.1-dev libayatana-appindicator3-dev librsvg2-dev
version: v1 # cache key
- name: Setup Node
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
@@ -153,7 +153,7 @@ jobs:
uses: cargo-bins/cargo-binstall@dc19f1e48450eefe5a29b8da6c6b00a87d730b37 # v1.18.1
- name: Setup nextest
run: cargo binstall --no-confirm --secure --locked cargo-nextest@0.9.133
run: cargo binstall --no-confirm --secure cargo-nextest@0.9.133
# cargo-binstall does not have pre-built binaries for sqlx-cli, so we fall
# back to a cached cargo install
-1
View File
@@ -22,7 +22,6 @@ node_modules
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
!.vscode/i18n-ally-custom-framework.yml
# IDE - IntelliJ
.idea/*
+1 -1
View File
@@ -1,3 +1,3 @@
{
"recommendations": ["esbenp.prettier-vscode", "Vue.volar", "rust-lang.rust-analyzer", "lokalise.i18n-ally"]
"recommendations": ["esbenp.prettier-vscode", "Vue.volar", "rust-lang.rust-analyzer"]
}
-10
View File
@@ -1,10 +0,0 @@
languageIds:
- vue
- typescript
- javascript
- typescriptreact
usageMatchRegex:
- id:\s*['"]({key})['"]
monopoly: true
+1 -11
View File
@@ -31,15 +31,5 @@
"editor.defaultFormatter": "rust-lang.rust-analyzer"
},
"css.lint.unknownAtRules": "ignore",
"scss.lint.unknownAtRules": "ignore",
"i18n-ally.localesPaths": [
"packages/ui/src/locales",
"apps/frontend/src/locales",
"packages/moderation/src/locales"
],
"i18n-ally.pathMatcher": "{locale}/index.{ext}",
"i18n-ally.keystyle": "flat",
"i18n-ally.sourceLanguage": "en-US",
"i18n-ally.namespace": false,
"i18n-ally.includeSubfolders": true
"scss.lint.unknownAtRules": "ignore"
}
Generated
+1 -45
View File
@@ -4817,7 +4817,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4b0f83760fb341a774ed326568e19f5a863af4a952def8c39f9ab92fd95b88e5"
dependencies = [
"equivalent",
"hashbrown 0.16.0",
"hashbrown 0.15.5",
"serde",
"serde_core",
]
@@ -5277,7 +5277,6 @@ dependencies = [
"quick-xml 0.38.3",
"rand 0.8.5",
"rand_chacha 0.3.1",
"rdkafka",
"redis",
"regex",
"reqwest 0.12.24",
@@ -5490,18 +5489,6 @@ dependencies = [
"zlib-rs",
]
[[package]]
name = "libz-sys"
version = "1.1.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85bc9657773828b90eeb625adff10eeac83cc21bbfd8e23a03eaa8a33c9e28d9"
dependencies = [
"cc",
"libc",
"pkg-config",
"vcpkg",
]
[[package]]
name = "linked-hash-map"
version = "0.5.6"
@@ -7980,37 +7967,6 @@ dependencies = [
"crossbeam-utils",
]
[[package]]
name = "rdkafka"
version = "0.36.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1beea247b9a7600a81d4cc33f659ce1a77e1988323d7d2809c7ed1c21f4c316d"
dependencies = [
"futures-channel",
"futures-util",
"libc",
"log",
"rdkafka-sys",
"serde",
"serde_derive",
"serde_json",
"slab",
"tokio",
]
[[package]]
name = "rdkafka-sys"
version = "4.10.0+2.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e234cf318915c1059d4921ef7f75616b5219b10b46e9f3a511a15eb4b56a3f77"
dependencies = [
"cmake",
"libc",
"libz-sys",
"num_enum",
"pkg-config",
]
[[package]]
name = "redis"
version = "0.32.7"
-2
View File
@@ -140,7 +140,6 @@ quick-xml = "0.38.3"
quote = { version = "1.0" }
rand = "=0.8.5" # Locked on 0.8 until argon2 and p256 update to 0.9
rand_chacha = "=0.3.1" # Locked on 0.3 until we can update rand to 0.9
rdkafka = { version = "0.36.2", features = ["cmake-build"] }
redis = "0.32.7"
regex = "1.12.2"
reqwest = { version = "0.12.24", default-features = false }
@@ -270,7 +269,6 @@ redundant_type_annotations = "warn"
result_large_err = "allow"
todo = "warn"
too_many_arguments = "allow"
type_complexity = "allow"
uninlined_format_args = "warn"
unnested_or_patterns = "warn"
wildcard_dependencies = "warn"
+1 -2
View File
@@ -44,8 +44,7 @@
"vue": "^3.5.13",
"vue-i18n": "^10.0.0",
"vue-router": "^4.6.0",
"vue-virtual-scroller": "v2.0.0-beta.8",
"vuedraggable": "^4.1.0"
"vue-virtual-scroller": "v2.0.0-beta.8"
},
"devDependencies": {
"@eslint/compat": "^1.1.1",
@@ -89,9 +89,14 @@ const initFiles = async () => {
disabled:
folder === 'profile.json' ||
folder.startsWith('modrinth_logs') ||
folder.startsWith('.fabric') ||
folder.startsWith('__MACOSX'),
folder.startsWith('.fabric'),
}))
.filter(
(pathData) =>
!pathData.path.includes('.DS_Store') &&
pathData.path !== 'mods/.connector' &&
!pathData.path.startsWith('mods/.connector/'),
)
.forEach((pathData) => {
const parent = pathData.path.split(sep).slice(0, -1).join(sep)
if (parent !== '') {
@@ -13,7 +13,7 @@ import {
} from '@modrinth/ui'
import type { GameVersionTag, PlatformTag } from '@modrinth/utils'
import { useQuery, useQueryClient } from '@tanstack/vue-query'
import { computed, ref } from 'vue'
import { computed, ref, shallowRef } from 'vue'
import { trackEvent } from '@/helpers/analytics'
import { get_project_versions, get_version } from '@/helpers/cache'
@@ -46,62 +46,55 @@ debug('metadata load: start', {
installStage: instance.value.install_stage,
})
function getSupportedModpackLoaders() {
return get_loaders().then((value: PlatformTag[]) =>
value
.filter((item) => item.supported_project_types.includes('modpack') || item.name === 'vanilla')
.sort((a, b) => (a.name === 'vanilla' ? -1 : b.name === 'vanilla' ? 1 : 0)),
)
}
const [
fabric_versions,
forge_versions,
quilt_versions,
neoforge_versions,
all_game_versions,
loaders,
] = await Promise.all([
get_loader_versions('fabric')
.then((manifest: Manifest) => shallowRef(manifest))
.catch(handleError),
get_loader_versions('forge')
.then((manifest: Manifest) => shallowRef(manifest))
.catch(handleError),
get_loader_versions('quilt')
.then((manifest: Manifest) => shallowRef(manifest))
.catch(handleError),
get_loader_versions('neo')
.then((manifest: Manifest) => shallowRef(manifest))
.catch(handleError),
get_game_versions()
.then((gameVersions: GameVersionTag[]) => shallowRef(gameVersions))
.catch(handleError),
get_loaders()
.then((value: PlatformTag[]) =>
value
.filter(
(item) => item.supported_project_types.includes('modpack') || item.name === 'vanilla',
)
.sort((a, b) => (a.name === 'vanilla' ? -1 : b.name === 'vanilla' ? 1 : 0)),
)
.then((loader: PlatformTag[]) => ref(loader))
.catch(handleError),
])
const fabricVersionsQuery = useQuery({
queryKey: ['instance-settings', 'loader-versions', 'fabric'],
queryFn: () => get_loader_versions('fabric') as Promise<Manifest>,
})
const forgeVersionsQuery = useQuery({
queryKey: ['instance-settings', 'loader-versions', 'forge'],
queryFn: () => get_loader_versions('forge') as Promise<Manifest>,
})
const quiltVersionsQuery = useQuery({
queryKey: ['instance-settings', 'loader-versions', 'quilt'],
queryFn: () => get_loader_versions('quilt') as Promise<Manifest>,
})
const neoforgeVersionsQuery = useQuery({
queryKey: ['instance-settings', 'loader-versions', 'neo'],
queryFn: () => get_loader_versions('neo') as Promise<Manifest>,
})
const gameVersionsQuery = useQuery({
queryKey: ['instance-settings', 'game-versions'],
queryFn: () => get_game_versions() as Promise<GameVersionTag[]>,
})
const loadersQuery = useQuery({
queryKey: ['instance-settings', 'loaders', 'modpack'],
queryFn: getSupportedModpackLoaders,
debug('metadata load: done', {
hasFabricManifest: !!fabric_versions?.value,
hasForgeManifest: !!forge_versions?.value,
hasQuiltManifest: !!quilt_versions?.value,
hasNeoforgeManifest: !!neoforge_versions?.value,
gameVersions: all_game_versions?.value?.length ?? 0,
availablePlatforms: loaders?.value?.map((loader) => loader.name) ?? [],
})
const metadataLoading = computed(() =>
[
fabricVersionsQuery,
forgeVersionsQuery,
quiltVersionsQuery,
neoforgeVersionsQuery,
gameVersionsQuery,
loadersQuery,
].some((query) => query.isLoading.value),
)
debug('metadata queries configured', {
instancePath: instance.value.path,
loader: instance.value.loader,
gameVersion: instance.value.game_version,
})
const modpackInfoQuery = useQuery({
const { data: modpackInfo } = useQuery({
queryKey: computed(() => ['linkedModpackInfo', instance.value.path]),
queryFn: () => get_linked_modpack_info(instance.value.path, 'must_revalidate'),
enabled: computed(() => !!instance.value.linked_data?.project_id && !offline),
})
const modpackInfo = modpackInfoQuery.data
const repairing = ref(false)
const reinstalling = ref(false)
@@ -114,17 +107,17 @@ const messages = defineMessages({
})
function getManifest(loader: string) {
const map: Record<string, Manifest | undefined> = {
fabric: fabricVersionsQuery.data.value,
forge: forgeVersionsQuery.data.value,
quilt: quiltVersionsQuery.data.value,
neoforge: neoforgeVersionsQuery.data.value,
const map: Record<string, typeof fabric_versions> = {
fabric: fabric_versions,
forge: forge_versions,
quilt: quilt_versions,
neoforge: neoforge_versions,
}
const manifest = map[loader]
debug('getManifest:', {
loader,
hasManifest: !!manifest,
gameVersions: manifest?.gameVersions?.length ?? 0,
hasManifest: !!manifest?.value,
gameVersions: manifest?.value?.gameVersions?.length ?? 0,
})
return manifest
}
@@ -150,7 +143,7 @@ provideAppBackup({
provideInstallationSettings({
closeSettings: closeModal,
loading: computed(() => metadataLoading.value || modpackInfoQuery.isLoading.value),
loading: ref(false),
installationInfo: computed(() => {
const rows = [
{
@@ -192,14 +185,14 @@ provideInstallationSettings({
currentPlatform: computed(() => instance.value.loader),
currentGameVersion: computed(() => instance.value.game_version),
currentLoaderVersion: computed(() => instance.value.loader_version ?? ''),
availablePlatforms: computed(() => loadersQuery.data.value?.map((x) => x.name) ?? []),
availablePlatforms: loaders?.value?.map((x) => x.name) ?? [],
resolveGameVersions(loader, showSnapshots) {
const versions = gameVersionsQuery.data.value ?? []
const versions = all_game_versions?.value ?? []
const filtered = versions.filter((item) => {
if (loader === 'vanilla') return true
const manifest = getManifest(loader)
return !!manifest?.gameVersions?.some((x) => item.version === x.id)
return !!manifest?.value?.gameVersions?.some((x) => item.version === x.id)
})
const result = (
showSnapshots ? filtered : filtered.filter((x) => x.version_type === 'release')
@@ -220,12 +213,12 @@ provideInstallationSettings({
return []
}
const manifest = getManifest(loader)
if (!manifest) {
if (!manifest?.value) {
debug('resolveLoaderVersions: no manifest', { loader, gameVersion })
return []
}
if (loader === 'fabric' || loader === 'quilt') {
const result = manifest.gameVersions[0]?.loaders ?? []
const result = manifest.value.gameVersions[0]?.loaders ?? []
debug('resolveLoaderVersions: fabric/quilt result', {
loader,
gameVersion,
@@ -233,13 +226,14 @@ provideInstallationSettings({
})
return result
}
const result = manifest.gameVersions?.find((item) => item.id === gameVersion)?.loaders ?? []
const result =
manifest.value.gameVersions?.find((item) => item.id === gameVersion)?.loaders ?? []
debug('resolveLoaderVersions: result', { loader, gameVersion, count: result.length })
return result
},
resolveHasSnapshots(loader) {
const versions = gameVersionsQuery.data.value ?? []
const versions = all_game_versions?.value ?? []
if (loader === 'vanilla') {
const result = versions.some((x) => x.version_type !== 'release')
debug('resolveHasSnapshots: vanilla', { loader, result })
@@ -247,7 +241,7 @@ provideInstallationSettings({
}
const manifest = getManifest(loader)
const supported = versions.filter(
(item) => !!manifest?.gameVersions?.some((x) => item.version === x.id),
(item) => !!manifest?.value?.gameVersions?.some((x) => item.version === x.id),
)
const result = supported.some((x) => x.version_type !== 'release')
debug('resolveHasSnapshots:', {
@@ -16,8 +16,7 @@ import {
type TabbedModalTab,
useVIntl,
} from '@modrinth/ui'
import type { PlatformTag } from '@modrinth/utils'
import { useQuery } from '@tanstack/vue-query'
import { useQueryClient } from '@tanstack/vue-query'
import { convertFileSrc } from '@tauri-apps/api/core'
import { computed, nextTick, ref, watch } from 'vue'
@@ -27,9 +26,7 @@ import InstallationSettings from '@/components/ui/instance_settings/Installation
import JavaSettings from '@/components/ui/instance_settings/JavaSettings.vue'
import WindowSettings from '@/components/ui/instance_settings/WindowSettings.vue'
import { get_project_v3 } from '@/helpers/cache'
import { get_loader_versions } from '@/helpers/metadata'
import { get_linked_modpack_info } from '@/helpers/profile'
import { get_game_versions, get_loaders } from '@/helpers/tags'
import { provideInstanceSettings } from '@/providers/instance-settings'
import type { GameInstance } from '../../../helpers/types'
@@ -48,6 +45,7 @@ const isMinecraftServer = ref(false)
const handleUnlinked = () => emit('unlinked')
const instanceRef = computed(() => props.instance)
const queryClient = useQueryClient()
const tabbedModal = ref<InstanceType<typeof TabbedModal> | null>(null)
function hide() {
@@ -122,46 +120,13 @@ const tabs = computed<TabbedModalTab[]>(() => [
},
])
function getSupportedModpackLoaders() {
return get_loaders().then((value: PlatformTag[]) =>
value
.filter((item) => item.supported_project_types.includes('modpack') || item.name === 'vanilla')
.sort((a, b) => (a.name === 'vanilla' ? -1 : b.name === 'vanilla' ? 1 : 0)),
)
}
// Preload
useQuery({
queryKey: ['instance-settings', 'loader-versions', 'fabric'],
queryFn: () => get_loader_versions('fabric'),
})
useQuery({
queryKey: ['instance-settings', 'loader-versions', 'forge'],
queryFn: () => get_loader_versions('forge'),
})
useQuery({
queryKey: ['instance-settings', 'loader-versions', 'quilt'],
queryFn: () => get_loader_versions('quilt'),
})
useQuery({
queryKey: ['instance-settings', 'loader-versions', 'neo'],
queryFn: () => get_loader_versions('neo'),
})
useQuery({
queryKey: ['instance-settings', 'game-versions'],
queryFn: get_game_versions,
})
useQuery({
queryKey: ['instance-settings', 'loaders', 'modpack'],
queryFn: getSupportedModpackLoaders,
})
useQuery({
queryKey: computed(() => ['linkedModpackInfo', props.instance.path]),
queryFn: () => get_linked_modpack_info(props.instance.path, 'stale_while_revalidate'),
enabled: computed(() => !!props.instance.linked_data?.project_id && !props.offline),
})
function show(tabIndex?: number) {
if (props.instance.linked_data?.project_id) {
queryClient.prefetchQuery({
queryKey: ['linkedModpackInfo', props.instance.path],
queryFn: () => get_linked_modpack_info(props.instance.path, 'stale_while_revalidate'),
})
}
tabbedModal.value?.show()
if (tabIndex !== undefined) {
nextTick(() => tabbedModal.value?.setTab(tabIndex))
@@ -148,6 +148,7 @@ import { arrayBufferToBase64 } from '@modrinth/utils'
import { computed, nextTick, ref, useTemplateRef, watch } from 'vue'
import {
add_and_equip_custom_skin,
type Cape,
determineModelType,
equip_skin,
@@ -439,22 +440,9 @@ async function save() {
const bytes: Uint8Array = new Uint8Array(await (await fetch(textureUrl)).arrayBuffer())
if (mode.value === 'new') {
const addedSkin = await save_custom_skin(
{
texture_key: '',
variant: variant.value,
cape_id: selectedCape.value?.id,
texture: textureUrl,
source: 'custom',
is_equipped: false,
},
bytes,
variant.value,
selectedCape.value,
true,
)
const addedSkin = await add_and_equip_custom_skin(bytes, variant.value, selectedCape.value)
emit('saved', {
applied: false,
applied: true,
skin: addedSkin,
})
} else {
@@ -13,7 +13,6 @@ import {
import { useElementSize, useWindowSize } from '@vueuse/core'
import { Tooltip } from 'floating-vue'
import { computed, nextTick, onUnmounted, ref, useTemplateRef, watch } from 'vue'
import Draggable from 'vuedraggable'
import type { RenderResult } from '@/helpers/rendering/batch-skin-renderer.ts'
import type { Skin } from '@/helpers/skins.ts'
@@ -90,7 +89,6 @@ const emit = defineEmits<{
select: [skin: Skin]
edit: [skin: Skin, event: MouseEvent]
delete: [skin: Skin]
'reorder-saved-skins': [skins: Skin[]]
'add-skin': []
'add-skin-dragenter': [event: DragEvent]
'add-skin-dragover': [event: DragEvent]
@@ -156,11 +154,6 @@ const sections = computed<SkinSection[]>(() => [
})),
])
const draggableSavedSkins = ref<Skin[]>([])
const isDraggingSavedSkin = ref(false)
const canReorderSavedSkins = computed(() => draggableSavedSkins.value.length > 1)
const fixedSavedSkins = computed(() => props.savedSkins.filter((skin) => !canDragSavedSkin(skin)))
const sectionLayouts = computed(() => {
const layouts: Array<{ section: SkinSection; top: number; height: number; index: number }> = []
let top = 0
@@ -217,18 +210,6 @@ watch(
{ immediate: true },
)
watch(
() => props.savedSkins,
(nextSkins) => {
if (isDraggingSavedSkin.value) {
return
}
draggableSavedSkins.value = nextSkins.filter(canDragSavedSkin)
},
{ immediate: true },
)
watch(
listWidth,
(width) => {
@@ -277,40 +258,6 @@ function skinKey(skin: Skin, prefix: string) {
return `${prefix}-${skin.source}-${skin.texture_key}-${skin.variant}-${skin.cape_id ?? 'no-cape'}`
}
function savedSkinKey(skin: Skin) {
return skinKey(skin, 'saved-skin')
}
function canDragSavedSkin(skin: Skin) {
return skin.source === 'custom' || skin.source === 'custom_external'
}
function doSkinOrdersMatch(firstSkins: Skin[], secondSkins: Skin[]) {
const draggableSecondSkins = secondSkins.filter(canDragSavedSkin)
return (
firstSkins.length === draggableSecondSkins.length &&
firstSkins.every(
(skin, index) => savedSkinKey(skin) === savedSkinKey(draggableSecondSkins[index]),
)
)
}
function onSavedSkinDragStart() {
isDraggingSavedSkin.value = true
}
function onSavedSkinDragEnd() {
isDraggingSavedSkin.value = false
if (doSkinOrdersMatch(draggableSavedSkins.value, props.savedSkins)) {
draggableSavedSkins.value = props.savedSkins.filter(canDragSavedSkin)
return
}
emit('reorder-saved-skins', [...draggableSavedSkins.value])
}
function isSectionOpen(key: string) {
return openSectionKeys.value.has(key)
}
@@ -408,125 +355,63 @@ defineExpose({ getAddSkinButtonElement })
</Tooltip>
</template>
<Draggable
<div
v-if="section.kind === 'saved'"
:list="draggableSavedSkins"
class="grid w-full grid-cols-3 gap-3 min-[1300px]:grid-cols-4 min-[1750px]:grid-cols-5 min-[2050px]:grid-cols-6"
:item-key="savedSkinKey"
:disabled="readOnly || !canReorderSavedSkins"
:animation="250"
:swap-threshold="1"
:invert-swap="false"
:force-fallback="true"
:fallback-on-body="true"
:fallback-tolerance="4"
ghost-class="skin-reorder-ghost"
chosen-class="skin-reorder-chosen"
drag-class="skin-reorder-drag"
fallback-class="skin-reorder-fallback"
@start="onSavedSkinDragStart"
@end="onSavedSkinDragEnd"
>
<template #header>
<SkinLikeTextButton
ref="addSkinButton"
class="aspect-[31/40] w-full min-w-0 box-border rounded-[20px]"
dropzone
:disabled="readOnly"
:drag-active="!readOnly && isAddSkinButtonDragActive"
@click="emit('add-skin')"
@dragenter="emit('add-skin-dragenter', $event)"
@dragover="emit('add-skin-dragover', $event)"
@dragleave="emit('add-skin-dragleave', $event)"
@drop="emit('add-skin-drop', $event)"
>
<template #icon>
<PlusIcon class="size-8" />
</template>
{{ formatMessage(messages.addSkinButton) }}
<template #subtitle>{{ formatMessage(messages.dragAndDropSubtitle) }}</template>
</SkinLikeTextButton>
</template>
<SkinLikeTextButton
ref="addSkinButton"
class="aspect-[31/40] w-full min-w-0 box-border rounded-[20px]"
dropzone
:disabled="readOnly"
:drag-active="!readOnly && isAddSkinButtonDragActive"
@click="emit('add-skin')"
@dragenter="emit('add-skin-dragenter', $event)"
@dragover="emit('add-skin-dragover', $event)"
@dragleave="emit('add-skin-dragleave', $event)"
@drop="emit('add-skin-drop', $event)"
>
<template #icon>
<PlusIcon class="size-8" />
</template>
{{ formatMessage(messages.addSkinButton) }}
<template #subtitle>{{ formatMessage(messages.dragAndDropSubtitle) }}</template>
</SkinLikeTextButton>
<template #item="{ element: skin }">
<div
:key="savedSkinKey(skin)"
class="relative aspect-[31/40] w-full min-w-0 box-border rounded-[20px]"
>
<SkinButton
class="h-full w-full min-w-0 box-border rounded-[20px]"
:forward-image-src="getBakedSkinTextures(skin)?.forwards"
:selected="isSkinSelected(skin)"
:active="isSkinActive(skin)"
:disabled="readOnly"
:is-dragging="isDraggingSavedSkin"
@select="emit('select', skin)"
>
<template v-if="!readOnly" #overlay-buttons>
<ButtonStyled color="brand">
<button
:aria-label="formatMessage(messages.editSkinButton)"
class="pointer-events-auto"
@click.stop="(event: MouseEvent) => emit('edit', skin, event)"
>
<EditIcon /> {{ formatMessage(commonMessages.editButton) }}
</button>
</ButtonStyled>
<ButtonStyled v-show="!skin.is_equipped" circular color="red">
<button
v-tooltip="formatMessage(messages.deleteSkinButton)"
:aria-label="formatMessage(messages.deleteSkinButton)"
class="!rounded-[100%] pointer-events-auto"
@click.stop="emit('delete', skin)"
>
<TrashIcon />
</button>
</ButtonStyled>
</template>
</SkinButton>
</div>
</template>
<template #footer>
<div
v-for="skin in fixedSavedSkins"
:key="savedSkinKey(skin)"
class="relative aspect-[31/40] w-full min-w-0 box-border rounded-[20px]"
>
<SkinButton
class="h-full w-full min-w-0 box-border rounded-[20px]"
:forward-image-src="getBakedSkinTextures(skin)?.forwards"
:selected="isSkinSelected(skin)"
:active="isSkinActive(skin)"
:disabled="readOnly"
:is-dragging="isDraggingSavedSkin"
@select="emit('select', skin)"
>
<template v-if="!readOnly" #overlay-buttons>
<ButtonStyled color="brand">
<button
:aria-label="formatMessage(messages.editSkinButton)"
class="pointer-events-auto"
@click.stop="(event: MouseEvent) => emit('edit', skin, event)"
>
<EditIcon /> {{ formatMessage(commonMessages.editButton) }}
</button>
</ButtonStyled>
<ButtonStyled v-show="!skin.is_equipped" circular color="red">
<button
v-tooltip="formatMessage(messages.deleteSkinButton)"
:aria-label="formatMessage(messages.deleteSkinButton)"
class="!rounded-[100%] pointer-events-auto"
@click.stop="emit('delete', skin)"
>
<TrashIcon />
</button>
</ButtonStyled>
</template>
</SkinButton>
</div>
</template>
</Draggable>
<SkinButton
v-for="skin in section.skins"
:key="skinKey(skin, 'saved-skin')"
class="aspect-[31/40] w-full min-w-0 box-border rounded-[20px]"
:forward-image-src="getBakedSkinTextures(skin)?.forwards"
:backward-image-src="getBakedSkinTextures(skin)?.backwards"
:selected="isSkinSelected(skin)"
:active="isSkinActive(skin)"
:disabled="readOnly"
@select="emit('select', skin)"
>
<template v-if="!readOnly" #overlay-buttons>
<ButtonStyled color="brand">
<button
:aria-label="formatMessage(messages.editSkinButton)"
class="pointer-events-auto"
@click.stop="(event: MouseEvent) => emit('edit', skin, event)"
>
<EditIcon /> {{ formatMessage(commonMessages.editButton) }}
</button>
</ButtonStyled>
<ButtonStyled v-show="!skin.is_equipped" circular color="red">
<button
v-tooltip="formatMessage(messages.deleteSkinButton)"
:aria-label="formatMessage(messages.deleteSkinButton)"
class="!rounded-[100%] pointer-events-auto"
@click.stop="emit('delete', skin)"
>
<TrashIcon />
</button>
</ButtonStyled>
</template>
</SkinButton>
</div>
<div
v-else
@@ -537,11 +422,11 @@ defineExpose({ getAddSkinButtonElement })
:key="skinKey(skin, section.key)"
class="aspect-[31/40] w-full min-w-0 box-border rounded-[20px]"
:forward-image-src="getBakedSkinTextures(skin)?.forwards"
:backward-image-src="getBakedSkinTextures(skin)?.backwards"
:selected="isSkinSelected(skin)"
:active="isSkinActive(skin)"
:tooltip="skin.name"
:disabled="readOnly"
:is-dragging="isDraggingSavedSkin"
@select="emit('select', skin)"
>
<template #overlay-buttons>
@@ -561,18 +446,3 @@ defineExpose({ getAddSkinButtonElement })
</div>
</div>
</template>
<style scoped>
:global(.skin-reorder-ghost) {
opacity: 0.35;
}
:global(.skin-reorder-drag) {
cursor: grabbing;
}
:global(.skin-reorder-fallback) {
opacity: 0.9;
pointer-events: none;
}
</style>
@@ -15,10 +15,12 @@ import { skinPreviewStorage } from '../storage/skin-preview-storage'
export interface RenderResult {
forwards: string
backwards: string
}
export interface RawRenderResult {
forwards: Blob
backwards: Blob
}
class BatchSkinRenderer {
@@ -90,9 +92,12 @@ class BatchSkinRenderer {
}
const frontCameraPos: [number, number, number] = [-1.3, 1, 6.3]
const forwards = await this.renderView(frontCameraPos, lookAtTarget)
const backCameraPos: [number, number, number] = [-1.3, 1, -2.5]
return { forwards }
const forwards = await this.renderView(frontCameraPos, lookAtTarget)
const backwards = await this.renderView(backCameraPos, lookAtTarget)
return { forwards, backwards }
}
private async renderView(
@@ -402,6 +407,7 @@ async function generateSkinPreviewsForGeneration(
if (rawCached && !skinBlobUrlMap.has(skinKey)) {
const cached: RenderResult = {
forwards: URL.createObjectURL(rawCached.forwards),
backwards: URL.createObjectURL(rawCached.backwards),
}
skinBlobUrlMap.set(skinKey, cached)
}
@@ -421,6 +427,7 @@ async function generateSkinPreviewsForGeneration(
if (DEBUG_MODE) {
const result = skinBlobUrlMap.get(key)!
URL.revokeObjectURL(result.forwards)
URL.revokeObjectURL(result.backwards)
skinBlobUrlMap.delete(key)
} else continue
}
@@ -449,6 +456,7 @@ async function generateSkinPreviewsForGeneration(
const renderResult: RenderResult = {
forwards: URL.createObjectURL(rawRenderResult.forwards),
backwards: URL.createObjectURL(rawRenderResult.backwards),
}
skinBlobUrlMap.set(key, renderResult)
-6
View File
@@ -142,12 +142,6 @@ export async function remove_custom_skin(skin: Skin): Promise<void> {
})
}
export async function set_custom_skin_order(textureKeys: string[]): Promise<void> {
await invoke('plugin:minecraft-skins|set_custom_skin_order', {
textureKeys,
})
}
export async function save_custom_skin(
skin: Skin,
textureBlob: Uint8Array,
@@ -2,6 +2,7 @@ import type { RawRenderResult } from '../rendering/batch-skin-renderer'
interface StoredPreview {
forwards: Blob
backwards: Blob
timestamp: number
}
@@ -37,6 +38,7 @@ export class SkinPreviewStorage {
const storedPreview: StoredPreview = {
forwards: result.forwards,
backwards: result.backwards,
timestamp: Date.now(),
}
@@ -65,7 +67,7 @@ export class SkinPreviewStorage {
return
}
resolve({ forwards: result.forwards })
resolve({ forwards: result.forwards, backwards: result.backwards })
}
request.onerror = () => reject(request.error)
})
@@ -93,7 +95,7 @@ export class SkinPreviewStorage {
const result = request.result as StoredPreview | undefined
if (result) {
results[key] = { forwards: result.forwards }
results[key] = { forwards: result.forwards, backwards: result.backwards }
} else {
results[key] = null
}
@@ -171,7 +173,7 @@ export class SkinPreviewStorage {
const key = cursor.primaryKey as string
const value = cursor.value as StoredPreview
const entrySize = value.forwards.size
const entrySize = value.forwards.size + value.backwards.size
totalSize += entrySize
count++
+9 -135
View File
@@ -2,9 +2,6 @@
"app.action-bar.downloading-java": {
"message": "جاز تنزيل إصدار جافا {version}"
},
"app.action-bar.downloading-update": {
"message": "تحميل التحديث"
},
"app.action-bar.downloads": {
"message": "التنزيلات"
},
@@ -23,18 +20,12 @@
"app.action-bar.primary-instance": {
"message": "النسخة ألرئيسية"
},
"app.action-bar.reload-to-update": {
"message": "أعد التشغيل للتحديث"
},
"app.action-bar.show-more-running-instances": {
"message": "عرض المزيد من النسخ قيد التشغيل"
},
"app.action-bar.stop-instance": {
"message": "إيقاف النسخة"
},
"app.action-bar.update": {
"message": "تحديث"
},
"app.action-bar.view-active-downloads": {
"message": "عرض التحميلات الفعالة"
},
@@ -44,36 +35,12 @@
"app.action-bar.view-logs": {
"message": "عرض التسجيل"
},
"app.appearance-settings.advanced-rendering.description": {
"message": "يتيح العرض المتقدم لتأثيرات مثل التشويش الذي قد تسبب مشكلات في الأداء دون استخدام العرض المسرع بالأجهزة."
},
"app.appearance-settings.advanced-rendering.title": {
"message": "العرض المتقدم"
},
"app.appearance-settings.color-theme.description": {
"message": "اختر لون سمتك المفضل من أجل تطبيق Modrinth App."
},
"app.appearance-settings.color-theme.title": {
"message": "لون السمة"
},
"app.appearance-settings.default-landing-page.description": {
"message": "غير الصفحة التي يفتح عليها المشغل."
},
"app.appearance-settings.default-landing-page.home": {
"message": "صفحة الرئيسية"
},
"app.appearance-settings.default-landing-page.library": {
"message": "مكتبة"
},
"app.appearance-settings.default-landing-page.title": {
"message": "الصفحة الرئيسة الافتراضية"
},
"app.appearance-settings.hide-nametag.description": {
"message": "ألغ تفعيل عرض الاسم فوق لاعبك في صفحة المظاهر."
},
"app.appearance-settings.hide-nametag.title": {
"message": "إخفاء عرض الاسم"
},
"app.auth-servers.unreachable.body": {
"message": "قد تكون خوادم مصادقة ماينكرافت معطلة حاليًا. تحقق من اتصالك بالإنترنت وحاول مرة أخرى لاحقًا."
},
@@ -92,27 +59,15 @@
"app.browse.already-added": {
"message": "مضاف فعلا"
},
"app.browse.back-to-instance": {
"message": "العودة للنموذج"
},
"app.browse.discover-content": {
"message": "استكشف محتوى"
},
"app.browse.discover-servers": {
"message": "استكشف خوادم"
},
"app.browse.hide-added-servers": {
"message": "إخفاء الخوادم المضافة مسبقًا"
},
"app.browse.server.installing": {
"message": "جاري التثبيت"
},
"app.creation-modal.installing-modpack.description": {
"message": "{fileName}"
},
"app.creation-modal.installing-modpack.title": {
"message": "تنزيل الحزم"
},
"app.export-modal.description-placeholder": {
"message": "أدخل وصف التعديل..."
},
@@ -128,9 +83,6 @@
"app.export-modal.modpack-name-placeholder": {
"message": "إسم حزمة التعديل"
},
"app.export-modal.select-files-label": {
"message": "أختيار الملفات التي سيتم تصديرها"
},
"app.export-modal.version-number-label": {
"message": "رقم الإصدار"
},
@@ -150,13 +102,13 @@
"message": "حذف النموذج"
},
"app.instance.modpack-already-installed.body": {
"message": "حُزْمَة التعديل مثبته مسبقًا فعلًا في نموذج <bold>{instanceName}</bold>. هل أنت متأكد بإرادة نسخه؟"
"message": "حُزْمَة التعديل هذه مثبته فعلًا في نموذج <bold>{instanceName}</bold>. هل انت متأكد بإرادة نسخه؟"
},
"app.instance.modpack-already-installed.create": {
"message": "إنشاء"
},
"app.instance.modpack-already-installed.header": {
"message": "حُزْمَة التعديل مثبتة فعلًا"
"message": "حُزْمَة التعديل مثبتة بالفعل"
},
"app.instance.modpack-already-installed.instance": {
"message": "النموذج"
@@ -174,7 +126,7 @@
"message": "تحقق من المشاريع التي أستخدمها في حزمة التعديل الخاص بي!"
},
"app.instance.mods.share-title": {
"message": "مشاركة محتوى حُزْمَة التعديل"
"message": "مشاركة محتوى حزمة التعديل"
},
"app.instance.mods.successfully-uploaded": {
"message": "تم الرفع بنجاح"
@@ -237,7 +189,7 @@
"message": "{count, plural, one {# تعديل} other {# تعديلات}}"
},
"app.modal.install-to-play.required-modpack": {
"message": ُزْمَة التعديل مطلوبة"
"message": زمة التعديل مطلوبة"
},
"app.modal.install-to-play.server-requires-mods": {
"message": "يتطلب هذا الخادم تعديلات للعب. انقر فوق \"تثبيت\" لإعداد الملفات المطلوبة من Modrinth، ثم قم بتشغيله مباشرة إلى الخادم."
@@ -258,13 +210,7 @@
"message": "يلزم التحديث"
},
"app.modal.update-to-play.update-required-description": {
"message": "هناك تحديث مطلوب للعب بـ {name}. الرجاء التحديث إلى أحدث اصدار لتشغيل اللعبة."
},
"app.project.install-context.back-to-browse": {
"message": "العودة إلى الاستكشاف"
},
"app.project.install-context.install-content-to-instance": {
"message": "تثبيت المحتويات الى النموذج"
"message": "هناك تحديث مطلوب للعب بـ {name}. الرجاء التحديث إلى آخر اصدار لتشغيل اللعبة."
},
"app.settings.developer-mode-enabled": {
"message": "تم تفعيل وضع المطوّر."
@@ -290,75 +236,6 @@
"app.settings.tabs.resource-management": {
"message": "إدارة الموارد"
},
"app.skins.add-button": {
"message": "إضافة مظهر"
},
"app.skins.add-button.drag-and-drop": {
"message": "أسحب ثم أفلت"
},
"app.skins.apply-button": {
"message": "تطبيق"
},
"app.skins.delete-button": {
"message": "حذف المظهر"
},
"app.skins.delete-modal.description": {
"message": "سيتم حذف المظهر بشكل نهائي، ولايمكن إرجاعه."
},
"app.skins.delete-modal.title": {
"message": "هل أنت متأكد من حذف المظهر هذا؟"
},
"app.skins.edit-button": {
"message": "تعديل المظهر"
},
"app.skins.modal.add-skin-button": {
"message": "إضافة المظهر"
},
"app.skins.modal.add-title": {
"message": "إضافة المظهر"
},
"app.skins.modal.cape-fallback-name": {
"message": "رداء"
},
"app.skins.modal.cape-section": {
"message": "رداء"
},
"app.skins.modal.edit-title": {
"message": "تعديل المظهر"
},
"app.skins.modal.make-edit-first-tooltip": {
"message": "عدل المظهر أولا للحفظ !"
},
"app.skins.modal.no-cape-tooltip": {
"message": "بدون رداء"
},
"app.skins.modal.none-cape-option": {
"message": "لا شيء"
},
"app.skins.modal.save-skin-button": {
"message": "حفظ المظهر"
},
"app.skins.modal.saving-tooltip": {
"message": "حفظ..."
},
"app.skins.modal.upload-skin-first-tooltip": {
"message": "أرفع المظهر أولا !"
},
"app.skins.preview.edit-button": {
"message": "تعديل المظهر"
},
"app.skins.previewing-badge": {
"message": "أستعراض"
},
"app.skins.rate-limit.title": {
"message": "تمهل !"
},
"app.skins.sign-in.title": {
"message": "الرجاء تسجيل الدخول"
},
"app.skins.title": {
"message": "اختيار المظهر"
},
"app.update-popup.body.download-complete": {
"message": "انتهى تنزيل تطبيق Modrinth v{version}. أعد التحميل للتحديث الآن، أو تلقائيًا عند إغلاق تطبيق Modrinth."
},
@@ -377,6 +254,9 @@
"app.update-popup.download-complete": {
"message": "اكتمل التنزيل"
},
"app.update-popup.reload": {
"message": "إعادة تحميل"
},
"app.update-popup.title": {
"message": "تحديث متاح"
},
@@ -387,7 +267,7 @@
"message": "تم تثبيت الإصدار {version} بنجاح!"
},
"app.world.server-modal.placeholder-address": {
"message": "مثال (example.modrinth.gg)"
"message": "مثال.مودرنث.جج"
},
"app.world.server-modal.select-an-option": {
"message": "حدد خيارا"
@@ -638,9 +518,6 @@
"instance.settings.tabs.java.java-memory": {
"message": "الذاكرة المخصَّصة"
},
"instance.settings.tabs.java.java-path-placeholder": {
"message": "/path/to/java"
},
"instance.settings.tabs.window": {
"message": "النافذة"
},
@@ -716,9 +593,6 @@
"minecraft-account.add-account": {
"message": "إضافة حساب"
},
"minecraft-account.label": {
"message": "حساب ماينكرافت"
},
"minecraft-account.select-account": {
"message": "اختيار الحساب"
},
+108 -294
View File
@@ -2,17 +2,14 @@
"app.action-bar.downloading-java": {
"message": "Stahování Javy {version}"
},
"app.action-bar.downloading-update": {
"message": "Stahování aktualizace"
},
"app.action-bar.downloads": {
"message": "Stahování"
"message": "Stažené Soubory"
},
"app.action-bar.hide-more-running-instances": {
"message": "Skrýt více spuštěných instancí"
},
"app.action-bar.make-primary-instance": {
"message": "Nastavit jako primární instanci"
"message": "Použít jako primární instanci"
},
"app.action-bar.no-instances-running": {
"message": "Žádné spuštěné instance"
@@ -23,17 +20,11 @@
"app.action-bar.primary-instance": {
"message": "Primární instance"
},
"app.action-bar.reload-to-update": {
"message": "Znovu načíst pro aktualizaci"
},
"app.action-bar.show-more-running-instances": {
"message": "Zobrazit další spuštěné instance"
},
"app.action-bar.stop-instance": {
"message": "Zastavit instanci"
},
"app.action-bar.update": {
"message": "Aktualizace"
"message": "Ukončit instanci"
},
"app.action-bar.view-active-downloads": {
"message": "Zobrazit probíhající stahování"
@@ -42,10 +33,10 @@
"message": "Zobrazit instanci"
},
"app.action-bar.view-logs": {
"message": "Zobrazit logy"
"message": "Zobrazit záznamy"
},
"app.appearance-settings.advanced-rendering.description": {
"message": "Povolí pokročilé vykreslování, například efekty rozmazání, které mohou bez hardwarového zrychlení způsobovat problémy s výkonem."
"message": "Umožňuje pokročilé vykreslování, jako jsou efekty rozostření, které mohou způsobit problémy s výkonem bez hardwarového vykreslení."
},
"app.appearance-settings.advanced-rendering.title": {
"message": "Pokročilé vykreslování"
@@ -57,16 +48,16 @@
"message": "Barevný motiv"
},
"app.appearance-settings.default-landing-page.description": {
"message": "Změňte stránku, která se otevře po spuštění launcheru."
"message": "Změňte stránku, na které se spouštěč otevře."
},
"app.appearance-settings.default-landing-page.home": {
"message": "Domů"
"message": "Domov"
},
"app.appearance-settings.default-landing-page.library": {
"message": "Knihovna"
},
"app.appearance-settings.default-landing-page.title": {
"message": "Výchozí stránka po spuštění"
"message": "Výchozí úvodní stránka"
},
"app.appearance-settings.hide-nametag.description": {
"message": "Zakáže zobrazení jmenovky nad vaší postavou na stránce skinů."
@@ -75,34 +66,31 @@
"message": "Skrýt jmenovku"
},
"app.appearance-settings.jump-back-into-worlds.description": {
"message": "Zahrne nedávné světy do sekce „Vrátit se do hry“ na domovské stránce."
"message": "V sekci „Vrátit se do hry“ na úvodní stránce jsou uvedeny nejnovější světy."
},
"app.appearance-settings.jump-back-into-worlds.title": {
"message": "Rychlý návrat do světů"
"message": "Skočit zpět do světů"
},
"app.appearance-settings.minimize-launcher.description": {
"message": "Minimalizovat launcher při spuštění Minecraftu."
"message": "Minimalizovat spouštěč při spuštění procesu Minecraftu."
},
"app.appearance-settings.minimize-launcher.title": {
"message": "Minimalizovat launcher"
"message": "Minimalizovat spouštěč"
},
"app.appearance-settings.native-decorations.description": {
"message": "Použít systémový rámeček okna (vyžaduje restart aplikace)."
},
"app.appearance-settings.native-decorations.title": {
"message": "Systémové dekorace"
"message": "Nativní dekorace"
},
"app.appearance-settings.select-option": {
"message": "Vyberte možnost"
},
"app.appearance-settings.show-play-time.description": {
"message": "Zobrazuje, kolik času jsi strávil hraním instance."
},
"app.appearance-settings.show-play-time.title": {
"message": "Zobrazit odehraný čas"
"message": "Ukázat herní čas"
},
"app.appearance-settings.toggle-sidebar.description": {
"message": "Umožní přepínat zobrazení postranního panelu."
"message": "Umožňí přepínat zobrazení postranního panelu."
},
"app.appearance-settings.toggle-sidebar.title": {
"message": "Přepnout postranní panel"
@@ -114,19 +102,19 @@
"message": "Upozorni mě před instalací neznámých modpacků"
},
"app.auth-servers.unreachable.body": {
"message": "Ověřovací Minecraft servery mohou být právě teď nedostupné. Zkontroluj si své připojení k internetu a zkus to znovu později."
"message": "Autořizační servery Minecraftu aktuálně nejsou k dispozici. Zkontrolujte si své připojení k internetu a zkuste to znovu později."
},
"app.auth-servers.unreachable.header": {
"message": "Nelze se připojit k ověřovacím serverům"
"message": "Připojení k autorizačním serverům se nezdařilo"
},
"app.browse.add-servers-to-instance": {
"message": "Přidání serveru do instance"
"message": "Přidat server do instance"
},
"app.browse.add-to-an-instance": {
"message": "Přidat do instance"
"message": "Přidat do instalace"
},
"app.browse.add-to-instance": {
"message": "Přidat do instance"
"message": "Přidat do instalace"
},
"app.browse.add-to-instance-name": {
"message": "Přidat do {instanceName}"
@@ -135,22 +123,22 @@
"message": "Přidáno"
},
"app.browse.already-added": {
"message": "Již přidáno"
"message": "Už přidáno"
},
"app.browse.back-to-instance": {
"message": "Zpět k instanci"
},
"app.browse.discover-content": {
"message": "Objevovat obsah"
"message": "Prozkoumat obsah"
},
"app.browse.discover-servers": {
"message": "Objevovat servery"
"message": "Prozkoumat servery"
},
"app.browse.hide-added-servers": {
"message": "Skrýt již přidané servery"
},
"app.browse.project-type.modpacks": {
"message": "Modpacky"
"message": "Balíčky Modifikací"
},
"app.browse.server-instance-content-warning": {
"message": "Přidání obsahu může narušit kompatibilitu při připojení k serveru. Veškerý přidaný obsah se navíc ztratí při aktualizaci obsahu instance serveru."
@@ -158,9 +146,6 @@
"app.browse.server.installing": {
"message": "Instalování"
},
"app.content-install.no-compatible-versions": {
"message": "Žádná dostupná verze se neshoduje s verzí {compatibilityLabel}. Vyber verzi, abys ji i přesto nainstaloval/a. Závislosti nebudou automaticky nainstalovány."
},
"app.creation-modal.installing-modpack.description": {
"message": "{fileName}"
},
@@ -168,7 +153,7 @@
"message": "Probíhá instalace modpacku..."
},
"app.export-modal.description-placeholder": {
"message": "Zadej popis modpacku..."
"message": "Přidej popis modpacku..."
},
"app.export-modal.export-button": {
"message": "Exportovat"
@@ -180,10 +165,10 @@
"message": "Zahrnout \"{file}\"?"
},
"app.export-modal.modpack-name-label": {
"message": "Název modpacku"
"message": "Jméno Modpacku"
},
"app.export-modal.modpack-name-placeholder": {
"message": "Název modpacku"
"message": "Jméno modpacku"
},
"app.export-modal.select-files-label": {
"message": "Nastavte, které soubory mají být do tohoto exportu zahrnuty"
@@ -195,7 +180,7 @@
"message": "1.0.0"
},
"app.instance.confirm-delete.admonition-body": {
"message": "Všechna data tvé instance budou trvale smazána, včetně světů, konfigurací a veškerého nainstalovaného obsahu."
"message": "Všechna data z instance budou navždy smazána, včetně světů, nastavení a všeho ostatního."
},
"app.instance.confirm-delete.admonition-header": {
"message": "Tato akce nemůže být vrácena"
@@ -204,19 +189,19 @@
"message": "Odstranit instanci"
},
"app.instance.confirm-delete.header": {
"message": "Odstranění instance"
"message": "Odstanit instanci"
},
"app.instance.modpack-already-installed.body": {
"message": "Tento modpack je již nainstalovaný v instanci <bold>{instanceName}</bold>. Opravdu ho chceš duplikovat?"
"message": "Tento modpack je už nainstalovaný v instanci <bold>{instanceName}</bold>. Opravdu ho chceš duplikovat?"
},
"app.instance.modpack-already-installed.create": {
"message": "Vytvořit"
},
"app.instance.modpack-already-installed.header": {
"message": "Modpack je již nainstalován"
"message": "Modpack už je nainstalovaný"
},
"app.instance.modpack-already-installed.instance": {
"message": "Instance"
"message": "Instalace"
},
"app.instance.mods.content-type-project": {
"message": "projekt"
@@ -228,7 +213,7 @@
"message": "{count} projektů bylo přidáno"
},
"app.instance.mods.share-text": {
"message": "Podívejte se na projekty, které používám ve svém modpacku!"
"message": "Podívejte se na projekty co používám ve svém modpacku!"
},
"app.instance.mods.share-title": {
"message": "Sdílení obsahu modpacku"
@@ -242,14 +227,11 @@
"app.instance.worlds.browse-servers": {
"message": "Procházet servery"
},
"app.instance.worlds.delete-world-description": {
"message": "'{name}' bude **navždy smazán** a nebude možné jej obnovit."
},
"app.instance.worlds.delete-world-title": {
"message": "Jsi si jistý, že chceš trvale smazat tento svět?"
"message": "Opravdu si jsi jistý, že chceš navždy smazat tenhle svět?"
},
"app.instance.worlds.filter-modded": {
"message": "Módovaný"
"message": "Módováno"
},
"app.instance.worlds.filter-offline": {
"message": "Offline"
@@ -261,22 +243,19 @@
"message": "Vanilla"
},
"app.instance.worlds.no-worlds-description": {
"message": "Přidej server nebo začni procházet"
"message": "Přidej nebo procházej servery"
},
"app.instance.worlds.no-worlds-heading": {
"message": "Žádné servery nebo světy nebyly přidány"
},
"app.instance.worlds.remove-server-description": {
"message": "'{name}' bude odstraněn z tvého seznamu i ze hry a nebude možné jej obnovit."
},
"app.instance.worlds.remove-server-description-with-address": {
"message": "'{name}' ({address}) bude odstraněn z tvého seznamu i ze hry a nebude možné jej obnovit."
"message": "'{name}' ({address}) bude odstraněn z vašeho seznamu, a to i ve hře, a nebude možné jej obnovit."
},
"app.instance.worlds.remove-server-title": {
"message": "Opravdu chceš odstranit {name}?"
},
"app.instance.worlds.search-worlds-placeholder": {
"message": "Hledat v {count} světech..."
"message": "Hledat ve světech {count}..."
},
"app.instance.worlds.this-server": {
"message": "tento server"
@@ -285,19 +264,19 @@
"message": "Požadovaný obsah"
},
"app.modal.install-to-play.header": {
"message": "Nainstaluj pro hraní"
"message": "Nainstaluj ke hraní"
},
"app.modal.install-to-play.install-button": {
"message": "Instalovat"
},
"app.modal.install-to-play.mod-count": {
"message": "{count, plural, one {# mód} few {# módy} other {# módů}}"
"message": "{count, plural,one {#mód} other {#módy}}"
},
"app.modal.install-to-play.required-modpack": {
"message": "Požadovaný modpack"
"message": "Povinný modpack"
},
"app.modal.install-to-play.server-requires-mods": {
"message": "Tento server k hraní vyžaduje módy. Klikni na instalovat pro získání potřebných módů z Modrinth a poté se rovnou připoj na server."
"message": "Tento server k hraní vyžaduje módy. Klikni na instalovat pro získání potřebných módů z Modrinth."
},
"app.modal.install-to-play.shared-instance": {
"message": "Sdílená instance"
@@ -309,22 +288,22 @@
"message": "Zobrazit obsah"
},
"app.modal.update-to-play.header": {
"message": "Aktualizuj pro hraní"
"message": "Updatuj ke hraní"
},
"app.modal.update-to-play.update-required": {
"message": "Je vyžadována aktualizace"
},
"app.modal.update-to-play.update-required-description": {
"message": "Pro hraní {name} je vyžadována aktualizace. Prosím aktualizuj na nejnovější verzi, abys mohl hru spustit."
"message": "Je nutná aktualizace ke hraní {name}. Prosím updatuj na nejnovější verzi k spuštění hry."
},
"app.project.install-button.already-installed": {
"message": "Tento projekt je již nainstalován"
},
"app.project.install-context.back-to-browse": {
"message": "Zpět k Objevování"
"message": "Zpět na Prozkoumat"
},
"app.project.install-context.install-content-to-instance": {
"message": "Nainstalovat obsah do instance"
"message": "Nainstalovat obsah do instnce"
},
"app.settings.developer-mode-enabled": {
"message": "Vývojářský režim povolen."
@@ -353,105 +332,21 @@
"app.skins.add-button": {
"message": "Přidat skin"
},
"app.skins.add-button.drag-and-drop": {
"message": "Přetáhni a pusť"
},
"app.skins.apply-button": {
"message": "Použít"
},
"app.skins.delete-button": {
"message": "Smazat skin"
},
"app.skins.delete-modal.description": {
"message": "Tahle akce navždy smaže vybraný skin a nejde vrátit zpět."
},
"app.skins.delete-modal.title": {
"message": "Opravdu chceš smazat tenhle skin?"
},
"app.skins.dropped-file-error.text": {
"message": "Nepodařilo se načíst přetažený soubor."
},
"app.skins.dropped-file-error.title": {
"message": "Chyba při načítání souboru"
},
"app.skins.edit-button": {
"message": "Upravit skin"
},
"app.skins.modal.add-skin-button": {
"message": "Přidat skin"
},
"app.skins.modal.add-title": {
"message": "Přidání skinu"
},
"app.skins.modal.arm-style-section": {
"message": "Typ paží"
},
"app.skins.modal.arm-style-slim": {
"message": "Štíhlý"
},
"app.skins.modal.arm-style-wide": {
"message": "Široký"
},
"app.skins.modal.cape-fallback-name": {
"message": "Plášť"
},
"app.skins.modal.cape-section": {
"message": "Plášť"
},
"app.skins.modal.edit-title": {
"message": "Úprava skinu"
},
"app.skins.modal.make-edit-first-tooltip": {
"message": "Nejdříve uprav skin!"
},
"app.skins.modal.no-cape-tooltip": {
"message": "Bez pláště"
},
"app.skins.modal.none-cape-option": {
"message": "Žádný"
},
"app.skins.modal.replace-texture-button": {
"message": "Nahradit texturu"
},
"app.skins.modal.save-skin-button": {
"message": "Uložit skin"
},
"app.skins.modal.saving-tooltip": {
"message": "Ukládání..."
},
"app.skins.modal.texture-section": {
"message": "Textura"
},
"app.skins.modal.upload-skin-first-tooltip": {
"message": "Nejdříve nahraj skin!"
},
"app.skins.preview.edit-button": {
"message": "Upravit skin"
},
"app.skins.previewing-badge": {
"message": "Náhled"
},
"app.skins.rate-limit.text": {
"message": "Měníš svůj skin příliš často. Servery Mojangu dočasně zablokovaly další požadavky. Počkej chvíli, než to zkusíš znovu."
},
"app.skins.rate-limit.title": {
"message": "Zpomal!"
},
"app.skins.reorder-error.text": {
"message": "Tvé pořadí skinů se nepodařilo uložit."
},
"app.skins.reorder-error.title": {
"message": "Nepodařilo se změnit pořadí skinů"
},
"app.skins.section.builders-and-biomes": {
"message": "Builders & Biomes"
},
"app.skins.section.chase-the-skies": {
"message": "Chase the Skies"
},
"app.skins.section.default-skins": {
"message": "Výchozí skiny"
},
"app.skins.section.minecon-earth-2017": {
"message": "MINECON Earth 2017"
},
@@ -461,47 +356,14 @@
"app.skins.section.modrinth-pride": {
"message": "Modrinth Pride"
},
"app.skins.section.modrinth-pride.tooltip": {
"message": "Tyto skiny jsi obdržel za přispění do sbírky Modrinth Pride behěm Pride měsíce."
},
"app.skins.section.mounts-of-mayhem": {
"message": "Mounts of Mayhem"
},
"app.skins.section.saved-skins": {
"message": "Uložené skiny"
},
"app.skins.section.striding-hero": {
"message": "Striding Hero"
},
"app.skins.section.the-copper-age": {
"message": "The Copper Age"
},
"app.skins.section.the-garden-awakens": {
"message": "The Garden Awakens"
},
"app.skins.section.tiny-takeover": {
"message": "Tiny Takeover"
},
"app.skins.sign-in.button": {
"message": "Přihlásit se"
},
"app.skins.sign-in.description": {
"message": "Přihlas se prosím ke svému Minecraft účtu, abys mohl/a využívat funkce pro správu skinů v aplikaci Modrinth."
},
"app.skins.sign-in.rinthbot-alt": {
"message": "Nadšený Modrinth Bot"
},
"app.skins.sign-in.title": {
"message": "Přihlas se prosím"
},
"app.skins.title": {
"message": "Výběr skinu"
},
"app.update-popup.body.download-complete": {
"message": "Stahování aplikace Modrinth v{version} bylo dokončeno. Aktualizujte ji znovunačtem nebo se automaticky aktualizuje po zavření."
"message": "Stahování aplikace Modrinth v{version} bylo dokončeno. Nainstalujte aktualizaci nyní nebo automaticky po zavření aplikace Modrinth."
},
"app.update-popup.body.linux": {
"message": "Aplikace Modrinth v{version} je k dispozici. Aktualizujte ji pomocí svého správce balíčků, abyste získali nejnovější funkce a opravy!"
"message": "Aplikace Modrinth v{version} je k dispozici. Aktualizujte pomocí svého správce balíčků, abyste získali nejnovější funkce a opravy!"
},
"app.update-popup.body.metered": {
"message": "Aplikace Modrinth v{version} je nyní k dispozici! Protože jste v měřené síti, nebyla stažena automaticky."
@@ -516,10 +378,10 @@
"message": "Stahování bylo dokončeno"
},
"app.update-popup.reload": {
"message": "Znovu načíst pro aktualizaci"
"message": "Načíst znovu"
},
"app.update-popup.title": {
"message": "Aktualizace k dispozici"
"message": "Je k dispozici aktualizace"
},
"app.update.complete-toast.text": {
"message": "Kliknutím sem zobrazíte seznam změn."
@@ -531,13 +393,13 @@
"message": "priklad.modrinth.gg"
},
"app.world.server-modal.select-an-option": {
"message": "Vybrat možnost"
"message": "Vyberte možnost"
},
"app.world.world-item.incompatible-version": {
"message": "Nekompatibilní verze {version}"
},
"app.world.world-item.not-played-yet": {
"message": "Zatím nehráno"
"message": "Ještě nehráno"
},
"app.world.world-item.offline": {
"message": "Offline"
@@ -549,25 +411,25 @@
"message": "Přidat přítele"
},
"friends.action.view-friend-requests": {
"message": "{count} {count, plural, one {žádost} few {žádosti} other {žádostí}} o přátelství"
"message": "{count} přátelé {count, plural, one {request} other {requests}}"
},
"friends.add-friend.submit": {
"message": "Poslat žádost o přátelství"
},
"friends.add-friend.title": {
"message": "Přidání přítele"
"message": "Přidákamaráda"
},
"friends.add-friend.username.description": {
"message": "Může se lišit od jejich Minecraft přezdívky!"
"message": "Může být jiné než jejich Minecraft jméno!"
},
"friends.add-friend.username.placeholder": {
"message": "Zadej přezdívku na Modrinthu..."
"message": "Zadejte uživatelské jméno Modrinth..."
},
"friends.add-friend.username.title": {
"message": "Jaká je přezdívka tvého přítele na Modrinthu?"
"message": "Jak se váš kamarád jmenuje na Modrinthu?"
},
"friends.add-friends-to-share": {
"message": "<link>Přidej si přátele</link>, abys viděl, co hrají!"
"message": "<link>Přidejte si přátelé</link> a podívejte se, co hrají!"
},
"friends.friend.cancel-request": {
"message": "Zrušit žádost"
@@ -582,7 +444,7 @@
"message": "Zobrazit profil"
},
"friends.heading": {
"message": "Přátelé"
"message": "Kamarádi"
},
"friends.heading.active": {
"message": "Aktivní"
@@ -596,9 +458,6 @@
"friends.heading.pending": {
"message": "Čeká na vyřízení"
},
"friends.no-friends-match": {
"message": "Žádní přátelé neodpovídají ''{query}''"
},
"friends.search-friends-placeholder": {
"message": "Hledat přátele..."
},
@@ -606,7 +465,7 @@
"message": "{title} - {count}"
},
"friends.sign-in-to-add-friends": {
"message": "<link>Přihlas se k Modrinth účtu</link>, abys mohl přidávat přátele a vidět, co hrají!"
"message": "<link>Přihlašte se ke svému Modrinth účtu</link>, abyste jsi přidali přátele a uviděli co hrají!"
},
"instance.add-server.add-and-play": {
"message": "Přidat a hrát"
@@ -618,10 +477,10 @@
"message": "Vypnuto"
},
"instance.add-server.resource-pack.enabled": {
"message": "Povolený"
"message": "Zapnuto"
},
"instance.add-server.resource-pack.prompt": {
"message": "Zeptat se"
"message": "Ptát se"
},
"instance.add-server.title": {
"message": "Přidat server"
@@ -630,7 +489,7 @@
"message": "Upravit server"
},
"instance.edit-world.hide-from-home": {
"message": "Skrýt z domovské stránky"
"message": "Skrýt z domovské obrazovky"
},
"instance.edit-world.name": {
"message": "Název"
@@ -639,7 +498,7 @@
"message": "Minecraft svět"
},
"instance.edit-world.reset-icon": {
"message": "Obnovit ikonu"
"message": "Resetovat ikonu"
},
"instance.edit-world.title": {
"message": "Upravit svět"
@@ -657,22 +516,22 @@
"message": "Název"
},
"instance.server-modal.placeholder-name": {
"message": "Minecraft server"
"message": "Minecraft Server"
},
"instance.server-modal.resource-pack": {
"message": "Balíček textur"
"message": "Balíčky textur"
},
"instance.settings.tabs.general": {
"message": "Obecné"
},
"instance.settings.tabs.general.delete": {
"message": "Odstranění instance"
"message": "Odstranit instalaci"
},
"instance.settings.tabs.general.delete.button": {
"message": "Odstranit instanci"
"message": "Odstranit instalaci"
},
"instance.settings.tabs.general.delete.description": {
"message": "Trvale smaže instanci z tvého zařízení, včetně světů, konfigurací a veškerého nainstalovaného obsahu. Buď opatrný, jakmile instanci smažeš, nepůjde ji obnovit."
"message": "Trvale smaže instalaci z vašeho zařízení, včetně vašich světů, konfigurací a veškerého nainstalovaného obsahu. Buďte opatrní, protože jakmile instalaci smažete, nebude možné ji obnovit."
},
"instance.settings.tabs.general.deleting.button": {
"message": "Mazání..."
@@ -681,64 +540,40 @@
"message": "Duplikovat"
},
"instance.settings.tabs.general.duplicate-button.tooltip.installing": {
"message": "Nelze duplikovat během instalace."
"message": "Nelze duplikovat při instalaci."
},
"instance.settings.tabs.general.duplicate-instance": {
"message": "Duplikace instance"
"message": "Duplikovat instalaci"
},
"instance.settings.tabs.general.duplicate-instance.description": {
"message": "Vytvoří kopii této instance, včetně světů, konfigurací, modů, atd."
"message": "Vytvoří kopii této instalace, včetně světů, konfigurací, modů atd."
},
"instance.settings.tabs.general.edit-icon": {
"message": "Upravit ikonu"
},
"instance.settings.tabs.general.edit-icon.remove": {
"message": "Odebrat ikonu"
"message": "Odstranit ikonu"
},
"instance.settings.tabs.general.edit-icon.replace": {
"message": "Nahradit ikonu"
"message": "Změnit ikonu"
},
"instance.settings.tabs.general.edit-icon.select": {
"message": "Vybrat ikonu"
},
"instance.settings.tabs.general.library-groups": {
"message": "Skupiny knihovny"
"message": "Skupina knihoven"
},
"instance.settings.tabs.general.library-groups.create": {
"message": "Vytvořit novou skupinu"
},
"instance.settings.tabs.general.library-groups.description": {
"message": "Skupiny knihovny ti umožňují uspořádat instance do různých sekcí v knihovně."
"message": "Skupiny knihoven vám umožňují uspořádat instalace do různých sekcí."
},
"instance.settings.tabs.general.library-groups.enter-name": {
"message": "Zadej název skupiny"
"message": "Zadejte název skupiny"
},
"instance.settings.tabs.general.name": {
"message": "Jméno"
},
"instance.settings.tabs.general.update-channel": {
"message": "Aktualizační kanál"
},
"instance.settings.tabs.general.update-channel.alpha": {
"message": "Alfa"
},
"instance.settings.tabs.general.update-channel.alpha.description": {
"message": "Vydané, beta a alfa verze se zobrazí jako dostupné aktualizace."
},
"instance.settings.tabs.general.update-channel.beta": {
"message": "Beta"
},
"instance.settings.tabs.general.update-channel.beta.description": {
"message": "Vydané a beta verze se zobrazí jako dostupné aktualizace."
},
"instance.settings.tabs.general.update-channel.release": {
"message": "Vydání"
},
"instance.settings.tabs.general.update-channel.release.description": {
"message": "Pouze vydané verze se zobrazí jako dostupné aktualizace."
},
"instance.settings.tabs.general.update-channel.select": {
"message": "Vyber aktualizační kanál"
"message": "Název"
},
"instance.settings.tabs.hooks": {
"message": "Spouštěcí hooky"
@@ -747,28 +582,28 @@
"message": "Vlastní spouštěcí hooky"
},
"instance.settings.tabs.hooks.description": {
"message": "Hooky umožňují pokročilým uživatelům spustit určité systémové příkazy před a po spuštění hry."
"message": "Hooky umožňují pokročilým uživatelům spouštět určité systémové příkazy před a po spuštění hry."
},
"instance.settings.tabs.hooks.post-exit": {
"message": "Po ukončení"
},
"instance.settings.tabs.hooks.post-exit.description": {
"message": "Provede se po ukončení hry."
"message": "Spušteno po vypnutí hry."
},
"instance.settings.tabs.hooks.post-exit.enter": {
"message": "Zadej příkaz k provedení po ukončení..."
"message": "Zadejte příkaz k provedení po ukončení..."
},
"instance.settings.tabs.hooks.pre-launch": {
"message": "Před spuštěním"
},
"instance.settings.tabs.hooks.pre-launch.description": {
"message": "Provede se před spuštěním instance."
"message": "Spouštějí se před spuštěním instalace."
},
"instance.settings.tabs.hooks.pre-launch.enter": {
"message": "Zadej příkaz k provedení před spuštěním..."
"message": "Zadejte příkaz k provedení před spuštěním..."
},
"instance.settings.tabs.hooks.title": {
"message": "Spouštěcí příkazy hry"
"message": "Spouštěcí hooky hry"
},
"instance.settings.tabs.hooks.wrapper": {
"message": "Wrapper"
@@ -777,7 +612,7 @@
"message": "Příkaz wrapperu pro spuštění Minecraftu."
},
"instance.settings.tabs.hooks.wrapper.enter": {
"message": "Zadej příkaz wrapperu..."
"message": "Zadejte příkaz wrapperu..."
},
"instance.settings.tabs.installation": {
"message": "Instalace"
@@ -788,24 +623,6 @@
"instance.settings.tabs.java": {
"message": "Java a pamět"
},
"instance.settings.tabs.java.custom-environment-variables": {
"message": "Vlastní proměnná prostředí"
},
"instance.settings.tabs.java.custom-java-arguments": {
"message": "Vlastní argumenty Javy"
},
"instance.settings.tabs.java.custom-java-installation": {
"message": "Vlastní instalace Javy"
},
"instance.settings.tabs.java.custom-memory-allocation": {
"message": "Vlastní nastavení paměti"
},
"instance.settings.tabs.java.enter-environment-variables": {
"message": "Zadej proměnná prostředí..."
},
"instance.settings.tabs.java.enter-java-arguments": {
"message": "Zadej argumenty Javy..."
},
"instance.settings.tabs.java.environment-variables": {
"message": "Proměnné prostředí"
},
@@ -813,16 +630,13 @@
"message": "Hooky"
},
"instance.settings.tabs.java.java-arguments": {
"message": "Argumenty Javy"
"message": "Java argumenty"
},
"instance.settings.tabs.java.java-installation": {
"message": "Instalace Javy"
},
"instance.settings.tabs.java.java-memory": {
"message": "Přiřazeno paměti"
},
"instance.settings.tabs.java.java-path-placeholder": {
"message": "/path/to/java"
"message": "Paměti přiřazeno"
},
"instance.settings.tabs.window": {
"message": "Okno"
@@ -831,10 +645,10 @@
"message": "Vlastní nastavení okna"
},
"instance.settings.tabs.window.fullscreen": {
"message": "Režim celé obrazovky"
"message": "Celá obrazovka"
},
"instance.settings.tabs.window.fullscreen.description": {
"message": "Při spuštění otevřít hru v režimu celé obrazovky (použitím options.txt)."
"message": "Spustit hru v plné obrazovce pokud spuštěno (použitím options.txt)."
},
"instance.settings.tabs.window.height": {
"message": "Výška"
@@ -843,7 +657,7 @@
"message": "Výška herního okna při spuštění."
},
"instance.settings.tabs.window.height.enter": {
"message": "Zadej výšku..."
"message": "Zadejte výšku..."
},
"instance.settings.tabs.window.width": {
"message": "Šířka"
@@ -852,19 +666,19 @@
"message": "Šířka herního okna při spuštění."
},
"instance.settings.tabs.window.width.enter": {
"message": "Zadej šířku..."
"message": "Zadejte šířku..."
},
"instance.worlds.a_minecraft_server": {
"message": "Minecraft server"
"message": "Minecraft Server"
},
"instance.worlds.cant_connect": {
"message": "Nelze se připojit k serveru"
"message": "Nelze připojit k serveru"
},
"instance.worlds.copy_address": {
"message": "Kopírovat adresu"
},
"instance.worlds.dont_show_on_home": {
"message": "Nezobrazovat na domovské stránce"
"message": "Nezobrazuje na domovské stránce"
},
"instance.worlds.game_already_open": {
"message": "Instance je již otevřená"
@@ -879,13 +693,13 @@
"message": "Spravováno projektem serveru"
},
"instance.worlds.no_contact": {
"message": "Se serverem se nepodařilo spojit"
"message": "Server se nepodařilo spojit"
},
"instance.worlds.no_server_quick_play": {
"message": "Připojit se rovnou k serverům můžeš pouze s Minecraft verzí Alpha 1.0.5+"
"message": "Můžeš rovnou skočit na server pouze v Minecraftu Alpha 1.0.5+"
},
"instance.worlds.no_singleplayer_quick_play": {
"message": "Můžeš se rovnou připojit do světů pro jednoho hráče pouze s Minecraft verzí 1.20+"
"message": "V Minecraftu 1.20+ se dá rovnou přeskočit pouze do singleplayerových světů"
},
"instance.worlds.play_instance": {
"message": "Hrát instanci"
@@ -894,22 +708,22 @@
"message": "Zobrazit instanci"
},
"instance.worlds.world_in_use": {
"message": "Svět je používán"
"message": "Svět je použit"
},
"minecraft-account.add-account": {
"message": "Přidat účet"
},
"minecraft-account.label": {
"message": "Minecraft účet"
"message": "Účet Minecraft"
},
"minecraft-account.not-signed-in": {
"message": "Nejste přihlášeni"
},
"minecraft-account.remove-account": {
"message": "Odebrat účet"
"message": "Odstranit účet"
},
"minecraft-account.select-account": {
"message": "Zvolte účet"
"message": "Vyberte účet"
},
"minecraft-account.sign-in": {
"message": "Přihlásit se do Minecraftu"
@@ -918,31 +732,31 @@
"message": "Poskytováno instancí"
},
"search.filter.locked.instance-game-version.title": {
"message": "Verze hry je poskytována instancí"
"message": "Verze hry je poskytnut instanci"
},
"search.filter.locked.instance-loader.title": {
"message": "Načítací modul je poskytován instancí"
"message": "Spouštěč je poskytnut instancí"
},
"search.filter.locked.instance.sync": {
"message": "Synchronizováno s instancí"
"message": "Synchronizováno z instancí"
},
"search.filter.locked.server": {
"message": "Poskytováno serverem"
},
"search.filter.locked.server-environment.title": {
"message": "Pouze módy ze strany klientu mohou být přidány na server"
"message": "Jen klientské mody mohou být přidány na server"
},
"search.filter.locked.server-game-version.title": {
"message": "Verze hry je poskytována serverem"
"message": "Verzi hry poskytuje server"
},
"search.filter.locked.server-loader.title": {
"message": "Loader zprostředkovává server"
},
"unknown-pack-warning-modal.body": {
"message": "Soubor je zkontrolován pouze v případě, že je nahrán na Modrinth, bez ohledu na jeho formát (včetně formátu .mrpack)."
"message": "Soubor je zkontrolován pouze v případě, že je nahrán na Modrinth, a to bez ohledu na jeho formát (včetně formátu .mrpack)."
},
"unknown-pack-warning-modal.dont-show-again": {
"message": "Toto varování znovu nezobrazovat"
"message": "Toto varování již nezobrazovat"
},
"unknown-pack-warning-modal.header": {
"message": "Potvrdit instalaci"
+3 -12
View File
@@ -2,9 +2,6 @@
"app.action-bar.downloading-java": {
"message": "Downloader Java {version}"
},
"app.action-bar.downloading-update": {
"message": "Downloader opdatering"
},
"app.action-bar.downloads": {
"message": "Downloads"
},
@@ -23,18 +20,12 @@
"app.action-bar.primary-instance": {
"message": "Primære instance"
},
"app.action-bar.reload-to-update": {
"message": "Genindlæs for at opdater"
},
"app.action-bar.show-more-running-instances": {
"message": "Vis flere kørende instances"
},
"app.action-bar.stop-instance": {
"message": "Stop instance"
},
"app.action-bar.update": {
"message": "Opdater"
},
"app.action-bar.view-active-downloads": {
"message": "Vis aktive downloads"
},
@@ -152,9 +143,6 @@
"app.browse.server.installing": {
"message": "Installere"
},
"app.content-install.no-compatible-versions": {
"message": "Ingen tilgængelige versioner passer til {compatibilityLabel}. Vælg en version for at installere. Afhængigheder bliver ikke installeret automatisk."
},
"app.creation-modal.installing-modpack.description": {
"message": "{fileName}"
},
@@ -485,6 +473,9 @@
"app.update-popup.download-complete": {
"message": "Download færdiggjort"
},
"app.update-popup.reload": {
"message": "Geninlæs"
},
"app.update-popup.title": {
"message": "Opdatering tilgængelig"
},
+1 -40
View File
@@ -2,9 +2,6 @@
"app.action-bar.downloading-java": {
"message": "Java {version} wird heruntergeladen"
},
"app.action-bar.downloading-update": {
"message": "Update wird heruntergeladen"
},
"app.action-bar.downloads": {
"message": "Downloads"
},
@@ -23,18 +20,12 @@
"app.action-bar.primary-instance": {
"message": "Primäre Instanz"
},
"app.action-bar.reload-to-update": {
"message": "Zum Aktualisieren neu laden"
},
"app.action-bar.show-more-running-instances": {
"message": "Weitere laufende Instanzen anzeigen"
},
"app.action-bar.stop-instance": {
"message": "Instanz stoppen"
},
"app.action-bar.update": {
"message": "Aktualisieren"
},
"app.action-bar.view-active-downloads": {
"message": "Aktive Downloads anzeigen"
},
@@ -428,12 +419,6 @@
"app.skins.rate-limit.title": {
"message": "Nicht so schnell!"
},
"app.skins.reorder-error.text": {
"message": "Deine Skin-Reihenfolge konnte nicht gespeichert werden."
},
"app.skins.reorder-error.title": {
"message": "Skins konnten nicht neu angeordnet werden"
},
"app.skins.section.builders-and-biomes": {
"message": "Builders & Biomes"
},
@@ -507,7 +492,7 @@
"message": "Download abgeschlossen"
},
"app.update-popup.reload": {
"message": "Zum Aktualisieren neu laden"
"message": "Neu Laden"
},
"app.update-popup.title": {
"message": "Aktualisierung verfügbar"
@@ -707,30 +692,6 @@
"instance.settings.tabs.general.name": {
"message": "Name"
},
"instance.settings.tabs.general.update-channel": {
"message": "Update-Kanal"
},
"instance.settings.tabs.general.update-channel.alpha": {
"message": "Alpha"
},
"instance.settings.tabs.general.update-channel.alpha.description": {
"message": "Release-, Beta- und Alpha-Versionen werden als verfügbare Updates angezeigt."
},
"instance.settings.tabs.general.update-channel.beta": {
"message": "Beta"
},
"instance.settings.tabs.general.update-channel.beta.description": {
"message": "Release- und Beta-Versionen werden als verfügbare Updates angezeigt."
},
"instance.settings.tabs.general.update-channel.release": {
"message": "Release"
},
"instance.settings.tabs.general.update-channel.release.description": {
"message": "Nur Release-Versionen werden als verfügbare Updates angezeigt."
},
"instance.settings.tabs.general.update-channel.select": {
"message": "Update-Kanal auswählen"
},
"instance.settings.tabs.hooks": {
"message": "Start Hooks"
},
+4 -43
View File
@@ -2,9 +2,6 @@
"app.action-bar.downloading-java": {
"message": "Java {version} wird heruntergeladen"
},
"app.action-bar.downloading-update": {
"message": "Update wird heruntergeladen"
},
"app.action-bar.downloads": {
"message": "Downloads"
},
@@ -15,7 +12,7 @@
"message": "Zur primären Instanz machen"
},
"app.action-bar.no-instances-running": {
"message": "Keine laufenden Instanzen"
"message": "Keine aktiven Instanzen"
},
"app.action-bar.offline": {
"message": "Offline"
@@ -23,18 +20,12 @@
"app.action-bar.primary-instance": {
"message": "Primäre Instanz"
},
"app.action-bar.reload-to-update": {
"message": "Zum Aktualisieren neu laden"
},
"app.action-bar.show-more-running-instances": {
"message": "Weitere laufende Instanzen anzeigen"
},
"app.action-bar.stop-instance": {
"message": "Instanz stoppen"
},
"app.action-bar.update": {
"message": "Aktualisieren"
},
"app.action-bar.view-active-downloads": {
"message": "Aktive Downloads anzeigen"
},
@@ -42,7 +33,7 @@
"message": "Instanz anzeigen"
},
"app.action-bar.view-logs": {
"message": "Logs anzeigen"
"message": "Protokolle anzeigen"
},
"app.appearance-settings.advanced-rendering.description": {
"message": "Aktiviert erweiterte Rendering-Funktionen wie Unschärfe-Effekte, welche ohne Hardware-beschleunigtes Rendering zu Leistungsproblemen führen können."
@@ -423,17 +414,11 @@
"message": "Vorschau"
},
"app.skins.rate-limit.text": {
"message": "Du wechselst deinen Skin zu oft. Die Server von Mojang haben weitere Anfragen vorübergehend blockiert. Bitte warte einen Moment, bevor du es erneut versuchst."
"message": "Du wechselst dein Skin zu oft. Die Server von Mojang haben weitere Anfragen vorübergehend blockiert. Bitte warte einen Moment, bevor du es erneut versuchst."
},
"app.skins.rate-limit.title": {
"message": "Langsamer!"
},
"app.skins.reorder-error.text": {
"message": "Die Anordnung deiner Skins konnte nicht gespeichert werden."
},
"app.skins.reorder-error.title": {
"message": "Skins konnten nicht neu angeordnet werden"
},
"app.skins.section.builders-and-biomes": {
"message": "Builders & Biomes"
},
@@ -507,7 +492,7 @@
"message": "Download abgeschlossen"
},
"app.update-popup.reload": {
"message": "Zum Aktualisieren neu laden"
"message": "Neu laden"
},
"app.update-popup.title": {
"message": "Aktualisierung verfügbar"
@@ -707,30 +692,6 @@
"instance.settings.tabs.general.name": {
"message": "Name"
},
"instance.settings.tabs.general.update-channel": {
"message": "Aktualisierungskanal"
},
"instance.settings.tabs.general.update-channel.alpha": {
"message": "Alpha"
},
"instance.settings.tabs.general.update-channel.alpha.description": {
"message": "Voll, Beta- und Alpha-Versionen werden als verfügbare Aktualisierungen angezeigt."
},
"instance.settings.tabs.general.update-channel.beta": {
"message": "Beta"
},
"instance.settings.tabs.general.update-channel.beta.description": {
"message": "Voll- und Beta-Versionen werden als verfügbare Aktualisierungen angezeigt."
},
"instance.settings.tabs.general.update-channel.release": {
"message": "Vollversion"
},
"instance.settings.tabs.general.update-channel.release.description": {
"message": "Nur Vollversionen werden als verfügbare Aktualisierungen angezeigt."
},
"instance.settings.tabs.general.update-channel.select": {
"message": "Aktualisierungskanal auswählen"
},
"instance.settings.tabs.hooks": {
"message": "Startargumente"
},
@@ -437,12 +437,6 @@
"app.skins.rate-limit.title": {
"message": "Slow down!"
},
"app.skins.reorder-error.text": {
"message": "Your skin order could not be saved."
},
"app.skins.reorder-error.title": {
"message": "Failed to reorder skins"
},
"app.skins.section.builders-and-biomes": {
"message": "Builders & Biomes"
},
@@ -2,9 +2,6 @@
"app.action-bar.downloading-java": {
"message": "Descargando Java {version}"
},
"app.action-bar.downloading-update": {
"message": "Descargando actualización"
},
"app.action-bar.downloads": {
"message": "Descargas"
},
@@ -23,18 +20,12 @@
"app.action-bar.primary-instance": {
"message": "Instancia principal"
},
"app.action-bar.reload-to-update": {
"message": "Reinicia para actualizar"
},
"app.action-bar.show-more-running-instances": {
"message": "Mostrar más instancias en ejecución"
},
"app.action-bar.stop-instance": {
"message": "Detener instancia"
},
"app.action-bar.update": {
"message": "Actualizar"
},
"app.action-bar.view-active-downloads": {
"message": "Ver descargas en curso"
},
@@ -312,7 +303,7 @@
"message": "Este proyecto ya está instalado"
},
"app.project.install-context.back-to-browse": {
"message": "Volver al explorador"
"message": "Volver a descubrir contenido"
},
"app.project.install-context.install-content-to-instance": {
"message": "Instalar contenido a la instancia"
@@ -345,7 +336,7 @@
"message": "Añadir skin"
},
"app.skins.add-button.drag-and-drop": {
"message": "Arrastra y suelta"
"message": "Arrastrar y soltar"
},
"app.skins.apply-button": {
"message": "Aplicar"
@@ -354,10 +345,10 @@
"message": "Eliminar skin"
},
"app.skins.delete-modal.description": {
"message": "Esto eliminará permanentemente la skin seleccionada. Esta acción no se puede deshacer."
"message": "Esto eliminará permanentemente el skin seleccionado. Esta acción no se puede deshacer."
},
"app.skins.delete-modal.title": {
"message": "¿Estás seguro de que quieres eliminar esta skin?"
"message": "¿Estás seguro de que quieres eliminar este skin?"
},
"app.skins.dropped-file-error.text": {
"message": "Error al leer el archivo arrastrado."
@@ -372,7 +363,7 @@
"message": "Añadir skin"
},
"app.skins.modal.add-title": {
"message": "Añadiendo una skin"
"message": "Añadiendo un skin"
},
"app.skins.modal.arm-style-section": {
"message": "Estilo de brazo"
@@ -414,7 +405,7 @@
"message": "Textura"
},
"app.skins.modal.upload-skin-first-tooltip": {
"message": "¡Primero carga una skin!"
"message": "¡Primero carga un skin!"
},
"app.skins.preview.edit-button": {
"message": "Editar skin"
@@ -428,12 +419,6 @@
"app.skins.rate-limit.title": {
"message": "¡Más despacio!"
},
"app.skins.reorder-error.text": {
"message": "No se ha podido guardar el orden de las skins."
},
"app.skins.reorder-error.title": {
"message": "Error al reordenar las skins"
},
"app.skins.section.builders-and-biomes": {
"message": "Constructores y Biomas"
},
@@ -459,7 +444,7 @@
"message": "Monturas del Caos"
},
"app.skins.section.saved-skins": {
"message": "Skins guardadas"
"message": "Skins guardados"
},
"app.skins.section.striding-hero": {
"message": "Striding Hero"
@@ -507,7 +492,7 @@
"message": "Descarga completada"
},
"app.update-popup.reload": {
"message": "Reinicia para actualizar"
"message": "Recargar"
},
"app.update-popup.title": {
"message": "Actualización disponible"
@@ -707,30 +692,6 @@
"instance.settings.tabs.general.name": {
"message": "Nombre"
},
"instance.settings.tabs.general.update-channel": {
"message": "Canal de actualizaciones"
},
"instance.settings.tabs.general.update-channel.alpha": {
"message": "Alpha"
},
"instance.settings.tabs.general.update-channel.alpha.description": {
"message": "Las versiones estables, beta y alpha se mostrarán como actualizaciones disponibles. "
},
"instance.settings.tabs.general.update-channel.beta": {
"message": "Beta"
},
"instance.settings.tabs.general.update-channel.beta.description": {
"message": "Las versiones estables y beta serán mostradas como actualizaciones disponibles."
},
"instance.settings.tabs.general.update-channel.release": {
"message": "Estable"
},
"instance.settings.tabs.general.update-channel.release.description": {
"message": "Sólo las versiones estables serán mostradas como actualizaciones disponibles."
},
"instance.settings.tabs.general.update-channel.select": {
"message": "Selecciona el canal de actualizaciones"
},
"instance.settings.tabs.hooks": {
"message": "Hooks de inicio"
},
@@ -921,7 +882,7 @@
"message": "Proporcionado por el servidor"
},
"search.filter.locked.server-environment.title": {
"message": "Solo se pueden añadir mods que sean del lado del cliente a la instancia del servidor"
"message": "Solo se puede añadir mods que sean del lado del cliente a la instancia del servidor"
},
"search.filter.locked.server-game-version.title": {
"message": "La versión del juego es proporcionada por el servidor"
+4 -43
View File
@@ -2,9 +2,6 @@
"app.action-bar.downloading-java": {
"message": "Descargando Java {version}"
},
"app.action-bar.downloading-update": {
"message": "Descargando actualización"
},
"app.action-bar.downloads": {
"message": "Descargas"
},
@@ -23,18 +20,12 @@
"app.action-bar.primary-instance": {
"message": "Instancia principal"
},
"app.action-bar.reload-to-update": {
"message": "Recargar para actualizar"
},
"app.action-bar.show-more-running-instances": {
"message": "Mostrar más instancias en ejecución"
},
"app.action-bar.stop-instance": {
"message": "Detener instancia"
},
"app.action-bar.update": {
"message": "Actualizar"
},
"app.action-bar.view-active-downloads": {
"message": "Mostrar descargas activas"
},
@@ -57,7 +48,7 @@
"message": "Tema de color"
},
"app.appearance-settings.default-landing-page.description": {
"message": "Cambia la página en la que el launcher inicia."
"message": "Cambia la página que se abre al iniciar el lanzador."
},
"app.appearance-settings.default-landing-page.home": {
"message": "Inicio"
@@ -437,12 +428,6 @@
"app.skins.rate-limit.title": {
"message": "¡Cálmate!"
},
"app.skins.reorder-error.text": {
"message": "No se ha podido guardar el orden de las skins."
},
"app.skins.reorder-error.title": {
"message": "Error al reordenar las skins"
},
"app.skins.section.builders-and-biomes": {
"message": "Builders y Biomas"
},
@@ -516,7 +501,7 @@
"message": "Descarga completada"
},
"app.update-popup.reload": {
"message": "Recargar para actualizar"
"message": "Recargar"
},
"app.update-popup.title": {
"message": "Actualización disponible"
@@ -716,30 +701,6 @@
"instance.settings.tabs.general.name": {
"message": "Nombre"
},
"instance.settings.tabs.general.update-channel": {
"message": "Canal de actualizaciones"
},
"instance.settings.tabs.general.update-channel.alpha": {
"message": "Alfa"
},
"instance.settings.tabs.general.update-channel.alpha.description": {
"message": "Se mostrarán las versiones estables, beta y alfa como actualizaciones disponibles."
},
"instance.settings.tabs.general.update-channel.beta": {
"message": "Beta"
},
"instance.settings.tabs.general.update-channel.beta.description": {
"message": "Se mostrarán las versiones estables y beta como actualizaciones disponibles."
},
"instance.settings.tabs.general.update-channel.release": {
"message": "Estable"
},
"instance.settings.tabs.general.update-channel.release.description": {
"message": "Solo se mostrarán las versiones estables como actualizaciones disponibles."
},
"instance.settings.tabs.general.update-channel.select": {
"message": "Seleccionar canal de actualizaciones"
},
"instance.settings.tabs.hooks": {
"message": "Hooks de lanzamiento"
},
@@ -921,7 +882,7 @@
"message": "Versión del juego proporciona por la instancia"
},
"search.filter.locked.instance-loader.title": {
"message": "Loader proporcionado por la instancia"
"message": "Cargador proporcionado por la instancia"
},
"search.filter.locked.instance.sync": {
"message": "Sincronizar con la instancia"
@@ -936,7 +897,7 @@
"message": "La versión del juego es proporcionada por el servidor"
},
"search.filter.locked.server-loader.title": {
"message": "Loader proporcionado por el servidor"
"message": "El cargador lo proporciona el servidor"
},
"unknown-pack-warning-modal.body": {
"message": "Un archivo solo es revisado si es subido a Modrinth, independientemente de su formato de archivo (incluyendo .mrpack)."
+1 -28
View File
@@ -143,9 +143,6 @@
"app.browse.server.installing": {
"message": "Asennetaan"
},
"app.content-install.no-compatible-versions": {
"message": "Yhtään versiota ei ole saatavilla yhteensopivuudella {compatibilityLabel}. Valitse asennettava versio siitä huolimatta. Riippuvuuksia ei asenneta automaattisesti."
},
"app.creation-modal.installing-modpack.description": {
"message": "{fileName}"
},
@@ -335,21 +332,6 @@
"app.settings.tabs.resource-management": {
"message": "Resurssien hallinta"
},
"app.skins.reorder-error.title": {
"message": "Skinien järjestyksen muuttaminen epäonnistui"
},
"app.skins.section.mounts-of-mayhem": {
"message": "Sekasorron ratsut"
},
"app.skins.section.tiny-takeover": {
"message": "Pienten vallankumous"
},
"app.skins.sign-in.title": {
"message": "Kirjaudu sisään"
},
"app.skins.title": {
"message": "Skinivalinta"
},
"app.update-popup.body.download-complete": {
"message": "Modrinth App versio {version} on ladattu. Voit käynnistää sovelluksen uudelleen päivittääksesi heti tai antaa päivityksen asentua automaattisesti, kun suljet Modrinth Appin."
},
@@ -369,7 +351,7 @@
"message": "Lataus valmis"
},
"app.update-popup.reload": {
"message": "Lataa uudelleen päivittääksesi"
"message": "Lataa uudelleen"
},
"app.update-popup.title": {
"message": "Päivitys saatavilla"
@@ -569,15 +551,6 @@
"instance.settings.tabs.general.name": {
"message": "Nimi"
},
"instance.settings.tabs.general.update-channel.alpha": {
"message": "Alfa"
},
"instance.settings.tabs.general.update-channel.beta": {
"message": "Beta"
},
"instance.settings.tabs.general.update-channel.release": {
"message": "Julkaisu"
},
"instance.settings.tabs.hooks": {
"message": "Aloitustoiminnot"
},
@@ -2,9 +2,6 @@
"app.action-bar.downloading-java": {
"message": "Dina-download ang Java {version}"
},
"app.action-bar.downloading-update": {
"message": "Dina-download ang update"
},
"app.action-bar.downloads": {
"message": "Mga downloads"
},
@@ -23,18 +20,12 @@
"app.action-bar.primary-instance": {
"message": "Pangunahing instansiya"
},
"app.action-bar.reload-to-update": {
"message": "Mag-reload upang ma-update"
},
"app.action-bar.show-more-running-instances": {
"message": "Ipakita ang mas marami pang tumatakbong instansiya"
},
"app.action-bar.stop-instance": {
"message": "Itigil ang instansiya"
},
"app.action-bar.update": {
"message": "Mag-update"
},
"app.action-bar.view-active-downloads": {
"message": "Tignan ang mga active downloads"
},
@@ -95,9 +86,6 @@
"app.appearance-settings.select-option": {
"message": "Pumili ng opsiyon"
},
"app.appearance-settings.show-play-time.description": {
"message": "Magpapakita ang katagal ng iyong paglalaro sa isang instansiya."
},
"app.appearance-settings.toggle-sidebar.description": {
"message": "Pagpapagana na mata-toggle ang sidebar."
},
@@ -389,6 +377,9 @@
"app.update-popup.download-complete": {
"message": "Nakumpleto ang pagdownload"
},
"app.update-popup.reload": {
"message": "Mag-reload"
},
"app.update-popup.title": {
"message": "May bagong update"
},
@@ -587,12 +578,6 @@
"instance.settings.tabs.general.name": {
"message": "Pangalan"
},
"instance.settings.tabs.general.update-channel.alpha": {
"message": "Alpha"
},
"instance.settings.tabs.general.update-channel.beta": {
"message": "Beta"
},
"instance.settings.tabs.hooks": {
"message": "Mga launch hook"
},
+2 -41
View File
@@ -2,9 +2,6 @@
"app.action-bar.downloading-java": {
"message": "Téléchargement de Java {version} en cours"
},
"app.action-bar.downloading-update": {
"message": "Téléchargement de la mise à jour"
},
"app.action-bar.downloads": {
"message": "Téléchargements"
},
@@ -23,18 +20,12 @@
"app.action-bar.primary-instance": {
"message": "Instance première"
},
"app.action-bar.reload-to-update": {
"message": "Recharger pour mettre à jour"
},
"app.action-bar.show-more-running-instances": {
"message": "Montrer plus d'instances en exécution"
},
"app.action-bar.stop-instance": {
"message": "Arrêter l'instance"
},
"app.action-bar.update": {
"message": "Mettre à jour"
},
"app.action-bar.view-active-downloads": {
"message": "Voir les téléchargements actifs"
},
@@ -102,7 +93,7 @@
"message": "Afficher le temps de jeu"
},
"app.appearance-settings.toggle-sidebar.description": {
"message": "Permet d'ouvrir ou de fermer la barre latérale."
"message": "Permet d'ouvrir de fermer la barre latérale."
},
"app.appearance-settings.toggle-sidebar.title": {
"message": "Tiroir latéral"
@@ -428,12 +419,6 @@
"app.skins.rate-limit.title": {
"message": "Du calme !"
},
"app.skins.reorder-error.text": {
"message": "L'ordre des skins n'a pas pu être sauvegardé."
},
"app.skins.reorder-error.title": {
"message": "Impossible de réorganiser les skins"
},
"app.skins.section.builders-and-biomes": {
"message": "Builders & Biomes"
},
@@ -507,7 +492,7 @@
"message": "Téléchargement terminé"
},
"app.update-popup.reload": {
"message": "Recharger pour mettre à jour"
"message": "Recharger"
},
"app.update-popup.title": {
"message": "Mise à jour disponible"
@@ -704,30 +689,6 @@
"instance.settings.tabs.general.name": {
"message": "Nom"
},
"instance.settings.tabs.general.update-channel": {
"message": "Canal de mise à jour"
},
"instance.settings.tabs.general.update-channel.alpha": {
"message": "Alpha"
},
"instance.settings.tabs.general.update-channel.alpha.description": {
"message": "Les versions stables, bêta et alpha seront affichées comme mises à jour disponibles."
},
"instance.settings.tabs.general.update-channel.beta": {
"message": "Bêta"
},
"instance.settings.tabs.general.update-channel.beta.description": {
"message": "Les versions stables et bêta seront affichées comme mises à jour disponibles."
},
"instance.settings.tabs.general.update-channel.release": {
"message": "Stable"
},
"instance.settings.tabs.general.update-channel.release.description": {
"message": "Seules les versions stables seront affichées comme mises à jour disponibles."
},
"instance.settings.tabs.general.update-channel.select": {
"message": "Sélectionner le canal de mise à jour"
},
"instance.settings.tabs.hooks": {
"message": "Crochets de lancement"
},
+3 -18
View File
@@ -1,19 +1,7 @@
{
"app.action-bar.downloading-java": {
"message": "מוריד את Java {version}"
},
"app.action-bar.downloading-update": {
"message": "מוריד עדכון"
},
"app.action-bar.downloads": {
"message": "הורדות"
},
"app.action-bar.hide-more-running-instances": {
"message": "החבא יותר התקנים רצים"
},
"app.action-bar.make-primary-instance": {
"message": "הפוך למקרה ראשי"
},
"app.action-bar.offline": {
"message": "לא מקוון"
},
@@ -23,9 +11,6 @@
"app.appearance-settings.advanced-rendering.description": {
"message": "מאפשר עיבוד מתקדם כגון אפקטים טשטוש העלולים לגרום לבעיות ביצועים ללא עיבוד מואץ לחומרה."
},
"app.appearance-settings.jump-back-into-worlds.title": {
"message": "חזרה לעולמות"
},
"app.auth-servers.unreachable.body": {
"message": "ייתכן ששרתי האימות של Minecraft מושבתים כרגע. יש לבדוק את חיבור האינטרנט שלך ולנסות שוב מאוחר יותר."
},
@@ -236,6 +221,9 @@
"app.update-popup.download-complete": {
"message": "הורדה הושלמה"
},
"app.update-popup.reload": {
"message": "רענן"
},
"app.update-popup.title": {
"message": "עדכון זמין"
},
@@ -266,9 +254,6 @@
"friends.action.add-friend": {
"message": "הוספת חבר"
},
"friends.action.view-friend-requests": {
"message": "{count, plural, one {בקשת חברות אחת} other {# בקשות חברות}}"
},
"friends.add-friend.submit": {
"message": "שליחת בקשת חברות"
},
+2 -41
View File
@@ -2,9 +2,6 @@
"app.action-bar.downloading-java": {
"message": "A Java {version} letöltése"
},
"app.action-bar.downloading-update": {
"message": "Frissítés letöltése"
},
"app.action-bar.downloads": {
"message": "Letöltések"
},
@@ -23,18 +20,12 @@
"app.action-bar.primary-instance": {
"message": "Elsődleges példány"
},
"app.action-bar.reload-to-update": {
"message": "Töltsd újra a frissítéshez"
},
"app.action-bar.show-more-running-instances": {
"message": "További futó pédányok megjelenítése"
},
"app.action-bar.stop-instance": {
"message": "Példány leállítása"
},
"app.action-bar.update": {
"message": "Frissítés"
},
"app.action-bar.view-active-downloads": {
"message": "Aktív letöltések megtekintése"
},
@@ -78,7 +69,7 @@
"message": "A kezdőlap „Folytasd itt” részében a legújabb világok is megtalálhatók."
},
"app.appearance-settings.jump-back-into-worlds.title": {
"message": "Visszaugrás a világokba"
"message": "„Folytasd itt” a világokat is"
},
"app.appearance-settings.minimize-launcher.description": {
"message": "Minimalizáld az indítót a Minecraft indításakor."
@@ -290,9 +281,6 @@
"app.modal.install-to-play.install-button": {
"message": "Telepítés"
},
"app.modal.install-to-play.mod-count": {
"message": "{count} mod"
},
"app.modal.install-to-play.required-modpack": {
"message": "Szükséges modcsomag"
},
@@ -510,7 +498,7 @@
"message": "Sikeres letöltés"
},
"app.update-popup.reload": {
"message": "Töltsd újra a frissítéshez"
"message": "Frissítés"
},
"app.update-popup.title": {
"message": "Frissítés elérhető"
@@ -542,9 +530,6 @@
"friends.action.add-friend": {
"message": "Barát hozzáadása"
},
"friends.action.view-friend-requests": {
"message": "{count} barát {count, plural, other {kérelem}}"
},
"friends.add-friend.submit": {
"message": "Barátkérelem elküldése"
},
@@ -710,30 +695,6 @@
"instance.settings.tabs.general.name": {
"message": "Név"
},
"instance.settings.tabs.general.update-channel": {
"message": "Frissítési csatorna"
},
"instance.settings.tabs.general.update-channel.alpha": {
"message": "Alfa"
},
"instance.settings.tabs.general.update-channel.alpha.description": {
"message": "A kiadás, béta és az alfa verziók is meg fognak jelenni az elérhető frissítések között."
},
"instance.settings.tabs.general.update-channel.beta": {
"message": "Béta"
},
"instance.settings.tabs.general.update-channel.beta.description": {
"message": "A kiadás és a béta verziók is meg fognak jelenni az elérhető frissítések között."
},
"instance.settings.tabs.general.update-channel.release": {
"message": "Kiadás"
},
"instance.settings.tabs.general.update-channel.release.description": {
"message": "Csak a release verziók fognak megjelenni az elérhető frissítések között."
},
"instance.settings.tabs.general.update-channel.select": {
"message": "Válaszd ki a frissítési csatornát"
},
"instance.settings.tabs.hooks": {
"message": "Indítási horgok"
},
+1 -55
View File
@@ -2,9 +2,6 @@
"app.action-bar.downloading-java": {
"message": "Mengunduh Java {version}"
},
"app.action-bar.downloading-update": {
"message": "Mengunduh pembaruan"
},
"app.action-bar.downloads": {
"message": "Unduhan"
},
@@ -23,18 +20,12 @@
"app.action-bar.primary-instance": {
"message": "Instans utama"
},
"app.action-bar.reload-to-update": {
"message": "Muat ulang untuk memperbarui"
},
"app.action-bar.show-more-running-instances": {
"message": "Tampilkan lebih banyak instans yang sedang berjalan"
},
"app.action-bar.stop-instance": {
"message": "Hentikan instans"
},
"app.action-bar.update": {
"message": "Perbarui"
},
"app.action-bar.view-active-downloads": {
"message": "Lihat pengunduhan berlangsung"
},
@@ -95,12 +86,6 @@
"app.appearance-settings.select-option": {
"message": "Pilih opsi"
},
"app.appearance-settings.show-play-time.description": {
"message": "Menampilkan seberapa lama Anda memainkan sebuah instans."
},
"app.appearance-settings.show-play-time.title": {
"message": "Tampilkan waktu bermain"
},
"app.appearance-settings.toggle-sidebar.description": {
"message": "Menghidupkan kemampuan untuk menghidupkan dan mematikan bilah sisi."
},
@@ -158,9 +143,6 @@
"app.browse.server.installing": {
"message": "Memasang"
},
"app.content-install.no-compatible-versions": {
"message": "Tidak ada versi tersedia yang cocok untuk {compatibilityLabel}. Anda tetap dapat memilih versi yang akan dipasang. Dependensi tidak akan dipasang secara otomatis."
},
"app.creation-modal.installing-modpack.description": {
"message": "{fileName}"
},
@@ -290,9 +272,6 @@
"app.modal.install-to-play.install-button": {
"message": "Pasang"
},
"app.modal.install-to-play.mod-count": {
"message": "{count, plural, other {# mod}}"
},
"app.modal.install-to-play.required-modpack": {
"message": "Paket mod yang diperlukan"
},
@@ -437,12 +416,6 @@
"app.skins.rate-limit.title": {
"message": "Pelan-pelan!"
},
"app.skins.reorder-error.text": {
"message": "Urutan rupa Anda tidak dapat disimpan."
},
"app.skins.reorder-error.title": {
"message": "Gagal mengurut ulang rupa"
},
"app.skins.section.builders-and-biomes": {
"message": "Builders & Biomes"
},
@@ -516,7 +489,7 @@
"message": "Selesai mengunduh"
},
"app.update-popup.reload": {
"message": "Muat ulang untuk memperbarui"
"message": "Muat ulang"
},
"app.update-popup.title": {
"message": "Pembaruan tersedia"
@@ -548,9 +521,6 @@
"friends.action.add-friend": {
"message": "Tambah teman"
},
"friends.action.view-friend-requests": {
"message": "{count} {count, plural, other {permintaan}} teman"
},
"friends.add-friend.submit": {
"message": "Kirim permintaan teman"
},
@@ -716,30 +686,6 @@
"instance.settings.tabs.general.name": {
"message": "Nama"
},
"instance.settings.tabs.general.update-channel": {
"message": "Perbarui kanal"
},
"instance.settings.tabs.general.update-channel.alpha": {
"message": "Alpha"
},
"instance.settings.tabs.general.update-channel.alpha.description": {
"message": "Versi rilisan, beta, dan alpha akan ditampilkan sebagai pembaruan yang tersedia."
},
"instance.settings.tabs.general.update-channel.beta": {
"message": "Beta"
},
"instance.settings.tabs.general.update-channel.beta.description": {
"message": "Versi beta dan alpha akan ditampilkan sebagai pembaruan yang tersedia."
},
"instance.settings.tabs.general.update-channel.release": {
"message": "Rilisan"
},
"instance.settings.tabs.general.update-channel.release.description": {
"message": "Hanya versi rilisan yang akan ditampilkan sebagai pembaruan yang tersedia."
},
"instance.settings.tabs.general.update-channel.select": {
"message": "Pilih kanal pembaruan"
},
"instance.settings.tabs.hooks": {
"message": "Luncurkan kait"
},
+25 -64
View File
@@ -2,9 +2,6 @@
"app.action-bar.downloading-java": {
"message": "Java {version}"
},
"app.action-bar.downloading-update": {
"message": "Scaricando l'aggiornamento"
},
"app.action-bar.downloads": {
"message": "Download"
},
@@ -23,18 +20,12 @@
"app.action-bar.primary-instance": {
"message": "Primaria"
},
"app.action-bar.reload-to-update": {
"message": "Ricarica per aggiornare"
},
"app.action-bar.show-more-running-instances": {
"message": "Mostra più istanze in esecuzione"
},
"app.action-bar.stop-instance": {
"message": "Ferma l'istanza"
},
"app.action-bar.update": {
"message": "Aggiorna"
},
"app.action-bar.view-active-downloads": {
"message": "Mostra i download attivi"
},
@@ -99,7 +90,7 @@
"message": "Mostra quanto tempo hai speso giocando a un'istanza."
},
"app.appearance-settings.show-play-time.title": {
"message": "Mostra le ore di gioco"
"message": "Mostra tempo di gioco"
},
"app.appearance-settings.toggle-sidebar.description": {
"message": "Mostra o nascondi la barra laterale."
@@ -153,13 +144,13 @@
"message": "Pacchetti di mod"
},
"app.browse.server-instance-content-warning": {
"message": "Aggiungere dei contenuti potrebbe causare problemi entrando nel server. Inoltre, qualsiasi contenuto aggiunto sarà perso quando aggiorni i contenuti dell'istanza."
"message": "Aggiungere dei contenuti potrebbe causare problemi entrando nel server. Inoltre, qualsiasi contenuto aggiunto sarà perso a ogni aggiornamento dei contenuti dell'istanza."
},
"app.browse.server.installing": {
"message": "Installazione"
},
"app.content-install.no-compatible-versions": {
"message": "Nessuna versione disponibile soddisfa {compatibilityLabel}. Puoi selezionare una versione da installare comunque. Le dipendenze non verranno scaricate automaticamente."
"message": "Nessuna versione disponibile corrisponde a {compatibilityLabel}. Seleziona una versione da installare comunque. Le dipendenze non verranno scaricate automaticamente."
},
"app.creation-modal.installing-modpack.description": {
"message": "{fileName}"
@@ -224,6 +215,9 @@
"app.instance.mods.project-was-added": {
"message": "\"{name}\" è stato aggiunto"
},
"app.instance.mods.projects-were-added": {
"message": "{count} progetti aggiunti"
},
"app.instance.mods.share-text": {
"message": "Guarda le mod che uso nel mio pacchetto!"
},
@@ -278,9 +272,6 @@
"app.modal.install-to-play.install-button": {
"message": "Installa"
},
"app.modal.install-to-play.mod-count": {
"message": "{count} mod"
},
"app.modal.install-to-play.required-modpack": {
"message": "Pacchetto di mod richiesto"
},
@@ -342,7 +333,7 @@
"message": "Nuova skin"
},
"app.skins.add-button.drag-and-drop": {
"message": "Trascina qui"
"message": "Trascina e rilascia"
},
"app.skins.apply-button": {
"message": "Applica"
@@ -357,7 +348,7 @@
"message": "Vuoi davvero eliminare questa skin?"
},
"app.skins.dropped-file-error.text": {
"message": "Non è stato possibile leggere il file trascinato."
"message": "Non è stato possibile leggere il file rilasciato."
},
"app.skins.dropped-file-error.title": {
"message": "Impossibile processare il file"
@@ -366,13 +357,13 @@
"message": "Modifica"
},
"app.skins.modal.add-skin-button": {
"message": "Aggiungi"
"message": "Salva"
},
"app.skins.modal.add-title": {
"message": "Aggiungi una skin"
"message": "Aggiungi una nuova skin"
},
"app.skins.modal.arm-style-section": {
"message": "Modello"
"message": "Modello del giocatore"
},
"app.skins.modal.arm-style-slim": {
"message": "Snello"
@@ -390,7 +381,7 @@
"message": "Modifica la skin"
},
"app.skins.modal.make-edit-first-tooltip": {
"message": "Modifica prima la skin!"
"message": "Modifica la skin prima!"
},
"app.skins.modal.no-cape-tooltip": {
"message": "Nessun mantello"
@@ -399,19 +390,19 @@
"message": "Nessuno"
},
"app.skins.modal.replace-texture-button": {
"message": "Cambia"
"message": "Sfoglia"
},
"app.skins.modal.save-skin-button": {
"message": "Salva"
},
"app.skins.modal.saving-tooltip": {
"message": "Salvando..."
"message": "Salvataggio..."
},
"app.skins.modal.texture-section": {
"message": "Aspetto"
"message": "File della skin"
},
"app.skins.modal.upload-skin-first-tooltip": {
"message": "Carica prima una skin!"
"message": "Carica una skin prima!"
},
"app.skins.preview.edit-button": {
"message": "Modifica"
@@ -420,17 +411,11 @@
"message": "Anteprima"
},
"app.skins.rate-limit.text": {
"message": "Stai cambiando skin troppo spesso. I server di Mojang hanno limitato le nostre richieste. Aspetta un momento prima di riprovare."
"message": "Stai modificando la skin troppo spesso. I server di Mojang hanno limitato le nostre richieste. Aspetta un momento prima di riprovare."
},
"app.skins.rate-limit.title": {
"message": "Rallenta!"
},
"app.skins.reorder-error.text": {
"message": "L'ordine delle skin non è stato salvato."
},
"app.skins.reorder-error.title": {
"message": "Errore nel riordine delle skin"
},
"app.skins.section.builders-and-biomes": {
"message": "Builders & Biomes"
},
@@ -456,7 +441,7 @@
"message": "Mounts of Mayhem"
},
"app.skins.section.saved-skins": {
"message": "Skin salvate"
"message": "Raccolta"
},
"app.skins.section.striding-hero": {
"message": "Striding Hero"
@@ -474,13 +459,13 @@
"message": "Accedi"
},
"app.skins.sign-in.description": {
"message": "Accedi col tuo account Minecraft per poter gestire le skin qui."
"message": "Accedi col tuo account Minecraft per usare la gestione delle skin."
},
"app.skins.sign-in.rinthbot-alt": {
"message": "Modrinth Bot emozionato"
},
"app.skins.sign-in.title": {
"message": "Effettua l'accesso"
"message": "Accedi"
},
"app.skins.title": {
"message": "Seleziona una skin"
@@ -537,7 +522,7 @@
"message": "Stringi un'amicizia"
},
"friends.action.view-friend-requests": {
"message": "{count, plural, one {# richiesta} other {# richieste}} d''amicizia"
"message": "{count} {count, plural, one {richiesta} other {richieste}} d''amicizia"
},
"friends.add-friend.submit": {
"message": "Invia richiesta d'amicizia"
@@ -704,30 +689,6 @@
"instance.settings.tabs.general.name": {
"message": "Nome"
},
"instance.settings.tabs.general.update-channel": {
"message": "Canale di aggiornamento"
},
"instance.settings.tabs.general.update-channel.alpha": {
"message": "Alpha"
},
"instance.settings.tabs.general.update-channel.alpha.description": {
"message": "Le versioni stabili, beta e alpha saranno mostrate come aggiornamenti disponibili."
},
"instance.settings.tabs.general.update-channel.beta": {
"message": "Beta"
},
"instance.settings.tabs.general.update-channel.beta.description": {
"message": "Le versioni stabili e beta saranno mostrate come aggiornamenti disponibili."
},
"instance.settings.tabs.general.update-channel.release": {
"message": "Stabile"
},
"instance.settings.tabs.general.update-channel.release.description": {
"message": "Solo le versioni stabili saranno mostrate come aggiornamenti disponibili."
},
"instance.settings.tabs.general.update-channel.select": {
"message": "Seleziona il canale di aggiornamento"
},
"instance.settings.tabs.hooks": {
"message": "Hook di avvio"
},
@@ -777,16 +738,16 @@
"message": "Java e memoria"
},
"instance.settings.tabs.java.custom-environment-variables": {
"message": "Personalizzate"
"message": "Variabili d'ambiente"
},
"instance.settings.tabs.java.custom-java-arguments": {
"message": "Personalizzati"
"message": "Argomenti JVM"
},
"instance.settings.tabs.java.custom-java-installation": {
"message": "Personalizzata"
"message": "Eseguibile di Java"
},
"instance.settings.tabs.java.custom-memory-allocation": {
"message": "Personalizzata"
"message": "Memoria allocata"
},
"instance.settings.tabs.java.enter-environment-variables": {
"message": "Inserisci le variabili d'ambiente..."
+12 -75
View File
@@ -2,9 +2,6 @@
"app.action-bar.downloading-java": {
"message": "Java {version} をダウンロード中"
},
"app.action-bar.downloading-update": {
"message": "アップデートをダウンロード中"
},
"app.action-bar.downloads": {
"message": "ダウンロード"
},
@@ -45,7 +42,7 @@
"message": "高度なレンダリング"
},
"app.appearance-settings.color-theme.description": {
"message": "Modrinth Appでのお好みのテーマを選択してください"
"message": "Modrinth Appでのお好みのテーマを選択してください"
},
"app.appearance-settings.color-theme.title": {
"message": "テーマ"
@@ -63,25 +60,25 @@
"message": "デフォルトの起動ページ"
},
"app.appearance-settings.hide-nametag.description": {
"message": "スキンページでプレイヤー上部のネームタグを非表示にします。"
"message": "スキンページでプレイヤー名の上にある名前タグを無効にします。"
},
"app.appearance-settings.hide-nametag.title": {
"message": "ネームタグを非表示にする"
"message": "名札を非表示にする"
},
"app.appearance-settings.jump-back-into-worlds.description": {
"message": "ホームページの「ジャンプバック」セクションには、最近追加されたワールドが含まれています。"
},
"app.appearance-settings.jump-back-into-worlds.title": {
"message": "最近のワールドを表示"
"message": "再び世界へ飛び込もう"
},
"app.appearance-settings.minimize-launcher.description": {
"message": "Minecraftが起動した時、ランチャーを最小化します"
"message": "Minecraftが起動した時、ランチャーを最小化します"
},
"app.appearance-settings.minimize-launcher.title": {
"message": "ランチャーを最小化"
},
"app.appearance-settings.native-decorations.description": {
"message": "OSデフォルトのウィンドウUIを使用します。(アプリの再起動が必要です"
"message": "OSデフォルトのウィンドウUIを使用します(アプリの再起動が必要です)"
},
"app.appearance-settings.native-decorations.title": {
"message": "OSのウィンドウUIを使用"
@@ -89,23 +86,17 @@
"app.appearance-settings.select-option": {
"message": "オプションを選択してください"
},
"app.appearance-settings.show-play-time.description": {
"message": "インスタンスのプレイ時間を表示します。"
},
"app.appearance-settings.show-play-time.title": {
"message": "プレイ時間を表示"
},
"app.appearance-settings.toggle-sidebar.description": {
"message": "サイドバーの表示/非表示を切り替える機能を有効にします。"
},
"app.appearance-settings.toggle-sidebar.title": {
"message": "サイドバーの表示切替を有効化"
"message": "サイドバーの表示/非表示を切り替える"
},
"app.appearance-settings.unknown-pack-warning.description": {
"message": "Modrinthで配布されていないModrinth Pack.mrpack)をインストールする場合、事前にリスクについて警告します。"
"message": "Modrinth上にホストされていないModrinth Packファイル.mrpack)をインストールしようとした場合、インストール前にそのリスクを理解していただけるよう確認します。"
},
"app.appearance-settings.unknown-pack-warning.title": {
"message": "不明なModパックインストール前に警告する"
"message": "未知のMODパックインストールする前に警告してください"
},
"app.auth-servers.unreachable.body": {
"message": "Minecraftの認証サーバーは現在停止している可能性があります。インターネット接続を確認し、しばらくしてからもう一度お試しください。"
@@ -263,9 +254,6 @@
"app.modal.install-to-play.install-button": {
"message": "インストール"
},
"app.modal.install-to-play.mod-count": {
"message": "{count, plural, other {#個のMod}}"
},
"app.modal.install-to-play.required-modpack": {
"message": "必須のModパック"
},
@@ -314,21 +302,6 @@
"app.settings.tabs.resource-management": {
"message": "リソース管理"
},
"app.skins.modal.arm-style-slim": {
"message": "スリム"
},
"app.skins.modal.arm-style-wide": {
"message": "ワイド"
},
"app.skins.modal.cape-fallback-name": {
"message": "マント"
},
"app.skins.modal.cape-section": {
"message": "マント"
},
"app.skins.modal.no-cape-tooltip": {
"message": "マントなし"
},
"app.skins.modal.replace-texture-button": {
"message": "置きかえる"
},
@@ -338,42 +311,6 @@
"app.skins.preview.edit-button": {
"message": "編集"
},
"app.skins.section.builders-and-biomes": {
"message": "ビルダーズ&バイオーム"
},
"app.skins.section.chase-the-skies": {
"message": "チェイス・ザ・スカイ"
},
"app.skins.section.minecon-earth-2017": {
"message": "MINECON Earth 2017"
},
"app.skins.section.modrinth": {
"message": "Modrinth"
},
"app.skins.section.modrinth-pride": {
"message": "Modrinth プライド"
},
"app.skins.section.mounts-of-mayhem": {
"message": "マウント・オブ・メイヘム"
},
"app.skins.section.saved-skins": {
"message": "保存済みのスキン"
},
"app.skins.section.striding-hero": {
"message": "Striding Hero"
},
"app.skins.section.the-copper-age": {
"message": "銅の時代"
},
"app.skins.section.the-garden-awakens": {
"message": "ガーデン・アウェイケンズ"
},
"app.skins.section.tiny-takeover": {
"message": "タイニー・テイクオーバー"
},
"app.skins.sign-in.rinthbot-alt": {
"message": "ワクワクしている Modrinth ボット"
},
"app.update-popup.body.download-complete": {
"message": "Modrinth App v{version} のダウンロードが完了しました。今すぐ更新するには再読み込みするか、Modrinth Appを閉じる際に自動更新されます。"
},
@@ -392,6 +329,9 @@
"app.update-popup.download-complete": {
"message": "ダウンロード完了"
},
"app.update-popup.reload": {
"message": "再読み込み"
},
"app.update-popup.title": {
"message": "アップデートが利用可能です"
},
@@ -422,9 +362,6 @@
"friends.action.add-friend": {
"message": "フレンドを追加"
},
"friends.action.view-friend-requests": {
"message": "{count}件の友達リクエスト"
},
"friends.add-friend.submit": {
"message": "フレンド申請を送信"
},
@@ -2,9 +2,6 @@
"app.action-bar.downloading-java": {
"message": "Java {version} 다운로드 중"
},
"app.action-bar.downloading-update": {
"message": "업데이트 다운로드중"
},
"app.action-bar.downloads": {
"message": "다운로드"
},
@@ -503,6 +500,9 @@
"app.update-popup.download-complete": {
"message": "다운로드 완료"
},
"app.update-popup.reload": {
"message": "새로고침"
},
"app.update-popup.title": {
"message": "업데이트 가능"
},
@@ -533,9 +533,6 @@
"friends.action.add-friend": {
"message": "친구 추가"
},
"friends.action.view-friend-requests": {
"message": "{count}건의 친구 요청"
},
"friends.add-friend.submit": {
"message": "친구 요청 보내기"
},
@@ -275,9 +275,6 @@
"app.modal.install-to-play.install-button": {
"message": "Pasang"
},
"app.modal.install-to-play.mod-count": {
"message": "{count, plural, other {# mod}}"
},
"app.modal.install-to-play.required-modpack": {
"message": "Pek mod yang diperlukan"
},
@@ -458,6 +455,9 @@
"app.update-popup.download-complete": {
"message": "Muat turun selesai"
},
"app.update-popup.reload": {
"message": "Muat semula"
},
"app.update-popup.title": {
"message": "Kemas kini tersedia"
},
@@ -488,9 +488,6 @@
"friends.action.add-friend": {
"message": "Tambah rakan"
},
"friends.action.view-friend-requests": {
"message": "{count} {count, plural, other {permintaan}} rakan"
},
"friends.add-friend.submit": {
"message": "Hantar permintaan rakan"
},
+1 -217
View File
@@ -2,9 +2,6 @@
"app.action-bar.downloading-java": {
"message": "Java {version} aan het downloaden"
},
"app.action-bar.downloading-update": {
"message": "Update aan het downloaden"
},
"app.action-bar.downloads": {
"message": "Downloads"
},
@@ -23,18 +20,12 @@
"app.action-bar.primary-instance": {
"message": "Primaire instantie"
},
"app.action-bar.reload-to-update": {
"message": "Herlaadt om te updaten"
},
"app.action-bar.show-more-running-instances": {
"message": "Toon meer actieve instanties"
},
"app.action-bar.stop-instance": {
"message": "Stop instantie"
},
"app.action-bar.update": {
"message": "Updaten"
},
"app.action-bar.view-active-downloads": {
"message": "Bekijk actieve downloads"
},
@@ -95,12 +86,6 @@
"app.appearance-settings.select-option": {
"message": "Selecteer een optie"
},
"app.appearance-settings.show-play-time.description": {
"message": "Toont hoeveel tijd je aan een instantie hebt besteed."
},
"app.appearance-settings.show-play-time.title": {
"message": "Speeltijd laten zien"
},
"app.appearance-settings.toggle-sidebar.description": {
"message": "Schakelt de mogelijkheid om de zijbalk in- of uit te schakelen in."
},
@@ -158,9 +143,6 @@
"app.browse.server.installing": {
"message": "Installeren"
},
"app.content-install.no-compatible-versions": {
"message": "Er zijn geen beschikbare versies die overeenkomen met {compatibilityLabel}. Selecteer toch een versie om te installeren. Afhankelijkheden worden niet automatisch geïnstalleerd."
},
"app.creation-modal.installing-modpack.description": {
"message": "{fileName}"
},
@@ -272,9 +254,6 @@
"app.instance.worlds.remove-server-title": {
"message": "Ben je zeker dat je {name} wil verwijderen?"
},
"app.instance.worlds.search-worlds-placeholder": {
"message": "Zoek werelden"
},
"app.instance.worlds.this-server": {
"message": "deze server"
},
@@ -317,9 +296,6 @@
"app.project.install-button.already-installed": {
"message": "Dit project is al geïnstalleerd"
},
"app.project.install-context.back-to-browse": {
"message": "Terug naar Ontdekken"
},
"app.project.install-context.install-content-to-instance": {
"message": "Installeer content naar instantie"
},
@@ -347,153 +323,6 @@
"app.settings.tabs.resource-management": {
"message": "Bronnenbeheer"
},
"app.skins.add-button": {
"message": "Skin toevoegen"
},
"app.skins.add-button.drag-and-drop": {
"message": "Slepen en neerzetten"
},
"app.skins.apply-button": {
"message": "Toepassen"
},
"app.skins.delete-button": {
"message": "Skin verwijderen"
},
"app.skins.delete-modal.description": {
"message": "De geselecteerde skin wordt permanent verwijderd. Deze actie kan niet ongedaan worden gemaakt."
},
"app.skins.delete-modal.title": {
"message": "Weet u zeker dat u deze skin wilt verwijderen?"
},
"app.skins.dropped-file-error.text": {
"message": "Kan het neergezette bestand niet lezen."
},
"app.skins.dropped-file-error.title": {
"message": "Fout bij het verwerken van het bestand"
},
"app.skins.edit-button": {
"message": "Skin aanpassen"
},
"app.skins.modal.add-skin-button": {
"message": "Skin toevoegen"
},
"app.skins.modal.add-title": {
"message": "Voeg een skin toe"
},
"app.skins.modal.arm-style-section": {
"message": "Armstijl"
},
"app.skins.modal.arm-style-slim": {
"message": "Slank"
},
"app.skins.modal.arm-style-wide": {
"message": "Breed"
},
"app.skins.modal.cape-fallback-name": {
"message": "Cape"
},
"app.skins.modal.cape-section": {
"message": "Cape"
},
"app.skins.modal.edit-title": {
"message": "Skin aan het aanpassen"
},
"app.skins.modal.make-edit-first-tooltip": {
"message": "Verander eerst iets aan de skin!"
},
"app.skins.modal.no-cape-tooltip": {
"message": "Geen cape"
},
"app.skins.modal.none-cape-option": {
"message": "Geen"
},
"app.skins.modal.replace-texture-button": {
"message": "Texture vervangen"
},
"app.skins.modal.save-skin-button": {
"message": "Skin opslaan"
},
"app.skins.modal.saving-tooltip": {
"message": "Opslaan..."
},
"app.skins.modal.texture-section": {
"message": "Textuur"
},
"app.skins.modal.upload-skin-first-tooltip": {
"message": "Upload eerst een skin!"
},
"app.skins.preview.edit-button": {
"message": "Skin aanpassen"
},
"app.skins.previewing-badge": {
"message": "Voorvertonen"
},
"app.skins.rate-limit.text": {
"message": "U verandert te snel van skin. Mojang's servers hebben nieuwe aanvragen tijdelijk geblokkeerd. Wacht een moment voordat u het opnieuw probeert."
},
"app.skins.rate-limit.title": {
"message": "Rustig aan!"
},
"app.skins.reorder-error.text": {
"message": "Je skinvolgorde kon niet worden opgeslagen."
},
"app.skins.reorder-error.title": {
"message": "Het herschikken van skins is mislukt."
},
"app.skins.section.builders-and-biomes": {
"message": "Builders & Biomes"
},
"app.skins.section.chase-the-skies": {
"message": "Chase the Skies"
},
"app.skins.section.default-skins": {
"message": "Standaardskins"
},
"app.skins.section.minecon-earth-2017": {
"message": "MINECON Earth 2017"
},
"app.skins.section.modrinth": {
"message": "Modrinth"
},
"app.skins.section.modrinth-pride": {
"message": "Modrinth Pride"
},
"app.skins.section.modrinth-pride.tooltip": {
"message": "U heeft deze skins ontvangen omdat u tijdens Pride Month heeft gedoneerd aan een Modrinth Pride-inzamelingsactie."
},
"app.skins.section.mounts-of-mayhem": {
"message": "Mounts of Mayhem"
},
"app.skins.section.saved-skins": {
"message": "Opgeslagen skins"
},
"app.skins.section.striding-hero": {
"message": "Striding Hero"
},
"app.skins.section.the-copper-age": {
"message": "The Copper Age"
},
"app.skins.section.the-garden-awakens": {
"message": "The Garden Awakens"
},
"app.skins.section.tiny-takeover": {
"message": "Tiny Takeover"
},
"app.skins.sign-in.button": {
"message": "Inloggen"
},
"app.skins.sign-in.description": {
"message": "Log in op je Minecraft-account om de skinbeheerfuncties van de Modrinth-app te gebruiken."
},
"app.skins.sign-in.rinthbot-alt": {
"message": "Excited Modrinth Bot"
},
"app.skins.sign-in.title": {
"message": "Log in"
},
"app.skins.title": {
"message": "Skinkiezer"
},
"app.update-popup.body.download-complete": {
"message": "Modrinth App v{version} is klaar met downloaden. Herlaad om nu te updaten, of automatisch wanneer je de Modrinth App afsluit."
},
@@ -513,7 +342,7 @@
"message": "Downloaden voltooid"
},
"app.update-popup.reload": {
"message": "Herladen om te updaten"
"message": "Herlaad"
},
"app.update-popup.title": {
"message": "Update beschikbaar"
@@ -713,30 +542,6 @@
"instance.settings.tabs.general.name": {
"message": "Naam"
},
"instance.settings.tabs.general.update-channel": {
"message": "Updatekanaal"
},
"instance.settings.tabs.general.update-channel.alpha": {
"message": "Alfa"
},
"instance.settings.tabs.general.update-channel.alpha.description": {
"message": "Release-, beta- en alfaversies worden als beschikbare updates weergegeven."
},
"instance.settings.tabs.general.update-channel.beta": {
"message": "Beta"
},
"instance.settings.tabs.general.update-channel.beta.description": {
"message": "Release- en beta-versies worden als beschikbare updates weergegeven.\n"
},
"instance.settings.tabs.general.update-channel.release": {
"message": "Release"
},
"instance.settings.tabs.general.update-channel.release.description": {
"message": "Alleen releaseversies worden als beschikbare updates weergegeven."
},
"instance.settings.tabs.general.update-channel.select": {
"message": "Selecteer updatekanaal"
},
"instance.settings.tabs.hooks": {
"message": "Opstart haakjes"
},
@@ -785,24 +590,6 @@
"instance.settings.tabs.java": {
"message": "Java en geheugen"
},
"instance.settings.tabs.java.custom-environment-variables": {
"message": "Aangepaste omgevingsvariabelen"
},
"instance.settings.tabs.java.custom-java-arguments": {
"message": "Aangepaste Java-argumenten"
},
"instance.settings.tabs.java.custom-java-installation": {
"message": "Aangepaste Java-installatie"
},
"instance.settings.tabs.java.custom-memory-allocation": {
"message": "Aangepaste geheugentoewijzing"
},
"instance.settings.tabs.java.enter-environment-variables": {
"message": "Voer omgevingsvariabelen in…"
},
"instance.settings.tabs.java.enter-java-arguments": {
"message": "Voer Java-argumenten in…"
},
"instance.settings.tabs.java.environment-variables": {
"message": "Environment variabelen"
},
@@ -818,9 +605,6 @@
"instance.settings.tabs.java.java-memory": {
"message": "Voorgeschreven geheugen"
},
"instance.settings.tabs.java.java-path-placeholder": {
"message": "//pad/naar/java"
},
"instance.settings.tabs.window": {
"message": "Venster"
},
+2 -47
View File
@@ -2,9 +2,6 @@
"app.action-bar.downloading-java": {
"message": "Pobieranie Java {version}"
},
"app.action-bar.downloading-update": {
"message": "Pobieranie aktualizacji"
},
"app.action-bar.downloads": {
"message": "Pobrania"
},
@@ -23,18 +20,12 @@
"app.action-bar.primary-instance": {
"message": "Główna instancja"
},
"app.action-bar.reload-to-update": {
"message": "Załaduj ponownie, by zaktualizować"
},
"app.action-bar.show-more-running-instances": {
"message": "Pokaż więcej włączonych instancji"
},
"app.action-bar.stop-instance": {
"message": "Zatrzymaj instancję"
},
"app.action-bar.update": {
"message": "Zaktualizuj"
},
"app.action-bar.view-active-downloads": {
"message": "Pokaż aktualnie pobierane"
},
@@ -279,7 +270,7 @@
"message": "ten serwer"
},
"app.modal.install-to-play.content-required": {
"message": "Wymagana zawartość"
"message": "Wymagane treści"
},
"app.modal.install-to-play.header": {
"message": "Zainstaluj, aby grać"
@@ -287,9 +278,6 @@
"app.modal.install-to-play.install-button": {
"message": "Zainstaluj"
},
"app.modal.install-to-play.mod-count": {
"message": "{count, plural, one {# mod} few {# mody} other {# modów}}"
},
"app.modal.install-to-play.required-modpack": {
"message": "Wymagana paczka modów"
},
@@ -434,12 +422,6 @@
"app.skins.rate-limit.title": {
"message": "Zwolnij!"
},
"app.skins.reorder-error.text": {
"message": "Nie udało się zapisać kolejności skórek."
},
"app.skins.reorder-error.title": {
"message": "Nie udało się zmienić kolejności skórek"
},
"app.skins.section.builders-and-biomes": {
"message": "Builders & Biomes"
},
@@ -513,7 +495,7 @@
"message": "Pobieranie ukończone"
},
"app.update-popup.reload": {
"message": "Załaduj ponownie, by zaktualizować"
"message": "Załaduj ponownie"
},
"app.update-popup.title": {
"message": "Dostępna aktualizacja"
@@ -545,9 +527,6 @@
"friends.action.add-friend": {
"message": "Dodaj znajomego"
},
"friends.action.view-friend-requests": {
"message": "{count} {count, plural, one {zaproszenie} few {zaproszenia} other {zaproszeń}} do znajomych"
},
"friends.add-friend.submit": {
"message": "Wyślij zaproszenie"
},
@@ -713,30 +692,6 @@
"instance.settings.tabs.general.name": {
"message": "Nazwa"
},
"instance.settings.tabs.general.update-channel": {
"message": "Kanał aktualizacji"
},
"instance.settings.tabs.general.update-channel.alpha": {
"message": "Alfa"
},
"instance.settings.tabs.general.update-channel.alpha.description": {
"message": "Wersje stabilne, beta i alfa będą pokazane jako dostępne aktualizacje."
},
"instance.settings.tabs.general.update-channel.beta": {
"message": "Beta"
},
"instance.settings.tabs.general.update-channel.beta.description": {
"message": "Tylko wersje stabilne i wersje beta będą pokazane jako dostępne aktualizacje."
},
"instance.settings.tabs.general.update-channel.release": {
"message": "Stabilny"
},
"instance.settings.tabs.general.update-channel.release.description": {
"message": "Tylko wersje stabilne będą pokazane jako dostępne aktualizacje."
},
"instance.settings.tabs.general.update-channel.select": {
"message": "Wybierz kanał aktualizacji"
},
"instance.settings.tabs.hooks": {
"message": "Haczyki startowe"
},
+35 -77
View File
@@ -2,9 +2,6 @@
"app.action-bar.downloading-java": {
"message": "Baixando Java {version}"
},
"app.action-bar.downloading-update": {
"message": "Baixando atualização"
},
"app.action-bar.downloads": {
"message": "Downloads"
},
@@ -23,18 +20,12 @@
"app.action-bar.primary-instance": {
"message": "Instância principal"
},
"app.action-bar.reload-to-update": {
"message": "Recarregar para atualizar"
},
"app.action-bar.show-more-running-instances": {
"message": "Exibir mais instâncias em execução"
},
"app.action-bar.stop-instance": {
"message": "Parar instância"
},
"app.action-bar.update": {
"message": "Atualizar"
},
"app.action-bar.view-active-downloads": {
"message": "Ver downloads ativos"
},
@@ -51,7 +42,7 @@
"message": "Renderização avançada"
},
"app.appearance-settings.color-theme.description": {
"message": "Escolha seu tema de cores preferido para o Modrinth App."
"message": "Selecione seu tema de cores preferido para o Modrinth App."
},
"app.appearance-settings.color-theme.title": {
"message": "Tema de cores"
@@ -108,7 +99,7 @@
"message": "Alternar barra lateral"
},
"app.appearance-settings.unknown-pack-warning.description": {
"message": "Se você tentar instalar um arquivo Modrinth Pack (.mrpack) não hospedado no Modrinth, garantiremos que você entenda os riscos antes de instalar."
"message": "Se você tentar instalar um arquivo Modrinth Pack (.mrpack) não hospedado no Modrinth, garantiremos que você entenda os riscos antes de instalá-lo."
},
"app.appearance-settings.unknown-pack-warning.title": {
"message": "Avise-me antes de instalar pacotes de mods desconhecidos"
@@ -290,9 +281,6 @@
"app.modal.install-to-play.install-button": {
"message": "Instalar"
},
"app.modal.install-to-play.mod-count": {
"message": "{count, plural, =0 {Nenhum mod} one {# mod} other {# mods}}"
},
"app.modal.install-to-play.required-modpack": {
"message": "Pacote de mods necessário"
},
@@ -437,12 +425,6 @@
"app.skins.rate-limit.title": {
"message": "Mais lento!"
},
"app.skins.reorder-error.text": {
"message": "Não foi possível salvar a ordem de skins."
},
"app.skins.reorder-error.title": {
"message": "Falhou ao reordenar skins"
},
"app.skins.section.builders-and-biomes": {
"message": "Construtores & Biomas"
},
@@ -474,7 +456,7 @@
"message": "Striding Hero"
},
"app.skins.section.the-copper-age": {
"message": "The Copper Age"
"message": "A Era do Cobre"
},
"app.skins.section.the-garden-awakens": {
"message": "The Garden Awakens"
@@ -486,7 +468,7 @@
"message": "Iniciar sessão"
},
"app.skins.sign-in.description": {
"message": "Inicie sessão na sua conta do Minecraft para usar os recursos de gerenciamento de skins no Modrinth App."
"message": "Faça login na sua conta do Minecraft para usar os recursos de gerenciamento de skins do Modrinth app."
},
"app.skins.sign-in.rinthbot-alt": {
"message": "Modrinth Bot animado"
@@ -498,10 +480,10 @@
"message": "Seletor de skins"
},
"app.update-popup.body.download-complete": {
"message": "O Modrinth App v{version} foi instalado. Recarregue para atualizar agora ou ela será feita automaticamente ao fechar o Modrinth App."
"message": "O Modrinth App v{version} foi baixado. Recarregue para atualizar agora ou a atualização será aplicada automaticamente ao fechar o Modrinth App."
},
"app.update-popup.body.linux": {
"message": "O Modrinth App v{version} está disponível. Use o gerenciador de pacotes para atualizar para obter novos recursos e correções!"
"message": "O Modrinth App v{version} está disponível. Use seu gerenciador de pacotes para atualizar e obter os recursos e correções mais recentes!"
},
"app.update-popup.body.metered": {
"message": "O Modrinth App v{version} já está disponível! Já que sua rede é limitada, não a instalamos automaticamente."
@@ -516,7 +498,7 @@
"message": "Instalação concluída"
},
"app.update-popup.reload": {
"message": "Recarregar para atualizar"
"message": "Recarregar"
},
"app.update-popup.title": {
"message": "Atualização disponível"
@@ -570,13 +552,13 @@
"message": "<link>Adicione amigos</link> para ver o que eles estão jogando!"
},
"friends.friend.cancel-request": {
"message": "Cancelar pedido"
"message": "Cancelar solicitação"
},
"friends.friend.remove-friend": {
"message": "Remover amigo"
},
"friends.friend.request-sent": {
"message": "Pedido de amizade enviado"
"message": "Solicitação de amizade enviada"
},
"friends.friend.view-profile": {
"message": "Ver perfil"
@@ -630,7 +612,7 @@
"message": "Editar servidor"
},
"instance.edit-world.hide-from-home": {
"message": "Ocultar da página inicial"
"message": "Esconder da página inicial"
},
"instance.edit-world.name": {
"message": "Nome"
@@ -672,7 +654,7 @@
"message": "Excluir instância"
},
"instance.settings.tabs.general.delete.description": {
"message": "Exclui permanentemente uma instância do seu dispositivo, incluindo seus mundos, configs e todo o conteúdo instalado. Tome cuidado, assim que excluído, a instância não pode ser recuperada."
"message": "Exclui permanentemente uma instância do seu dispositivo, incluindo seus mundos, configurações e todo o conteúdo instalado. Tome cuidado, pois, uma vez excluída, a instância não pode ser recuperada."
},
"instance.settings.tabs.general.deleting.button": {
"message": "Excluindo..."
@@ -687,7 +669,7 @@
"message": "Duplicar instância"
},
"instance.settings.tabs.general.duplicate-instance.description": {
"message": "Cria uma cópia desta instância, incluindo mundos, configs, mods, etc."
"message": "Cria uma cópia desta instância, incluindo mundos, configurações, mods, etc."
},
"instance.settings.tabs.general.edit-icon": {
"message": "Editar ícone"
@@ -705,7 +687,7 @@
"message": "Grupos"
},
"instance.settings.tabs.general.library-groups.create": {
"message": "Criar grupo novo"
"message": "Criar novo grupo"
},
"instance.settings.tabs.general.library-groups.description": {
"message": "Os grupos da biblioteca permitem que você organize suas instâncias em diferentes seções."
@@ -716,30 +698,6 @@
"instance.settings.tabs.general.name": {
"message": "Nome"
},
"instance.settings.tabs.general.update-channel": {
"message": "Canal de atualização"
},
"instance.settings.tabs.general.update-channel.alpha": {
"message": "Alpha"
},
"instance.settings.tabs.general.update-channel.alpha.description": {
"message": "As versões release, beta e alpha aparecerão como atualizações disponíveis."
},
"instance.settings.tabs.general.update-channel.beta": {
"message": "Beta"
},
"instance.settings.tabs.general.update-channel.beta.description": {
"message": "As versões release e beta aparecerão como atualizações disponíveis."
},
"instance.settings.tabs.general.update-channel.release": {
"message": "Release"
},
"instance.settings.tabs.general.update-channel.release.description": {
"message": "Apenas versões release aparecerão como atualizações disponíveis."
},
"instance.settings.tabs.general.update-channel.select": {
"message": "Selecionar canal de atualização"
},
"instance.settings.tabs.hooks": {
"message": "Ações de inicialização"
},
@@ -747,7 +705,7 @@
"message": "Ações de inicialização personalizadas"
},
"instance.settings.tabs.hooks.description": {
"message": "As ações permitem usuários experientes que executem certos comandos do sistema antes ou após iniciar o jogo."
"message": "Essas ações permitem que usuários mais experientes executem comandos do sistema antes e depois de inicializar o jogo."
},
"instance.settings.tabs.hooks.post-exit": {
"message": "Ao sair"
@@ -756,16 +714,16 @@
"message": "Executado após o jogo fechar."
},
"instance.settings.tabs.hooks.post-exit.enter": {
"message": "Insira o comando ao sair..."
"message": "Insira o comando a ser executado após o jogo fechar..."
},
"instance.settings.tabs.hooks.pre-launch": {
"message": "Ao iniciar"
"message": "Pré-inicialização"
},
"instance.settings.tabs.hooks.pre-launch.description": {
"message": "Executado antes que a instância seja inicializada."
},
"instance.settings.tabs.hooks.pre-launch.enter": {
"message": "Insira o comando ao iniciar..."
"message": "Insira o comando a ser executado antes da inicialização..."
},
"instance.settings.tabs.hooks.title": {
"message": "Ações de inicialização do jogo"
@@ -777,13 +735,13 @@
"message": "Comando auxiliar para iniciar o Minecraft."
},
"instance.settings.tabs.hooks.wrapper.enter": {
"message": "Insira um comando auxiliar..."
"message": "Insira um comando..."
},
"instance.settings.tabs.installation": {
"message": "Instalação"
},
"instance.settings.tabs.installation.loader-version": {
"message": "Versão {loader}"
"message": "Versão do {loader}"
},
"instance.settings.tabs.java": {
"message": "Java e memória"
@@ -828,13 +786,13 @@
"message": "Janela"
},
"instance.settings.tabs.window.custom-window-settings": {
"message": "Opções de janela personalizada"
"message": "Configurações de janela personalizada"
},
"instance.settings.tabs.window.fullscreen": {
"message": "Tela cheia"
},
"instance.settings.tabs.window.fullscreen.description": {
"message": "Faz o jogo iniciar em tela cheia (usando options.txt)."
"message": "Faz com que o jogo inicie em tela cheia (usando options.txt)."
},
"instance.settings.tabs.window.height": {
"message": "Altura"
@@ -864,7 +822,7 @@
"message": "Copiar endereço"
},
"instance.worlds.dont_show_on_home": {
"message": "Não exibir na página inicial"
"message": "Não mostrar no início"
},
"instance.worlds.game_already_open": {
"message": "A instância já está aberta"
@@ -876,19 +834,19 @@
"message": "Servidor incompatível"
},
"instance.worlds.linked_server": {
"message": "Gerenciado pelo projeto do servidor"
"message": "Gerenciado pelo projeto de servidor"
},
"instance.worlds.no_contact": {
"message": "Não foi possível conectar-se ao servidor"
},
"instance.worlds.no_server_quick_play": {
"message": "Só é possível entrar diretamente em servidores a partir do Minecraft Alpha 1.0.5"
"message": "Você só pode entrar diretamente em servidores a partir do Minecraft 1.0.5"
},
"instance.worlds.no_singleplayer_quick_play": {
"message": "Só é possível entrar em mundos um jogador diretamente a partir do Minecraft 1.20"
"message": "Você só pode entrar diretamente em mundos de um jogador a partir do Minecraft 1.20+"
},
"instance.worlds.play_instance": {
"message": "Iniciar instância"
"message": "Jogar na instância"
},
"instance.worlds.view_instance": {
"message": "Ver instância"
@@ -900,7 +858,7 @@
"message": "Adicionar conta"
},
"minecraft-account.label": {
"message": "Conta de Minecraft"
"message": "Conta do minecraft"
},
"minecraft-account.not-signed-in": {
"message": "Não conectado"
@@ -912,7 +870,7 @@
"message": "Selecionar conta"
},
"minecraft-account.sign-in": {
"message": "Inicie sessão no Minecraft"
"message": "Faça login no Minecraft"
},
"search.filter.locked.instance": {
"message": "Fornecido pela instância"
@@ -921,7 +879,7 @@
"message": "A versão do jogo é fornecida pela instância"
},
"search.filter.locked.instance-loader.title": {
"message": "O loader é fornecido pela instância"
"message": "O carregador é fornecido pela instância"
},
"search.filter.locked.instance.sync": {
"message": "Sincronizar com a instância"
@@ -930,19 +888,19 @@
"message": "Fornecido pelo servidor"
},
"search.filter.locked.server-environment.title": {
"message": "Somente mods do cliente podem ser adicionados à instância do servidor"
"message": "Apenas mods do lado do cliente podem ser adicionados à instância do servidor"
},
"search.filter.locked.server-game-version.title": {
"message": "A versão do jogo é fornecida pelo servidor"
},
"search.filter.locked.server-loader.title": {
"message": "O loader é fornecido pelo servidor"
"message": "O carregador é fornecido pelo servidor"
},
"unknown-pack-warning-modal.body": {
"message": "Um arquivo só é revisado se for enviado no Modrinth, independente do formato do arquivo (incluindo .mrpack)."
"message": "Um arquivo só é revisado se for enviado para o Modrinth, não importa o seu formato de arquivo (incluindo .mrpack)."
},
"unknown-pack-warning-modal.dont-show-again": {
"message": "Não exibir aviso novamente"
"message": "Não mostrar este aviso novamente"
},
"unknown-pack-warning-modal.header": {
"message": "Confirmar instalação"
@@ -951,10 +909,10 @@
"message": "Instalar mesmo assim"
},
"unknown-pack-warning-modal.malware-statement": {
"message": "O malware é distribuído frequentemente através de arquivos de pacote de mods compartilhados em plataformas como Discord."
"message": "Malware é frequentemente distribuído através de arquivos de pacotes de mods compartilhados em plataformas como o Discord."
},
"unknown-pack-warning-modal.warning.body": {
"message": "Não encontramos este arquivo no Modrinth. Nós recomendamos fortemente instalar arquivos apenas de fontes confiáveis."
"message": "Não foi possível encontrar este arquivo no Modrinth. Recomendamos fortemente que você instale apenas arquivos de fontes confiáveis."
},
"unknown-pack-warning-modal.warning.title": {
"message": "Aviso de arquivo desconhecido"
+3 -30
View File
@@ -2,9 +2,6 @@
"app.action-bar.downloading-java": {
"message": "A descarregar a versão do java {version}"
},
"app.action-bar.downloading-update": {
"message": "A transferir atualização"
},
"app.action-bar.downloads": {
"message": "Downloads"
},
@@ -23,18 +20,12 @@
"app.action-bar.primary-instance": {
"message": "Instância principal"
},
"app.action-bar.reload-to-update": {
"message": "Recarregar para atualizar"
},
"app.action-bar.show-more-running-instances": {
"message": "Mostrar mais instâncias a correr"
},
"app.action-bar.stop-instance": {
"message": "Para instância"
},
"app.action-bar.update": {
"message": "Atualizar"
},
"app.action-bar.view-active-downloads": {
"message": "Ver instâncias ativas"
},
@@ -44,9 +35,6 @@
"app.action-bar.view-logs": {
"message": "Ver logs"
},
"app.appearance-settings.advanced-rendering.description": {
"message": "Ativa a renderização avançada, como efeitos de desfoque, que podem causar problemas de desempenho sem o uso de renderização acelerada por hardware."
},
"app.appearance-settings.advanced-rendering.title": {
"message": "Renderização avançada"
},
@@ -68,12 +56,6 @@
"app.appearance-settings.default-landing-page.title": {
"message": "Página de carregamento principal"
},
"app.appearance-settings.minimize-launcher.title": {
"message": "Minimizar launcher"
},
"app.appearance-settings.select-option": {
"message": "Seleciona uma opção"
},
"app.auth-servers.unreachable.body": {
"message": "Os servidores de autenticação do Minecraft poderão estar em baixo de momento. Verifica a tua ligação à internet e tenta novamente mais tarde."
},
@@ -98,9 +80,6 @@
"app.browse.discover-servers": {
"message": "Descobrir servidores"
},
"app.browse.project-type.modpacks": {
"message": "Modpacks"
},
"app.export-modal.description-placeholder": {
"message": "Insere a descrição do modpack..."
},
@@ -260,15 +239,6 @@
"app.settings.tabs.resource-management": {
"message": "Gestão de recursos"
},
"app.skins.delete-modal.title": {
"message": "Tens a certeza que queres apagar esta skin?"
},
"app.skins.dropped-file-error.text": {
"message": "Não foi possível ler o ficheiro largado."
},
"app.skins.section.modrinth": {
"message": "Modrinth"
},
"app.update-popup.body.download-complete": {
"message": "Modrinth App v{version} acabou de ser transferida. Recarrega para atualizar agora, ou automaticamente quando fechares a Modrinth App."
},
@@ -287,6 +257,9 @@
"app.update-popup.download-complete": {
"message": "Transferência concluída"
},
"app.update-popup.reload": {
"message": "Recarregar"
},
"app.update-popup.title": {
"message": "Atualização disponível"
},
@@ -83,6 +83,9 @@
"app.update-popup.download-complete": {
"message": "Descărcare finalizată"
},
"app.update-popup.reload": {
"message": "Reîncărcați"
},
"app.update-popup.title": {
"message": "Actualizare disponibilă"
},
@@ -2,9 +2,6 @@
"app.action-bar.downloading-java": {
"message": "Скачивание Java {version}"
},
"app.action-bar.downloading-update": {
"message": "Скачивание"
},
"app.action-bar.downloads": {
"message": "Загрузки"
},
@@ -23,18 +20,12 @@
"app.action-bar.primary-instance": {
"message": "Основная сборка"
},
"app.action-bar.reload-to-update": {
"message": "Перезапустить"
},
"app.action-bar.show-more-running-instances": {
"message": "Показать другие активные сборки"
},
"app.action-bar.stop-instance": {
"message": "Остановить сборку"
},
"app.action-bar.update": {
"message": "Обновить"
},
"app.action-bar.view-active-downloads": {
"message": "Посмотреть текущие загрузки"
},
@@ -275,9 +266,6 @@
"app.modal.install-to-play.install-button": {
"message": "Установить"
},
"app.modal.install-to-play.mod-count": {
"message": "{count, plural, one {# мод} few {# мода} other {# модов}}"
},
"app.modal.install-to-play.required-modpack": {
"message": "Необходимая сборка"
},
@@ -422,12 +410,6 @@
"app.skins.rate-limit.title": {
"message": "Не так быстро!"
},
"app.skins.reorder-error.text": {
"message": "Ваш порядок скинов не удалось сохранить."
},
"app.skins.reorder-error.title": {
"message": "Не удалось изменить порядок скинов"
},
"app.skins.section.builders-and-biomes": {
"message": "Builders & Biomes"
},
@@ -533,9 +515,6 @@
"friends.action.add-friend": {
"message": "Добавить в друзья"
},
"friends.action.view-friend-requests": {
"message": "{count} {count, plural, one {запрос} few {запроса} other {запросов}} дружбы"
},
"friends.add-friend.submit": {
"message": "Отправить запрос дружбы"
},
@@ -698,30 +677,6 @@
"instance.settings.tabs.general.name": {
"message": "Название"
},
"instance.settings.tabs.general.update-channel": {
"message": "Канал обновления"
},
"instance.settings.tabs.general.update-channel.alpha": {
"message": "Альфа"
},
"instance.settings.tabs.general.update-channel.alpha.description": {
"message": "Релизные, бета и альфа версии будут отображаться как доступные обновления."
},
"instance.settings.tabs.general.update-channel.beta": {
"message": "Бета"
},
"instance.settings.tabs.general.update-channel.beta.description": {
"message": "Релизные и бета версии будут отображаться как доступные обновления."
},
"instance.settings.tabs.general.update-channel.release": {
"message": "Релиз"
},
"instance.settings.tabs.general.update-channel.release.description": {
"message": "Только релизные версии будут отображаться как доступные обновления."
},
"instance.settings.tabs.general.update-channel.select": {
"message": "Выберите канал обновления"
},
"instance.settings.tabs.hooks": {
"message": "Команды запуска"
},
+7 -190
View File
@@ -2,20 +2,17 @@
"app.action-bar.downloading-java": {
"message": "Laddar ner Java {version}"
},
"app.action-bar.downloading-update": {
"message": "Laddar ner uppdatering"
},
"app.action-bar.downloads": {
"message": "Nedladdningar"
},
"app.action-bar.hide-more-running-instances": {
"message": "Göm fler aktiva instanser"
"message": "Göm fler pågående instanser"
},
"app.action-bar.make-primary-instance": {
"message": "Gör till primärinstans"
},
"app.action-bar.no-instances-running": {
"message": "Ingen aktiva instans"
"message": "Ingen pågående instans"
},
"app.action-bar.offline": {
"message": "Offline"
@@ -23,18 +20,12 @@
"app.action-bar.primary-instance": {
"message": "Primärinstans"
},
"app.action-bar.reload-to-update": {
"message": "Ladda om för att uppdatera"
},
"app.action-bar.show-more-running-instances": {
"message": "Visa fler aktiva instanser"
"message": "Visa fler pågående instanser"
},
"app.action-bar.stop-instance": {
"message": "Avsluta instans"
},
"app.action-bar.update": {
"message": "Uppdatera"
},
"app.action-bar.view-active-downloads": {
"message": "Visa aktiva nedladdningar"
},
@@ -74,9 +65,6 @@
"app.appearance-settings.hide-nametag.title": {
"message": "Dölj namnskylt"
},
"app.appearance-settings.jump-back-into-worlds.title": {
"message": "Hoppa tillbaka i världar"
},
"app.appearance-settings.minimize-launcher.title": {
"message": "Minimera programstartare"
},
@@ -89,33 +77,18 @@
"app.appearance-settings.select-option": {
"message": "Välj ett alternativ"
},
"app.appearance-settings.show-play-time.description": {
"message": "Visar hur mycket tid du spelat på en instans."
},
"app.appearance-settings.show-play-time.title": {
"message": "Visa speltid"
},
"app.appearance-settings.toggle-sidebar.description": {
"message": "Låter dig växla sidofältet."
},
"app.appearance-settings.toggle-sidebar.title": {
"message": "Växla sidofält"
},
"app.appearance-settings.unknown-pack-warning.description": {
"message": "Om du försöker installera en Modrinth Pack fil (.mrpack) som inte kommer från Modrinth ser vi till att du känner till riskerna innan du installerar den."
},
"app.appearance-settings.unknown-pack-warning.title": {
"message": "Varna mig innan jag installerar okända modpaket"
},
"app.auth-servers.unreachable.body": {
"message": "Minecrafts autentiseringsservrar kan vara nere just nu. Kontrollera din internetanslutning och försök igen senare."
},
"app.auth-servers.unreachable.header": {
"message": "Kan ej nå autentiseringsservrarna"
},
"app.browse.add-servers-to-instance": {
"message": "Lägger till server till instans"
},
"app.browse.add-to-an-instance": {
"message": "Lägg till i en instans"
},
@@ -149,9 +122,6 @@
"app.browse.server.installing": {
"message": "Installerar"
},
"app.content-install.no-compatible-versions": {
"message": "Ingen tillgänglig version matchar {compatibilityLabel}. Välj en version att installera ändå. Beroenden kommer inte installeras automatiskt."
},
"app.creation-modal.installing-modpack.description": {
"message": "{fileName}"
},
@@ -176,9 +146,6 @@
"app.export-modal.modpack-name-placeholder": {
"message": "Modpaketets namn"
},
"app.export-modal.select-files-label": {
"message": "Konfigurera vilka filer som inkluderas i denna export"
},
"app.export-modal.version-number-label": {
"message": "Versionsnummer"
},
@@ -186,7 +153,7 @@
"message": "1.0.0"
},
"app.instance.confirm-delete.admonition-body": {
"message": "All data från din instans kommer permanent raderas, däribland dina världar, konfigurationer samt allt installerat innehåll."
"message": "All data från din instans kommer att raderas permanent, inklusive dina världar, konfigurationer och allt installerat innehåll."
},
"app.instance.confirm-delete.admonition-header": {
"message": "Detta kan inte ogöras"
@@ -308,12 +275,6 @@
"app.modal.update-to-play.update-required-description": {
"message": "En uppdatering krävs för att spela {name}. Vänligen uppdatera till senaste version för att starta spelet."
},
"app.project.install-button.already-installed": {
"message": "Detta projekt är redan installerat"
},
"app.project.install-context.install-content-to-instance": {
"message": "Installera innehåll till instans"
},
"app.settings.developer-mode-enabled": {
"message": "Utvecklarläge aktiverat."
},
@@ -341,27 +302,9 @@
"app.skins.add-button": {
"message": "Lägg till utseende"
},
"app.skins.add-button.drag-and-drop": {
"message": "Dra och släpp"
},
"app.skins.apply-button": {
"message": "Tillämpa"
},
"app.skins.delete-button": {
"message": "Ta bort utseende"
},
"app.skins.delete-modal.description": {
"message": "Du kommer permanent radera det valda utseendet. Detta kan inte ångras."
},
"app.skins.delete-modal.title": {
"message": "Är du säker på att du vill radera utseendet?"
},
"app.skins.dropped-file-error.text": {
"message": "Misslyckades att läsa den släppta filen."
},
"app.skins.dropped-file-error.title": {
"message": "Fel med behandlingen av filen"
},
"app.skins.edit-button": {
"message": "Redigera utseende"
},
@@ -410,69 +353,6 @@
"app.skins.modal.texture-section": {
"message": "Textur"
},
"app.skins.modal.upload-skin-first-tooltip": {
"message": "Ladda upp utseende först!"
},
"app.skins.preview.edit-button": {
"message": "Redigera utseende"
},
"app.skins.previewing-badge": {
"message": "Förhandsvisa"
},
"app.skins.rate-limit.text": {
"message": "Du ändrar ditt utseende för ofta. Mojangs servrar har temporärt blockerat ytterligare förfrågningar. Vänligen vänta en stund innan du försöker igen."
},
"app.skins.rate-limit.title": {
"message": "Sakta ner!"
},
"app.skins.section.chase-the-skies": {
"message": "Chase the Skies"
},
"app.skins.section.default-skins": {
"message": "Standard utseenden"
},
"app.skins.section.minecon-earth-2017": {
"message": "MINECON Earth 2017"
},
"app.skins.section.modrinth": {
"message": "Modrinth"
},
"app.skins.section.modrinth-pride": {
"message": "Modrinth Pride"
},
"app.skins.section.modrinth-pride.tooltip": {
"message": "Du har fått dessa utseende eftersom du donerat till en Modrinth Pride insamling under pride månaden."
},
"app.skins.section.mounts-of-mayhem": {
"message": "Mounts of Mayhem"
},
"app.skins.section.saved-skins": {
"message": "Sparade utseenden"
},
"app.skins.section.the-copper-age": {
"message": "The Copper Age"
},
"app.skins.section.the-garden-awakens": {
"message": "The Garden Awakens"
},
"app.skins.section.tiny-takeover": {
"message": "Tiny Takeover"
},
"app.skins.sign-in.button": {
"message": "Logga in"
},
"app.skins.sign-in.description": {
"message": "Snälla logga in din Minecraft konto att använda hud hantering förmågor i Modrinth appen."
},
"app.skins.sign-in.rinthbot-alt": {
"message": "Exalterad Modrinth bot"
},
"app.skins.sign-in.title": {
"message": "Vänligen logga in"
},
"app.skins.title": {
"message": "Utseende väljare"
},
"app.update-popup.body.download-complete": {
"message": "Modrinth App v{version} har laddats ner. Ladda om för att uppdatera nu, eller automatiskt när du stänger Modrinth App."
},
@@ -492,7 +372,7 @@
"message": "Nedladdning slutförd"
},
"app.update-popup.reload": {
"message": "Ladda om för att uppdatera"
"message": "Ladda om"
},
"app.update-popup.title": {
"message": "Uppdatering tillgänglig"
@@ -692,30 +572,6 @@
"instance.settings.tabs.general.name": {
"message": "Namn"
},
"instance.settings.tabs.general.update-channel": {
"message": "Uppdateringskanal"
},
"instance.settings.tabs.general.update-channel.alpha": {
"message": "Alfa"
},
"instance.settings.tabs.general.update-channel.alpha.description": {
"message": "Utgåvor, beta och alfa versioner visas som tillgängliga uppdateringar."
},
"instance.settings.tabs.general.update-channel.beta": {
"message": "Beta"
},
"instance.settings.tabs.general.update-channel.beta.description": {
"message": "Utgåvor och beta versioner visas som tillgängliga uppdateringar."
},
"instance.settings.tabs.general.update-channel.release": {
"message": "Utgåva"
},
"instance.settings.tabs.general.update-channel.release.description": {
"message": "Endast utgivna versioner visas som tillgängliga uppdateringar."
},
"instance.settings.tabs.general.update-channel.select": {
"message": "Välj uppdateringskanal"
},
"instance.settings.tabs.hooks": {
"message": "Startkrokar"
},
@@ -764,24 +620,6 @@
"instance.settings.tabs.java": {
"message": "Java och minne"
},
"instance.settings.tabs.java.custom-environment-variables": {
"message": "Anpassade miljövariabler"
},
"instance.settings.tabs.java.custom-java-arguments": {
"message": "Anpassade Java-argument"
},
"instance.settings.tabs.java.custom-java-installation": {
"message": "Anpassad Java-installation"
},
"instance.settings.tabs.java.custom-memory-allocation": {
"message": "Anpassad minnestilldelning"
},
"instance.settings.tabs.java.enter-environment-variables": {
"message": "Skriv in miljövariabler..."
},
"instance.settings.tabs.java.enter-java-arguments": {
"message": "Skriv in Java-argument..."
},
"instance.settings.tabs.java.environment-variables": {
"message": "Miljövariabler"
},
@@ -797,9 +635,6 @@
"instance.settings.tabs.java.java-memory": {
"message": "Tilldelat minne"
},
"instance.settings.tabs.java.java-path-placeholder": {
"message": "/sökväg/till/java"
},
"instance.settings.tabs.window": {
"message": "Fönster"
},
@@ -816,7 +651,7 @@
"message": "Höjd"
},
"instance.settings.tabs.window.height.description": {
"message": "Höjden på spelfönstret vid uppstart."
"message": "Höjden på spel-fönstret vid uppstart."
},
"instance.settings.tabs.window.height.enter": {
"message": "Ange höjd..."
@@ -825,7 +660,7 @@
"message": "Bredd"
},
"instance.settings.tabs.window.width.description": {
"message": "Bredden på spelfönstret vid uppstart."
"message": "Bredden på spel-fönstret vid uppstart."
},
"instance.settings.tabs.window.width.enter": {
"message": "Ange bredd..."
@@ -914,25 +749,7 @@
"search.filter.locked.server-loader.title": {
"message": "Loader tillhandahålls av servern"
},
"unknown-pack-warning-modal.body": {
"message": "En fil granskas bara om den laddas upp till Modrinth, oavsett dess filformat (däribland .mrpack)."
},
"unknown-pack-warning-modal.dont-show-again": {
"message": "Visa inte denna varning igen"
},
"unknown-pack-warning-modal.header": {
"message": "Bekräfta installation"
},
"unknown-pack-warning-modal.install-anyway": {
"message": "Installera ändå"
},
"unknown-pack-warning-modal.malware-statement": {
"message": "Skadeprogram distribueras ofta via modpaketfiler genom att dela dem på plattformar som Discord."
},
"unknown-pack-warning-modal.warning.body": {
"message": "Vi kunde inte hitta filen på Modrinth. Vi rekommenderar starkt att endast installera filer från källor du litar på."
},
"unknown-pack-warning-modal.warning.title": {
"message": "Okänd filvarning"
}
}
+19 -205
View File
@@ -1,45 +1,36 @@
{
"app.action-bar.downloading-java": {
"message": "กำลังดาวน์โหลด Java {version}"
},
"app.action-bar.downloading-update": {
"message": "กำลังดาวน์โหลดอัปเดต"
"message": "ดาวน์โหลดเวอร์ชัน Java {version}"
},
"app.action-bar.downloads": {
"message": "ดาวน์โหลด"
},
"app.action-bar.hide-more-running-instances": {
"message": "ซ่อนอินสแตนซ์ที่ทำงานอยู่เพิ่มเติม"
"message": "ซ่อนการดำเนินการโปรแกรมเบื้องหลัง"
},
"app.action-bar.make-primary-instance": {
"message": "กำหนดอินสแตนซ์หลัก"
"message": "กำหนดโปรแกรมหลัก"
},
"app.action-bar.no-instances-running": {
"message": "ไม่มีอินสแตนซ์ที่กำลังทำงาน"
"message": "ไม่มีโปรแกรมใดดำเนินการ"
},
"app.action-bar.offline": {
"message": "ออฟไลน์"
},
"app.action-bar.primary-instance": {
"message": "อินสแตนซ์หลัก"
},
"app.action-bar.reload-to-update": {
"message": "รีโหลดเพื่ออัปเดต"
"message": "โปรแกรมหลัก"
},
"app.action-bar.show-more-running-instances": {
"message": "แสดงอินสแตนซ์ที่ทำงานอยู่เพิ่มเติม"
"message": "แสดงการดำเนินการโปรแกรมเบื้องหลังเพิ่มเติม"
},
"app.action-bar.stop-instance": {
"message": "หยุดอินสแตนซ์"
},
"app.action-bar.update": {
"message": "อัปเดต"
"message": "หยุดการทำงานโปรแกรม"
},
"app.action-bar.view-active-downloads": {
"message": "ดูการดาวน์โหลดที่กำลังดำเนินการอยู่"
"message": "ดูยอดการดาวน์โหลดที่ยังใช้งาน"
},
"app.action-bar.view-instance": {
"message": "ดูอินสแตนซ์"
"message": "ดูโปรแกรม"
},
"app.action-bar.view-logs": {
"message": "ดู Log"
@@ -51,7 +42,7 @@
"message": "การเรนเดอร์ขั้นสูง"
},
"app.appearance-settings.color-theme.description": {
"message": "เลือกธีมสีที่คุณต้องการสำหรับ Modrinth App"
"message": "เลือกธีมสีที่คุณชื่นชอบสำหรับ Modrinth บนอุปกรณ์นี้"
},
"app.appearance-settings.color-theme.title": {
"message": "ธีมสี"
@@ -63,22 +54,22 @@
"message": "หน้าหลัก"
},
"app.appearance-settings.default-landing-page.library": {
"message": "ไลบรารี"
"message": "รายการ"
},
"app.appearance-settings.default-landing-page.title": {
"message": "หน้าเริ่มต้น"
},
"app.appearance-settings.hide-nametag.description": {
"message": "ซ่อนป้ายชื่อบนศีรษะของผู้เล่นในหน้าแสดงสกิน"
"message": "ปิดป้ายชื่อบนศีรษะของผู้เล่นในหน้าแสดงสกิน"
},
"app.appearance-settings.hide-nametag.title": {
"message": "ซ่อนป้ายชื่อ"
},
"app.appearance-settings.jump-back-into-worlds.description": {
"message": "แสดงโลกที่เล่นล่าสุดในส่วน \"เล่นต่อ\" นหน้าหลัก"
"message": "การแสดงผลโลกล่าสุดที่ผู้เล่นล่ในส่วน \"กระโดดกลับเข้าไป\" นหน้าหลัก"
},
"app.appearance-settings.jump-back-into-worlds.title": {
"message": "เล่นต่อ"
"message": "กระโดดกลับเข้าไปในโลก"
},
"app.appearance-settings.minimize-launcher.description": {
"message": "ย่อลันเชอร์เมื่อกระบวนการการรัน Minecraft เริ่มต้นแล้ว"
@@ -87,19 +78,13 @@
"message": "ย่อลันเชอร์"
},
"app.appearance-settings.native-decorations.description": {
"message": "ใช้กรอบหน้าต่างระบบ (ต้องรีสตาร์ทแอป)"
"message": "ใช้ระบบกรอบหน้าต่าง (ต้องการการรีสตาร์ทโปรแกรมใหม่)"
},
"app.appearance-settings.native-decorations.title": {
"message": "ของตกแต่งดั้งเดิม"
},
"app.appearance-settings.select-option": {
"message": "เลือกตัวเลือก"
},
"app.appearance-settings.show-play-time.description": {
"message": "แสดงเวลาที่คุณใช้เล่นในแต่ละอินสแตนซ์"
},
"app.appearance-settings.show-play-time.title": {
"message": "แสดงระยะเวลาที่เล่น"
"message": "เลือกตัวเลือก"
},
"app.appearance-settings.toggle-sidebar.description": {
"message": "เปิดใช้งานตัวเลือกในการเลือกย่อหรือขยายแถบด้านข้าง"
@@ -119,12 +104,6 @@
"app.auth-servers.unreachable.header": {
"message": "ไม่สามารถเชื่อมต่อถึงเซิร์ฟเวอร์ได้"
},
"app.browse.add-servers-to-instance": {
"message": "เพิ่มเซิร์ฟเวอร์ลงในอินสแตนซ์"
},
"app.browse.add-to-an-instance": {
"message": "เพิ่มลงในอินสแตนซ์"
},
"app.browse.add-to-instance": {
"message": "เพิ่มลงในโปรแกรม"
},
@@ -137,9 +116,6 @@
"app.browse.already-added": {
"message": "เพิ่มแล้ว"
},
"app.browse.back-to-instance": {
"message": "กลับสู่อินสแตนซ์"
},
"app.browse.discover-content": {
"message": "สำรวจเนื้อหา"
},
@@ -152,15 +128,9 @@
"app.browse.project-type.modpacks": {
"message": "แพ็กม็อด"
},
"app.browse.server-instance-content-warning": {
"message": "การเพิ่มเนื้อหาอาจทำให้ใช้งานร่วมกันไม่ได้เมื่อเข้าร่วมเซิร์ฟเวอร์ เนื้อหาที่เพิ่มเข้ามาจะหายไปเมื่อคุณอัปเดตอินสแตนซ์ของเซิร์ฟเวอร์"
},
"app.browse.server.installing": {
"message": "กำลังติดตั้ง"
},
"app.content-install.no-compatible-versions": {
"message": "ไม่มีเวอร์ชันที่เข้ากันได้ {compatibilityLabel} โปรดเลือกเวอร์ชันที่จะติดตั้ง ไฟล์ที่จำเป็นจะไม่ถูกติดตั้งโดยอัตโนมัติ"
},
"app.creation-modal.installing-modpack.description": {
"message": "{fileName}"
},
@@ -290,9 +260,6 @@
"app.modal.install-to-play.install-button": {
"message": "ติดตั้ง"
},
"app.modal.install-to-play.mod-count": {
"message": "{count, plural, other {# ม็อด}}"
},
"app.modal.install-to-play.required-modpack": {
"message": "มอดแพ็กที่จำเป็น"
},
@@ -317,15 +284,6 @@
"app.modal.update-to-play.update-required-description": {
"message": "จำเป็นต้องอัปเดตเพื่อเล่น {name} กรุณาอัปเดตเป็นเวอร์ชันล่าสุดเพื่อเปิดเกม"
},
"app.project.install-button.already-installed": {
"message": "โปรเจคนี้ถูกติดตั้งแล้ว"
},
"app.project.install-context.back-to-browse": {
"message": "กลับไปที่หน้าค้นหา"
},
"app.project.install-context.install-content-to-instance": {
"message": "ติดตั้งเนื้อหาลงสู่อินสแตนซ์"
},
"app.settings.developer-mode-enabled": {
"message": "โหมดนักพัฒนาเปิดอยู่"
},
@@ -350,150 +308,6 @@
"app.settings.tabs.resource-management": {
"message": "การจัดการทรัพยากร"
},
"app.skins.add-button": {
"message": "เพิ่มสกิน"
},
"app.skins.add-button.drag-and-drop": {
"message": "ลากและวาง"
},
"app.skins.apply-button": {
"message": "นำไปใช้"
},
"app.skins.delete-button": {
"message": "ลบสกิน"
},
"app.skins.delete-modal.description": {
"message": "การดำเนินการนี้จะลบสกินที่เลือกอย่างถาวร การกระทำนี้ไม่สามารถย้อนกลับได้"
},
"app.skins.delete-modal.title": {
"message": "คุณแน่ใจหรือว่าต้องการที่จะลบสกินนี้?"
},
"app.skins.dropped-file-error.text": {
"message": "ไม่สามารถอ่านไฟล์ที่ถูกลากและวางได้"
},
"app.skins.dropped-file-error.title": {
"message": "ข้อผิดพลาดในการประมวลผลไฟล์"
},
"app.skins.edit-button": {
"message": "แก้ไขสกิน"
},
"app.skins.modal.add-skin-button": {
"message": "เพิ่มสกิน"
},
"app.skins.modal.add-title": {
"message": "การเพิ่มสกิน"
},
"app.skins.modal.arm-style-section": {
"message": "สไตล์แขน"
},
"app.skins.modal.arm-style-slim": {
"message": "เพรียว"
},
"app.skins.modal.arm-style-wide": {
"message": "กว้าง"
},
"app.skins.modal.cape-fallback-name": {
"message": "ผ้าคลุม"
},
"app.skins.modal.cape-section": {
"message": "ผ้าคลุม"
},
"app.skins.modal.edit-title": {
"message": "แก้ไขสกิน"
},
"app.skins.modal.make-edit-first-tooltip": {
"message": "แก้ไขสกินก่อน!"
},
"app.skins.modal.no-cape-tooltip": {
"message": "ไม่มีผ้าคลุม"
},
"app.skins.modal.none-cape-option": {
"message": "ไม่มี"
},
"app.skins.modal.replace-texture-button": {
"message": "เปลี่ยนเท็กซ์เจอร์"
},
"app.skins.modal.save-skin-button": {
"message": "บันทึกสกิน"
},
"app.skins.modal.saving-tooltip": {
"message": "กำลังบันทึก"
},
"app.skins.modal.texture-section": {
"message": "เท็กซ์เจอร์"
},
"app.skins.modal.upload-skin-first-tooltip": {
"message": "อัพโหลดสกินก่อน!"
},
"app.skins.preview.edit-button": {
"message": "แก้ไขสกิน"
},
"app.skins.previewing-badge": {
"message": "การแสดงตัวอย่าง"
},
"app.skins.rate-limit.text": {
"message": "คุณได้เปลี่ยนสกินบ่อยเกินไป เซิร์ฟเวอร์ของ Mojang ได้บล็อกคำขอเพิ่มเติมชั่วคราว โปรดรอสักครู่ก่อนลองอีกครั้ง"
},
"app.skins.rate-limit.title": {
"message": "ลดความถี่ลง!"
},
"app.skins.reorder-error.text": {
"message": "ไม่สามารถบันทึกลำดับสกินของคุณได้"
},
"app.skins.reorder-error.title": {
"message": "ไม่สามารถจัดเรียงสกินได้"
},
"app.skins.section.builders-and-biomes": {
"message": "Builders & Biomes"
},
"app.skins.section.chase-the-skies": {
"message": "Chase the Skies"
},
"app.skins.section.default-skins": {
"message": "สกินเริ่มต้น"
},
"app.skins.section.minecon-earth-2017": {
"message": "MINECON Earth 2017"
},
"app.skins.section.modrinth": {
"message": "Modrinth"
},
"app.skins.section.modrinth-pride": {
"message": "Modrinth Pride"
},
"app.skins.section.modrinth-pride.tooltip": {
"message": "คุณได้รับสกินเหล่านี้จากการร่วมบริจาคให้กับกิจกรรมระดมทุน Modrinth Pride ในช่วงเดือน Pride Month"
},
"app.skins.section.mounts-of-mayhem": {
"message": "Mounts of Mayhem"
},
"app.skins.section.saved-skins": {
"message": "สกินที่บันทึกไว้"
},
"app.skins.section.striding-hero": {
"message": "Striding Hero"
},
"app.skins.section.the-copper-age": {
"message": "The Copper Age"
},
"app.skins.section.the-garden-awakens": {
"message": "The Garden Awakens"
},
"app.skins.section.tiny-takeover": {
"message": "Tiny Takeover"
},
"app.skins.sign-in.button": {
"message": "ลงชื่อเข้าใช้"
},
"app.skins.sign-in.description": {
"message": "โปรดลงชื่อเข้าใช้บัญชี Minecraft ของคุณเพื่อใช้งานฟีเจอร์จัดการสกินของแอป Modrinth"
},
"app.skins.sign-in.rinthbot-alt": {
"message": "บอท Modrinth ที่กำลังตื่นเต้น"
},
"app.skins.sign-in.title": {
"message": "โปรดลงชื่อเข้าใช้"
},
"app.update-popup.body.download-complete": {
"message": "ดาวน์โหลด Modrinth v{version} สำเร็จแล้ว เปิดโปรแกรมใหม่อีกครั้งเพื่ออัปเดตตอนนี้ หรือจะรออัปเดตอัตโนมัติ ซึ่งจะเกิดขึ้นเมื่อคุณกดปิดโปรแกรม Modrinth"
},
@@ -512,6 +326,9 @@
"app.update-popup.download-complete": {
"message": "การดาวน์โหลดเสร็จสมบูรณ์"
},
"app.update-popup.reload": {
"message": "โหลดใหม่"
},
"app.update-popup.title": {
"message": "มีการอัปเดตใหม่"
},
@@ -542,9 +359,6 @@
"friends.action.add-friend": {
"message": "เพิ่มเพื่อน"
},
"friends.action.view-friend-requests": {
"message": "คำขอเป็นเพื่อน {count} {count, plural, other {คน}}"
},
"friends.add-friend.submit": {
"message": "ส่งคำขอเป็นเพื่อน"
},
+3 -45
View File
@@ -2,9 +2,6 @@
"app.action-bar.downloading-java": {
"message": "Java {version} indiriliyor"
},
"app.action-bar.downloading-update": {
"message": "Güncelleme indiriliyor"
},
"app.action-bar.downloads": {
"message": "İndirmeler"
},
@@ -23,18 +20,12 @@
"app.action-bar.primary-instance": {
"message": "Birincil kurulum"
},
"app.action-bar.reload-to-update": {
"message": "Güncellemek için yeniden yükle"
},
"app.action-bar.show-more-running-instances": {
"message": "Daha fazla çalışan kurulum göster"
},
"app.action-bar.stop-instance": {
"message": "Kurulumu durdur"
},
"app.action-bar.update": {
"message": "Güncelleme"
},
"app.action-bar.view-active-downloads": {
"message": "Şu anda indirilienleri göster"
},
@@ -54,7 +45,7 @@
"message": "Modrinth App için tercih ettiğiniz temayı seçin."
},
"app.appearance-settings.color-theme.title": {
"message": "Tema Rengi"
"message": "Tema"
},
"app.appearance-settings.default-landing-page.description": {
"message": "Launcher açıldığında varsayılan olarak açılacak sayfayı seçin."
@@ -144,7 +135,7 @@
"message": "İçerik keşfet"
},
"app.browse.discover-servers": {
"message": "Sunucuları keşfet"
"message": "Sunucu keşfet"
},
"app.browse.hide-added-servers": {
"message": "Zaten eklenmiş sunucuları gizle"
@@ -437,12 +428,6 @@
"app.skins.rate-limit.title": {
"message": "Yavaşla!"
},
"app.skins.reorder-error.text": {
"message": "Skinini kaydedemedik."
},
"app.skins.reorder-error.title": {
"message": "Skinler sıralanamadı"
},
"app.skins.section.builders-and-biomes": {
"message": "İnşaatçılar & Biyomlar"
},
@@ -516,7 +501,7 @@
"message": "İndirme tamamlandı"
},
"app.update-popup.reload": {
"message": "Güncellemek için yeniden yükle"
"message": "Yeniden başlat"
},
"app.update-popup.title": {
"message": "Güncelleme mevcut"
@@ -548,9 +533,6 @@
"friends.action.add-friend": {
"message": "Bir arkadaş ekle"
},
"friends.action.view-friend-requests": {
"message": "{count} arkadaşlık isteği"
},
"friends.add-friend.submit": {
"message": "Arkadaşlık isteği gönder"
},
@@ -716,30 +698,6 @@
"instance.settings.tabs.general.name": {
"message": "Ad"
},
"instance.settings.tabs.general.update-channel": {
"message": "Güncelleme kanalı"
},
"instance.settings.tabs.general.update-channel.alpha": {
"message": "Alfa"
},
"instance.settings.tabs.general.update-channel.alpha.description": {
"message": "Release, beta ve alpha versiyonları mevcut güncellemeler olarak gösterilecek."
},
"instance.settings.tabs.general.update-channel.beta": {
"message": "Beta"
},
"instance.settings.tabs.general.update-channel.beta.description": {
"message": "Release ve beta versiyonları mevcut güncellemeler olarak gösterilecek."
},
"instance.settings.tabs.general.update-channel.release": {
"message": "Yayın"
},
"instance.settings.tabs.general.update-channel.release.description": {
"message": "Yalnızca release versiyonları mevcut güncellemeler olarak gösterilecek."
},
"instance.settings.tabs.general.update-channel.select": {
"message": "Güncelleme kanalını seç"
},
"instance.settings.tabs.hooks": {
"message": "Başlatma kancaları"
},
+16 -64
View File
@@ -2,9 +2,6 @@
"app.action-bar.downloading-java": {
"message": "Завантаження Java {version}"
},
"app.action-bar.downloading-update": {
"message": "Завантаження оновлення"
},
"app.action-bar.downloads": {
"message": "Завантаження"
},
@@ -23,18 +20,12 @@
"app.action-bar.primary-instance": {
"message": "Основний профіль"
},
"app.action-bar.reload-to-update": {
"message": "Перезавантаження для оновлення"
},
"app.action-bar.show-more-running-instances": {
"message": "Показати більше запущених профілів"
},
"app.action-bar.stop-instance": {
"message": "Зупинити профіль"
},
"app.action-bar.update": {
"message": "Оновлення"
},
"app.action-bar.view-active-downloads": {
"message": "Переглянути активні завантаження"
},
@@ -158,9 +149,6 @@
"app.browse.server.installing": {
"message": "Установлення"
},
"app.content-install.no-compatible-versions": {
"message": "Жодна доступна версія не сумісна з {compatibilityLabel}. Виберіть версію, щоб усе одно її встановити. Залежності не встановлюватимуться автоматично."
},
"app.creation-modal.installing-modpack.description": {
"message": "{fileName}"
},
@@ -207,7 +195,7 @@
"message": "Видалити профіль"
},
"app.instance.modpack-already-installed.body": {
"message": "Ця збірка вже встановлена в профілі <bold>«{instanceName}»</bold>. Дійсно бажаєте її клонувати?"
"message": "Ця збірка вже встановлена в профілі <bold>«{instanceName}»</bold>. Ви впевнені, що хочете її скопіювати?"
},
"app.instance.modpack-already-installed.create": {
"message": "Створити"
@@ -281,9 +269,6 @@
"app.modal.install-to-play.install-button": {
"message": "Установити"
},
"app.modal.install-to-play.mod-count": {
"message": "{count, plural, one {# мод} few {# мода} other {# модів}}"
},
"app.modal.install-to-play.required-modpack": {
"message": "Потрібна збірка"
},
@@ -300,13 +285,13 @@
"message": "Дивитися вміст"
},
"app.modal.update-to-play.header": {
"message": "Оновлення для гри"
"message": "Оновлення перед грою"
},
"app.modal.update-to-play.update-required": {
"message": "Необхідне оновлення"
},
"app.modal.update-to-play.update-required-description": {
"message": "«{name}» потребує оновлення, щоб грати. Будь ласка, оновіть до останньої версії, щоб запустити гру."
"message": "«{name}» потребує оновлення, щоб грати. Будь ласка, оновіть гру до останньої версії, щоб запустити її."
},
"app.project.install-button.already-installed": {
"message": "Проєкт уже встановлено"
@@ -345,7 +330,7 @@
"message": "Додати скін"
},
"app.skins.add-button.drag-and-drop": {
"message": "Перетягніть сюди"
"message": "Перетягніть і вставте"
},
"app.skins.apply-button": {
"message": "Застосувати"
@@ -357,7 +342,7 @@
"message": "Це видалить вибраний скін назавжди. Ця дія не може бути скасована."
},
"app.skins.delete-modal.title": {
"message": "Ви дійсно бажаєте видалити цей скін?"
"message": "Чи ви впевнені що хочете видалити цей скін?"
},
"app.skins.dropped-file-error.text": {
"message": "Неможливо відкрити вибраний файл."
@@ -393,19 +378,19 @@
"message": "Редагування скіна"
},
"app.skins.modal.make-edit-first-tooltip": {
"message": "Спершу відредагуйте скін!"
"message": "Відредагуйте скін для початку!"
},
"app.skins.modal.no-cape-tooltip": {
"message": "Без плаща"
},
"app.skins.modal.none-cape-option": {
"message": "Немає"
"message": "Без"
},
"app.skins.modal.replace-texture-button": {
"message": "Замінити текстуру"
},
"app.skins.modal.save-skin-button": {
"message": "Зберегти скін"
"message": "Зберегти вигляд"
},
"app.skins.modal.saving-tooltip": {
"message": "Збереження…"
@@ -414,7 +399,7 @@
"message": "Текстура"
},
"app.skins.modal.upload-skin-first-tooltip": {
"message": "Спершу завантажте скін!"
"message": "Завантажте скін для початку!"
},
"app.skins.preview.edit-button": {
"message": "Редагувати скін"
@@ -423,16 +408,10 @@
"message": "Попередній перегляд"
},
"app.skins.rate-limit.text": {
"message": "Ви змінюєте скін занадто часто. Сервери Mojang тимчасово заблокували подальші запити. Зачекайте трохи, перш ніж спробувати знову."
"message": "Ви змінюєте свій скін дуже часто. Сервери Mojang тимчасово заблокували подальші запити. Зачекайте трохи перш ніж спробувати знову."
},
"app.skins.rate-limit.title": {
"message": "Повільніше!"
},
"app.skins.reorder-error.text": {
"message": "Ваше замовлення скінів не вдалося зберегти."
},
"app.skins.reorder-error.title": {
"message": "Не вдалося змінити порядок скінів"
"message": "Шановний!"
},
"app.skins.section.builders-and-biomes": {
"message": "Builders & Biomes"
@@ -489,13 +468,13 @@
"message": "Вибір скіну"
},
"app.update-popup.body.download-complete": {
"message": "Modrinth App v{version} завантажено. Перезапустіть, щоб оновити зараз, або це відбудеться автоматично після закриття застосунку."
"message": "Modrinth App v{version} завантажено. Перезапустіть зараз, щоб оновити його, або це відбудеться автоматично після закриття Modrinth App."
},
"app.update-popup.body.linux": {
"message": "Modrinth App v{version} вже доступно. Скористайтеся менеджером пакетів, щоб отримати найновіші функції та виправлення!"
"message": "Modrinth App v{version} вже доступно. Скористайтеся вашим менеджером пакетів, щоб отримати найновіші функції та виправлення!"
},
"app.update-popup.body.metered": {
"message": "Modrinth App v{version} вже доступно! Оскільки ви використовуєте лімітне підключення, ми не завантажили його автоматично."
"message": "Modrinth App v{version} доступний для завантаження! Оскільки ви на лімітному з’єднанні, ми не завантажили його автоматично."
},
"app.update-popup.changelog": {
"message": "Журнал змін"
@@ -507,7 +486,7 @@
"message": "Завантаження завершено"
},
"app.update-popup.reload": {
"message": "Перезавантаження для оновлення"
"message": "Перезавантажити"
},
"app.update-popup.title": {
"message": "Доступне оновлення"
@@ -531,7 +510,7 @@
"message": "Ще не зіграно"
},
"app.world.world-item.offline": {
"message": "Не в мережі"
"message": "Поза мережею"
},
"app.world.world-item.players-online": {
"message": "{count} у мережі"
@@ -539,9 +518,6 @@
"friends.action.add-friend": {
"message": "Додати друга"
},
"friends.action.view-friend-requests": {
"message": "{count} запит{count, plural, one { у} few {и в} many {ів у} other {у в}} друзі"
},
"friends.add-friend.submit": {
"message": "Надіслати запит у друзі"
},
@@ -707,30 +683,6 @@
"instance.settings.tabs.general.name": {
"message": "Назва"
},
"instance.settings.tabs.general.update-channel": {
"message": "Випуск оновлення"
},
"instance.settings.tabs.general.update-channel.alpha": {
"message": "Альфа"
},
"instance.settings.tabs.general.update-channel.alpha.description": {
"message": "Випуски, бета- та альфа-версії будуть показуватися як доступні оновлення."
},
"instance.settings.tabs.general.update-channel.beta": {
"message": "Бета"
},
"instance.settings.tabs.general.update-channel.beta.description": {
"message": "Випуски та бета-версії будуть показуватися як доступні оновлення."
},
"instance.settings.tabs.general.update-channel.release": {
"message": "Випуск"
},
"instance.settings.tabs.general.update-channel.release.description": {
"message": "Тільки випуски версії будуть показуватися як доступні оновлення."
},
"instance.settings.tabs.general.update-channel.select": {
"message": "Оберіть випуск оновлення"
},
"instance.settings.tabs.hooks": {
"message": "Гуки запуску"
},
+1 -49
View File
@@ -2,9 +2,6 @@
"app.action-bar.downloading-java": {
"message": "Đang tải xuống Java {version}"
},
"app.action-bar.downloading-update": {
"message": "Đang tải xuống bản cập nhật"
},
"app.action-bar.downloads": {
"message": "Tải xuống"
},
@@ -23,18 +20,12 @@
"app.action-bar.primary-instance": {
"message": "Bản instance chính"
},
"app.action-bar.reload-to-update": {
"message": "Tải lại để cập nhật"
},
"app.action-bar.show-more-running-instances": {
"message": "Hiện thêm các phiên bản đang chạy"
},
"app.action-bar.stop-instance": {
"message": "Dừng instance"
},
"app.action-bar.update": {
"message": "Cập nhật"
},
"app.action-bar.view-active-downloads": {
"message": "Xem các lượt tải xuống"
},
@@ -158,9 +149,6 @@
"app.browse.server.installing": {
"message": "Đang cài đặt"
},
"app.content-install.no-compatible-versions": {
"message": "Không có phiên bản nào phù hợp với {compatibilityLabel}. Nếu vẫn chọn một phiên bản để cài đặt, các gói phụ thuộc sẽ không được tải tự động."
},
"app.creation-modal.installing-modpack.description": {
"message": "{fileName}"
},
@@ -290,9 +278,6 @@
"app.modal.install-to-play.install-button": {
"message": "Tải xuống"
},
"app.modal.install-to-play.mod-count": {
"message": "{count, plural, other {# mod}}"
},
"app.modal.install-to-play.required-modpack": {
"message": "Yêu cầu modpack"
},
@@ -437,12 +422,6 @@
"app.skins.rate-limit.title": {
"message": "Bình tĩnh lại!"
},
"app.skins.reorder-error.text": {
"message": "Không thể lưu thứ tự skin của bạn."
},
"app.skins.reorder-error.title": {
"message": "Sắp xếp lại skin thất bại"
},
"app.skins.section.builders-and-biomes": {
"message": "Builders & Biomes"
},
@@ -516,7 +495,7 @@
"message": "Cài đặt thành công"
},
"app.update-popup.reload": {
"message": "Tải lại để cập nhật"
"message": "Khởi động lại"
},
"app.update-popup.title": {
"message": "Cập nhật mới hiện đang khả dụng"
@@ -548,9 +527,6 @@
"friends.action.add-friend": {
"message": "Thêm một người bạn"
},
"friends.action.view-friend-requests": {
"message": "{count} {count, plural, other {yêu cầu kết bạn}}"
},
"friends.add-friend.submit": {
"message": "Gửi yêu cầu kết bạn"
},
@@ -716,30 +692,6 @@
"instance.settings.tabs.general.name": {
"message": "Tên"
},
"instance.settings.tabs.general.update-channel": {
"message": "Kênh cập nhật"
},
"instance.settings.tabs.general.update-channel.alpha": {
"message": "Sơ khai"
},
"instance.settings.tabs.general.update-channel.alpha.description": {
"message": "Các phiên bản chính thức, thử nghiệm và sơ khai sẽ hiển thị trong danh sách cập nhật có sẵn."
},
"instance.settings.tabs.general.update-channel.beta": {
"message": "Thử nghiệm"
},
"instance.settings.tabs.general.update-channel.beta.description": {
"message": "Các phiên bản chính thức và thử nghiệm sẽ hiển thị trong danh sách cập nhật có sẵn."
},
"instance.settings.tabs.general.update-channel.release": {
"message": "Chính thức"
},
"instance.settings.tabs.general.update-channel.release.description": {
"message": "Chỉ phiên bản chính thức sẽ hiển thị trong danh sách cập nhật có sẵn."
},
"instance.settings.tabs.general.update-channel.select": {
"message": "Chọn kênh cập nhật"
},
"instance.settings.tabs.hooks": {
"message": "Khởi chạy Hooks"
},
+4 -49
View File
@@ -2,9 +2,6 @@
"app.action-bar.downloading-java": {
"message": "正在下载 Java {version}"
},
"app.action-bar.downloading-update": {
"message": "下载更新中"
},
"app.action-bar.downloads": {
"message": "下载"
},
@@ -23,18 +20,12 @@
"app.action-bar.primary-instance": {
"message": "主实例"
},
"app.action-bar.reload-to-update": {
"message": "重新加载以更新"
},
"app.action-bar.show-more-running-instances": {
"message": "显示更多运行中的实例"
},
"app.action-bar.stop-instance": {
"message": "停止实例"
},
"app.action-bar.update": {
"message": "更新"
},
"app.action-bar.view-active-downloads": {
"message": "查看正在下载内容"
},
@@ -72,7 +63,7 @@
"message": "在皮肤页面中禁用你玩家头顶上的名牌。"
},
"app.appearance-settings.hide-nametag.title": {
"message": "隐藏名"
"message": "隐藏名称标签"
},
"app.appearance-settings.jump-back-into-worlds.description": {
"message": "在主页的“快速回到”部分包含最近的世界。"
@@ -120,7 +111,7 @@
"message": "无法连接到身份验证服务器"
},
"app.browse.add-servers-to-instance": {
"message": "正在添加服务器到此实例"
"message": "将服务器添加到实例"
},
"app.browse.add-to-an-instance": {
"message": "添加到实例"
@@ -153,7 +144,7 @@
"message": "整合包"
},
"app.browse.server-instance-content-warning": {
"message": "加入服务器时,添加内容可能会破坏兼容性。当更新服务器实例内容时任何添加的内容也将丢失。"
"message": "加入服务器时,添加内容可能会破坏兼容性。当更新服务器实例内容时任何添加的内容也将丢失。"
},
"app.browse.server.installing": {
"message": "安装中"
@@ -281,9 +272,6 @@
"app.modal.install-to-play.install-button": {
"message": "安装"
},
"app.modal.install-to-play.mod-count": {
"message": "{count} 个模组"
},
"app.modal.install-to-play.required-modpack": {
"message": "需求整合包"
},
@@ -428,12 +416,6 @@
"app.skins.rate-limit.title": {
"message": "慢点!"
},
"app.skins.reorder-error.text": {
"message": "未能保存皮肤顺序。"
},
"app.skins.reorder-error.title": {
"message": "皮肤排序失败"
},
"app.skins.section.builders-and-biomes": {
"message": "建造者与生物群系"
},
@@ -507,7 +489,7 @@
"message": "下载完成"
},
"app.update-popup.reload": {
"message": "重新加载以更新"
"message": "重新启动"
},
"app.update-popup.title": {
"message": "有可用的更新"
@@ -539,9 +521,6 @@
"friends.action.add-friend": {
"message": "添加好友"
},
"friends.action.view-friend-requests": {
"message": "{count} 个好友请求"
},
"friends.add-friend.submit": {
"message": "发送好友请求"
},
@@ -707,30 +686,6 @@
"instance.settings.tabs.general.name": {
"message": "名称"
},
"instance.settings.tabs.general.update-channel": {
"message": "更新渠道"
},
"instance.settings.tabs.general.update-channel.alpha": {
"message": "Alpha 测试版"
},
"instance.settings.tabs.general.update-channel.alpha.description": {
"message": "正式版,beta 和 alpha 测试版会被显示为可用更新。"
},
"instance.settings.tabs.general.update-channel.beta": {
"message": "Beta 测试版"
},
"instance.settings.tabs.general.update-channel.beta.description": {
"message": "正式版和 beta 测试版会被显示为可用更新。"
},
"instance.settings.tabs.general.update-channel.release": {
"message": "正式版"
},
"instance.settings.tabs.general.update-channel.release.description": {
"message": "只有正式版会被显示为可用更新。"
},
"instance.settings.tabs.general.update-channel.select": {
"message": "选择更新渠道"
},
"instance.settings.tabs.hooks": {
"message": "启动 Hooks"
},
+31 -142
View File
@@ -2,9 +2,6 @@
"app.action-bar.downloading-java": {
"message": "正在下載 Java {version}"
},
"app.action-bar.downloading-update": {
"message": "正在下載更新"
},
"app.action-bar.downloads": {
"message": "下載"
},
@@ -23,18 +20,12 @@
"app.action-bar.primary-instance": {
"message": "主要實例"
},
"app.action-bar.reload-to-update": {
"message": "重新載入即可更新"
},
"app.action-bar.show-more-running-instances": {
"message": "顯示更多執行中的實例"
},
"app.action-bar.stop-instance": {
"message": "停止實例"
},
"app.action-bar.update": {
"message": "更新"
},
"app.action-bar.view-active-downloads": {
"message": "查看正在下載的項目"
},
@@ -42,10 +33,10 @@
"message": "查看實例"
},
"app.action-bar.view-logs": {
"message": "查看紀錄"
"message": "查看紀錄"
},
"app.appearance-settings.advanced-rendering.description": {
"message": "啟用進階繪製(如模糊效果)若無硬體加速可能會導致效能問題。"
"message": "啟用進階繪製(如模糊效果)若無硬體加速可能會導致效能問題。"
},
"app.appearance-settings.advanced-rendering.title": {
"message": "進階繪製"
@@ -87,19 +78,19 @@
"message": "最小化啟動器"
},
"app.appearance-settings.native-decorations.description": {
"message": "使用系統內建的視窗外框(需要重應用程式)。"
"message": "使用系統視窗外框(需要重新啟動應用程式)。"
},
"app.appearance-settings.native-decorations.title": {
"message": "原生視窗"
"message": "原生裝飾"
},
"app.appearance-settings.select-option": {
"message": "選擇選項"
},
"app.appearance-settings.show-play-time.description": {
"message": "顯示在該實例的遊玩時數。"
"message": "顯示在該實例的遊玩時"
},
"app.appearance-settings.show-play-time.title": {
"message": "顯示遊玩時"
"message": "顯示遊玩時"
},
"app.appearance-settings.toggle-sidebar.description": {
"message": "啟用切換側邊欄的功能。"
@@ -158,17 +149,14 @@
"app.browse.server.installing": {
"message": "安裝中"
},
"app.content-install.no-compatible-versions": {
"message": "沒有符合 {compatibilityLabel} 的可用版本。請選擇一個版本強制安裝。不會自動安裝相依模組。"
},
"app.creation-modal.installing-modpack.description": {
"message": "{fileName}"
},
"app.creation-modal.installing-modpack.title": {
"message": "正在安裝模組包..."
"message": "正在安裝模組包……"
},
"app.export-modal.description-placeholder": {
"message": "輸入模組包描述..."
"message": "輸入模組包描述……"
},
"app.export-modal.export-button": {
"message": "匯出"
@@ -267,7 +255,7 @@
"message": "確定要移除「{name}」嗎?"
},
"app.instance.worlds.search-worlds-placeholder": {
"message": "搜尋 {count} 個世界..."
"message": "搜尋 {count} 個世界……"
},
"app.instance.worlds.this-server": {
"message": "這個伺服器"
@@ -281,9 +269,6 @@
"app.modal.install-to-play.install-button": {
"message": "安裝"
},
"app.modal.install-to-play.mod-count": {
"message": "{count, plural, other {# 個模組}}"
},
"app.modal.install-to-play.required-modpack": {
"message": "所需模組包"
},
@@ -342,46 +327,25 @@
"message": "資源管理"
},
"app.skins.add-button": {
"message": "新增外觀"
},
"app.skins.add-button.drag-and-drop": {
"message": "拖曳"
"message": "新增皮膚"
},
"app.skins.apply-button": {
"message": "套用"
"message": "確認"
},
"app.skins.delete-button": {
"message": "刪除外觀"
},
"app.skins.delete-modal.description": {
"message": "這會永久刪除所選的外觀,這個動作無法復原。"
"message": "刪除皮膚"
},
"app.skins.delete-modal.title": {
"message": "你確定要刪除這個外觀嗎?"
},
"app.skins.dropped-file-error.text": {
"message": "無法讀取拖曳的檔案。"
},
"app.skins.dropped-file-error.title": {
"message": "處理檔案時發生錯誤"
"message": "你確定要刪除這個皮膚嗎"
},
"app.skins.edit-button": {
"message": "編輯外觀"
"message": "編輯皮膚"
},
"app.skins.modal.add-skin-button": {
"message": "新增外觀"
"message": "新增皮膚"
},
"app.skins.modal.add-title": {
"message": "新增外觀"
},
"app.skins.modal.arm-style-section": {
"message": "手臂身形"
},
"app.skins.modal.arm-style-slim": {
"message": "苗條"
},
"app.skins.modal.arm-style-wide": {
"message": "寬"
"message": "正在新增皮膚"
},
"app.skins.modal.cape-fallback-name": {
"message": "披風"
@@ -390,10 +354,7 @@
"message": "披風"
},
"app.skins.modal.edit-title": {
"message": "編輯外觀"
},
"app.skins.modal.make-edit-first-tooltip": {
"message": "請先對外觀進行編輯!"
"message": "編輯皮膚"
},
"app.skins.modal.no-cape-tooltip": {
"message": "無披風"
@@ -402,91 +363,46 @@
"message": "無"
},
"app.skins.modal.replace-texture-button": {
"message": "取代材質"
"message": "覆蓋材質"
},
"app.skins.modal.save-skin-button": {
"message": "儲存外觀"
"message": "儲存皮膚"
},
"app.skins.modal.saving-tooltip": {
"message": "正在儲存..."
"message": "正在儲存"
},
"app.skins.modal.texture-section": {
"message": "材質"
"message": "材質"
},
"app.skins.modal.upload-skin-first-tooltip": {
"message": "請先上傳外觀檔案!"
"message": "馬上新增一個皮膚"
},
"app.skins.preview.edit-button": {
"message": "編輯外觀"
"message": "編輯皮膚"
},
"app.skins.previewing-badge": {
"message": "預覽"
},
"app.skins.rate-limit.text": {
"message": "你變更外觀的頻率太過頻繁。Mojang 伺服器已暫時封鎖後續請求,請稍等片刻,然後再試一次。"
"message": "正在預覽"
},
"app.skins.rate-limit.title": {
"message": "慢一點"
},
"app.skins.reorder-error.text": {
"message": "無法儲存你的外觀排序。"
},
"app.skins.reorder-error.title": {
"message": "重新排序外觀失敗"
},
"app.skins.section.builders-and-biomes": {
"message": "建造者&生態域"
},
"app.skins.section.chase-the-skies": {
"message": "追逐天空"
"message": "慢一點"
},
"app.skins.section.default-skins": {
"message": "預設外觀"
},
"app.skins.section.minecon-earth-2017": {
"message": "MINECON Earth 2017"
"message": "預設皮膚"
},
"app.skins.section.modrinth": {
"message": "Modrinth"
},
"app.skins.section.modrinth-pride": {
"message": "Modrinth 驕傲"
},
"app.skins.section.modrinth-pride.tooltip": {
"message": "這些外觀是你在驕傲月期間捐款給 Modrinth 驕傲月籌款活動所獲得的。"
},
"app.skins.section.mounts-of-mayhem": {
"message": "群騎紛爭"
},
"app.skins.section.saved-skins": {
"message": "已儲存外觀"
},
"app.skins.section.striding-hero": {
"message": "Striding Hero"
},
"app.skins.section.the-copper-age": {
"message": "銅器時代"
},
"app.skins.section.the-garden-awakens": {
"message": "蒼園覺醒"
},
"app.skins.section.tiny-takeover": {
"message": "小鬼當家"
"message": "皮膚已儲存"
},
"app.skins.sign-in.button": {
"message": "登入"
},
"app.skins.sign-in.description": {
"message": "請登入你的 Minecraft 帳號以使用 Modrinth App 的外觀管理功能。"
},
"app.skins.sign-in.rinthbot-alt": {
"message": "興奮的 Modrinth 機器人"
},
"app.skins.sign-in.title": {
"message": "請登入"
},
"app.skins.title": {
"message": "外觀選擇工具"
"message": "皮膚選擇"
},
"app.update-popup.body.download-complete": {
"message": "Modrinth App v{version} 已完成下載!立即重新載入以更新,或在關閉 Modrinth App 時自動更新。"
@@ -507,7 +423,7 @@
"message": "下載完成"
},
"app.update-popup.reload": {
"message": "重新載入即可更新"
"message": "重新載入"
},
"app.update-popup.title": {
"message": "有可用的更新"
@@ -539,9 +455,6 @@
"friends.action.add-friend": {
"message": "新增好友"
},
"friends.action.view-friend-requests": {
"message": "{count} 個好友{count, plural, other {邀請}}"
},
"friends.add-friend.submit": {
"message": "送出好友邀請"
},
@@ -707,30 +620,6 @@
"instance.settings.tabs.general.name": {
"message": "名稱"
},
"instance.settings.tabs.general.update-channel": {
"message": "更新通道"
},
"instance.settings.tabs.general.update-channel.alpha": {
"message": "Alpha 版"
},
"instance.settings.tabs.general.update-channel.alpha.description": {
"message": "相容的正式版、Beta 版和 Alpha 版本都會顯示在可用更新中。"
},
"instance.settings.tabs.general.update-channel.beta": {
"message": "Beta 版"
},
"instance.settings.tabs.general.update-channel.beta.description": {
"message": "相容的正式版和 Beta 版本都會顯示在可用更新中。"
},
"instance.settings.tabs.general.update-channel.release": {
"message": "正式版"
},
"instance.settings.tabs.general.update-channel.release.description": {
"message": "僅有正式版會顯示在可用更新中。"
},
"instance.settings.tabs.general.update-channel.select": {
"message": "選擇更新通道"
},
"instance.settings.tabs.hooks": {
"message": "啟動掛勾"
},
@@ -792,10 +681,10 @@
"message": "自訂記憶體分配"
},
"instance.settings.tabs.java.enter-environment-variables": {
"message": "輸入環境變數..."
"message": "輸入環境變數……"
},
"instance.settings.tabs.java.enter-java-arguments": {
"message": "輸入 Java 參數..."
"message": "輸入 Java 參數……"
},
"instance.settings.tabs.java.environment-variables": {
"message": "環境變數"
+22 -72
View File
@@ -29,7 +29,7 @@ import {
import { useQueryClient } from '@tanstack/vue-query'
import { convertFileSrc } from '@tauri-apps/api/core'
import type { Ref } from 'vue'
import { computed, onMounted, onUnmounted, ref, watch } from 'vue'
import { computed, ref, watch } from 'vue'
import type { LocationQuery } from 'vue-router'
import { onBeforeRouteLeave, useRoute, useRouter } from 'vue-router'
@@ -41,7 +41,6 @@ import {
get_search_results_v3,
get_version_many,
} from '@/helpers/cache.js'
import { profile_listener } from '@/helpers/events.js'
import { get_loader_versions as getLoaderManifest } from '@/helpers/metadata'
import {
get as getInstance,
@@ -182,28 +181,6 @@ watchServerContextChanges()
await initInstanceContext()
async function refreshInstalledProjectIds() {
if (!route.query.i) return
if (route.query.from === 'worlds') {
const worlds = await get_profile_worlds(route.query.i as string).catch(handleError)
if (!worlds) return
const serverProjectIds = worlds
.filter((w) => w.type === 'server' && 'project_id' in w && w.project_id)
.map((w) => (w as { project_id: string }).project_id)
debugLog('installedServerProjectIds loaded', { count: serverProjectIds.length })
installedProjectIds.value = serverProjectIds
return
}
const ids = await getInstalledProjectIds(route.query.i as string).catch(handleError)
if (!ids) return
debugLog('installedProjectIds loaded', { count: ids.length })
installedProjectIds.value = ids
}
async function initInstanceContext() {
debugLog('initInstanceContext', {
queryI: route.query.i,
@@ -222,7 +199,24 @@ async function initInstanceContext() {
gameVersion: instance.value?.game_version,
})
await refreshInstalledProjectIds()
if (route.query.from === 'worlds') {
get_profile_worlds(route.query.i as string)
.then((worlds) => {
const serverProjectIds = worlds
.filter((w) => w.type === 'server' && 'project_id' in w && w.project_id)
.map((w) => (w as { project_id: string }).project_id)
debugLog('installedServerProjectIds loaded', { count: serverProjectIds.length })
installedProjectIds.value = serverProjectIds
})
.catch(handleError)
} else {
getInstalledProjectIds(route.query.i as string)
.then((ids) => {
debugLog('installedProjectIds loaded', { count: ids?.length })
installedProjectIds.value = ids
})
.catch(handleError)
}
if (instance.value?.linked_data?.project_id) {
debugLog('checking linked project for server status', instance.value.linked_data.project_id)
@@ -811,10 +805,10 @@ function getCardActions(
selectedInstall.versionId,
instance.value ? instance.value.path : null,
'SearchCard',
(versionId, installedProjectIds) => {
(versionId) => {
setProjectInstalling(projectResult.project_id, false)
if (versionId) {
onSearchResultsInstalled(installedProjectIds ?? [projectResult.project_id])
onSearchResultInstalled(projectResult.project_id)
}
},
(profile) => {
@@ -840,19 +834,7 @@ function onSearchResultInstalled(id: string) {
markServerProjectInstalled(id)
return
}
if (!newlyInstalled.value.includes(id)) {
newlyInstalled.value = [...newlyInstalled.value, id]
}
}
function onSearchResultsInstalled(ids: string[]) {
if (isServerContext.value) {
for (const id of ids) {
markServerProjectInstalled(id)
}
return
}
newlyInstalled.value = Array.from(new Set([...newlyInstalled.value, ...ids]))
newlyInstalled.value.push(id)
}
async function search(requestParams: string) {
@@ -984,38 +966,6 @@ if (instance.value?.game_version) {
await searchState.refreshSearch()
type UnlistenFn = () => void
let isUnmounted = false
let unlistenProfiles: UnlistenFn | null = null
onMounted(() => {
profile_listener(async (event: { event: string; profile_path_id: string }) => {
if (
instance.value &&
event.profile_path_id === instance.value.path &&
event.event === 'synced'
) {
await refreshInstalledProjectIds()
await searchState.refreshSearch()
}
})
.then((unlisten) => {
if (isUnmounted) {
unlisten()
return
}
unlistenProfiles = unlisten
})
.catch(handleError)
})
onUnmounted(() => {
isUnmounted = true
unlistenProfiles?.()
})
function getProjectBrowseQuery() {
if (!installContext.value) return undefined
return {
+1 -108
View File
@@ -46,8 +46,6 @@ import {
get_normalized_skin_texture,
normalize_skin_texture,
remove_custom_skin,
save_custom_skin,
set_custom_skin_order,
} from '@/helpers/skins.ts'
import { hasPride26Badge } from '@/helpers/user-campaigns.ts'
import { handleSevereError } from '@/store/error'
@@ -131,14 +129,6 @@ const messages = defineMessages({
id: 'app.skins.dropped-file-error.text',
defaultMessage: 'Failed to read the dropped file.',
},
reorderSkinErrorTitle: {
id: 'app.skins.reorder-error.title',
defaultMessage: 'Failed to reorder skins',
},
reorderSkinErrorText: {
id: 'app.skins.reorder-error.text',
defaultMessage: 'Your skin order could not be saved.',
},
deleteSkinTitle: {
id: 'app.skins.delete-modal.title',
defaultMessage: 'Are you sure you want to delete this skin?',
@@ -400,14 +390,6 @@ function skinsMatch(a?: Skin | null, b?: Skin | null) {
)
}
function skinsMatchIgnoringSource(a?: Skin | null, b?: Skin | null) {
return (
a?.texture_key === b?.texture_key &&
a?.variant === b?.variant &&
(a?.cape_id ?? null) === (b?.cape_id ?? null)
)
}
function isSkinSelected(skin: Skin) {
return skinsMatch(selectedSkin.value, skin)
}
@@ -509,19 +491,6 @@ function setLocallyEquippedSkin(skinToApply: Skin) {
void accountsCard.value?.setEquippedSkin(originalSelectedSkin.value)
}
function insertLocalSkin(savedSkin: Skin) {
const firstNonCustomSkinIndex = skins.value.findIndex((skin) => skin.source !== 'custom')
if (firstNonCustomSkinIndex === -1) {
skins.value = [...skins.value, savedSkin]
return
}
const nextSkins = [...skins.value]
nextSkins.splice(firstNonCustomSkinIndex, 0, savedSkin)
skins.value = nextSkins
}
function updateLocalSkin(savedSkin: Skin, applied: boolean, previousSkin?: Skin) {
let foundSkin = false
const replacesSelectedSkin =
@@ -550,7 +519,7 @@ function updateLocalSkin(savedSkin: Skin, applied: boolean, previousSkin?: Skin)
})
if (!foundSkin) {
insertLocalSkin({
skins.value.unshift({
...savedSkin,
is_equipped: applied || savedSkin.is_equipped,
})
@@ -579,81 +548,6 @@ function updateLocalSkin(savedSkin: Skin, applied: boolean, previousSkin?: Skin)
generateSkinPreviews(skins.value, capes.value)
}
async function reorderSavedSkins(orderedSkins: Skin[]) {
const previousSkins = skins.value
const previousSelectedSkin = selectedSkin.value
const previousOriginalSelectedSkin = originalSelectedSkin.value
const orderedTextureKeys = orderedSkins.map((skin) => skin.texture_key)
const orderedTextureKeySet = new Set(orderedTextureKeys)
const remainingSavedSkins = previousSkins.filter(
(skin) => skin.source !== 'default' && !orderedTextureKeySet.has(skin.texture_key),
)
const defaultSkins = previousSkins.filter((skin) => skin.source === 'default')
const nextSavedSkins = [...orderedSkins, ...remainingSavedSkins]
skins.value = [...nextSavedSkins, ...defaultSkins]
generateSkinPreviews(skins.value, capes.value)
try {
const persistedSavedSkins = await preserveExternalSkins(nextSavedSkins)
if (persistedSavedSkins.some((skin, index) => skin !== nextSavedSkins[index])) {
skins.value = [...persistedSavedSkins, ...defaultSkins]
generateSkinPreviews(skins.value, capes.value)
}
await set_custom_skin_order(
persistedSavedSkins
.filter((skin) => skin.source === 'custom')
.map((skin) => skin.texture_key),
)
} catch (error) {
skins.value = previousSkins
selectedSkin.value = previousSelectedSkin
originalSelectedSkin.value = previousOriginalSelectedSkin
generateSkinPreviews(skins.value, capes.value)
addNotification({
type: 'error',
title: formatMessage(messages.reorderSkinErrorTitle),
text: error instanceof Error ? error.message : formatMessage(messages.reorderSkinErrorText),
})
await loadSkins()
}
}
async function preserveExternalSkins(skinsToPersist: Skin[]) {
const preservedSkins: Skin[] = []
for (const skin of skinsToPersist) {
if (skin.source !== 'custom_external') {
preservedSkins.push(skin)
continue
}
const textureBlob = await normalize_skin_texture(skin.texture)
const capeId = skin.cape_id ? capes.value.find((cape) => cape.id === skin.cape_id) : undefined
const savedSkin = await save_custom_skin(skin, textureBlob, skin.variant, capeId, false)
const preservedSkin: Skin = {
...savedSkin,
source: 'custom',
is_equipped: skin.is_equipped,
}
if (skinsMatchIgnoringSource(selectedSkin.value, skin)) {
selectedSkin.value = preservedSkin
}
if (skinsMatchIgnoringSource(originalSelectedSkin.value, skin)) {
originalSelectedSkin.value = preservedSkin
void accountsCard.value?.setEquippedSkin(preservedSkin)
}
preservedSkins.push(preservedSkin)
}
return preservedSkins
}
function schedulePendingSkinRefresh() {
if (pendingSkinRefreshTimeout !== null) {
window.clearTimeout(pendingSkinRefreshTimeout)
@@ -1091,7 +985,6 @@ await loadSkins()
@select="changeSkin"
@edit="(skin, event) => editSkinModal?.show(event, skin)"
@delete="confirmDeleteSkin"
@reorder-saved-skins="reorderSavedSkins"
@add-skin="openAddSkinFileBrowser"
@add-skin-dragenter="onAddSkinDragOver"
@add-skin-dragover="onAddSkinDragOver"
+13 -92
View File
@@ -90,7 +90,6 @@ import {
useVIntl,
versionChangesGameVersion,
} from '@modrinth/ui'
import { useQuery, useQueryClient } from '@tanstack/vue-query'
import { getCurrentWebview } from '@tauri-apps/api/webview'
import { open } from '@tauri-apps/plugin-dialog'
import { openUrl } from '@tauri-apps/plugin-opener'
@@ -100,7 +99,7 @@ import { useRouter } from 'vue-router'
import ExportModal from '@/components/ui/ExportModal.vue'
import ShareModalWrapper from '@/components/ui/modal/ShareModalWrapper.vue'
import { trackEvent } from '@/helpers/analytics'
import { get_project_versions, get_version, get_version_many } from '@/helpers/cache.js'
import { get_project_versions, get_version } from '@/helpers/cache.js'
import { profile_listener } from '@/helpers/events.js'
import { type InstanceContentData, loadInstanceContentData } from '@/helpers/instance-content'
import {
@@ -154,7 +153,6 @@ const { formatMessage } = useVIntl()
const { handleError, addNotification } = injectNotificationManager()
const { installingItems } = injectContentInstall()
const router = useRouter()
const queryClient = useQueryClient()
const debug = useDebugLogger('Mods:ContentUpdate')
const props = defineProps<{
@@ -214,13 +212,6 @@ const contentUpdaterModal = ref<InstanceType<typeof ContentUpdaterModal> | null>
const modpackContentModal = ref<InstanceType<typeof ModpackContentModal> | null>()
const modpackUpdateConfirmModal = ref<InstanceType<typeof ConfirmModpackUpdateModal> | null>()
const modpackContentQueryKey = computed(() => ['linkedModpackContent', props.instance.path])
const modpackContentQuery = useQuery({
queryKey: modpackContentQueryKey,
queryFn: () => get_linked_modpack_content(props.instance.path),
enabled: computed(() => !!props.instance?.path && !!props.instance?.linked_data),
})
// TODO: Extract content operation and updater modal state into composables; this page currently owns file mutations, dependency installs, busy flags, and version selection flow.
const updatingProject = ref<ContentItem | null>(null)
const updatingProjectVersions = ref<Labrinth.Versions.v2.Version[]>([])
@@ -460,63 +451,6 @@ async function removeMod(mod: ContentItem) {
}
}
function isBreakingDependency(dependency: Labrinth.Versions.v2.Dependency) {
return dependency.dependency_type === 'required' || dependency.dependency_type === 'embedded'
}
function dependencyTargetsItem(dependency: Labrinth.Versions.v2.Dependency, item: ContentItem) {
return (
(!!dependency.project_id && dependency.project_id === item.project?.id) ||
('version_id' in dependency &&
!!dependency.version_id &&
dependency.version_id === item.version?.id)
)
}
async function getDeleteDependencyWarning(items: ContentItem[]) {
if (props.isServerInstance) return null
const deletingIds = new Set(items.map(getContentItemId))
const remainingItems = projects.value.filter((item) => !deletingIds.has(getContentItemId(item)))
const versionIds = [
...new Set(remainingItems.map((item) => item.version?.id).filter((id): id is string => !!id)),
]
if (versionIds.length === 0) return null
const versions = (await get_version_many(versionIds).catch((err) => {
handleError(err as Error)
return null
})) as Labrinth.Versions.v2.Version[] | null
if (!versions) return null
const versionsById = new Map(versions.map((version) => [version.id, version]))
const dependents = remainingItems
.map((candidate) => {
const version = candidate.version?.id ? versionsById.get(candidate.version.id) : null
if (!version) return null
const dependencies = items.filter((item) => {
if (!item.project?.id && !item.version?.id) return false
return version.dependencies?.some(
(dependency) =>
isBreakingDependency(dependency) && dependencyTargetsItem(dependency, item),
)
})
return dependencies.length > 0 ? { item: candidate, dependencies } : null
})
.filter(
(dependent): dependent is { item: ContentItem; dependencies: ContentItem[] } =>
dependent !== null,
)
return dependents.length > 0 ? { items, dependents } : null
}
async function updateProject(mod: ContentItem) {
if (!mod.file_path) return
const operation = beginContentOperation(mod)
@@ -547,7 +481,6 @@ async function updateProject(mod: ContentItem) {
})
} catch (err) {
handleError(err as Error)
throw err
} finally {
await refreshContentState('must_revalidate')
finishContentOperation(mod, operation)
@@ -749,19 +682,13 @@ async function handleModpackContentBulkToggle(items: ContentItem[]) {
async function handleModpackContent() {
if (!props.instance?.path) return
if (modpackContentQuery.data.value !== undefined) {
modpackContentModal.value?.show(modpackContentQuery.data.value)
return
}
modpackContentModal.value?.showLoading()
const { data, error } = await modpackContentQuery.refetch()
const contentItems = await get_linked_modpack_content(props.instance.path).catch(handleError)
if (data !== undefined) {
modpackContentModal.value?.show(data)
if (contentItems) {
modpackContentModal.value?.show(contentItems)
} else {
if (error) handleError(error)
modpackContentModal.value?.hide()
}
}
@@ -769,12 +696,9 @@ async function handleModpackContent() {
async function refreshModpackContentItems(cacheBehaviour?: CacheBehaviour) {
if (!props.instance?.path) return
const contentItems = await queryClient
.fetchQuery({
queryKey: modpackContentQueryKey.value,
queryFn: () => get_linked_modpack_content(props.instance.path, cacheBehaviour),
})
.catch(handleError)
const contentItems = await get_linked_modpack_content(props.instance.path, cacheBehaviour).catch(
handleError,
)
if (contentItems) {
modpackContentModal.value?.setItems(contentItems)
@@ -961,15 +885,13 @@ async function handleModalUpdate(
} else if (updatingProject.value) {
const mod = updatingProject.value
try {
if (mod.has_update && mod.update_version_id === selectedVersion.id) {
await updateProject(mod)
} else {
await switchProjectVersion(mod, selectedVersion)
}
} finally {
resetUpdateState()
if (mod.has_update && mod.update_version_id === selectedVersion.id) {
await updateProject(mod)
} else {
await switchProjectVersion(mod, selectedVersion)
}
resetUpdateState()
}
}
@@ -1159,7 +1081,6 @@ provideContentManager({
deleteItem: removeMod,
bulkDeleteItems: (items: ContentItem[]) =>
Promise.all(items.map((item) => removeMod(item))).then(() => {}),
getDeleteDependencyWarning,
refresh: () => initProjects('must_revalidate'),
browse: handleBrowseContent,
uploadFiles: handleUploadFiles,
+3 -18
View File
@@ -406,20 +406,6 @@ function buildProjectHref(path, extraQuery = {}) {
return qs ? `${path}?${qs}` : path
}
function buildBrowseHref(path) {
const params = new URLSearchParams()
for (const [key, val] of Object.entries(route.query)) {
if (key === 'b') continue
if (Array.isArray(val)) {
for (const v of val) params.append(key, v)
} else if (val) {
params.append(key, String(val))
}
}
const qs = params.toString()
return qs ? `${path}?${qs}` : path
}
const projectDescriptionHref = computed(() => buildProjectHref(`/project/${route.params.id}`))
const versionsHref = computed(() =>
buildProjectHref(`/project/${route.params.id}/versions`, instanceFilters.value),
@@ -430,7 +416,7 @@ const projectBrowseBackUrl = computed(() => {
const browsePath = route.query.b
if (typeof browsePath === 'string' && browsePath.startsWith('/browse/')) return browsePath
const type = data.value?.project_type ? `${data.value.project_type}` : 'mod'
return buildBrowseHref(`/browse/${type}`)
return `/browse/${type}`
})
const projectInstallContext = computed(() => {
@@ -739,11 +725,10 @@ async function install(version) {
version,
instance.value ? instance.value.path : null,
'ProjectPage',
(version, installedProjectIds) => {
(version) => {
installing.value = false
const installedIds = installedProjectIds ?? [data.value.id]
if (instance.value && version && installedIds.includes(data.value.id)) {
if (instance.value && version) {
installed.value = true
installedVersion.value = version
}
@@ -5,7 +5,7 @@
:current-title="version.name"
:link-stack="[
{
href: buildProjectHref(`/project/${route.params.id}/versions`),
href: `/project/${route.params.id}/versions`,
label: 'Versions',
},
]"
@@ -249,19 +249,6 @@ const author = computed(() =>
const displayDependencies = ref({})
function buildProjectHref(path) {
const params = new URLSearchParams()
for (const [key, val] of Object.entries(route.query)) {
if (Array.isArray(val)) {
for (const v of val) params.append(key, v)
} else if (val) {
params.append(key, String(val))
}
}
const qs = params.toString()
return qs ? `${path}?${qs}` : path
}
async function refreshDisplayDependencies() {
const projectIds = new Set()
const versionIds = new Set()
@@ -295,7 +282,7 @@ async function refreshDisplayDependencies() {
icon: project?.icon_url,
title: project?.title || project?.name,
subtitle: `Version ${version.version_number} is ${dependency.dependency_type}`,
link: buildProjectHref(`/project/${project.slug}/version/${version.id}`),
link: `/project/${project.slug}/version/${version.id}`,
}
} else {
const project = dependencies.projects.find((obj) => obj.id === dependency.project_id)
@@ -305,7 +292,7 @@ async function refreshDisplayDependencies() {
icon: project?.icon_url,
title: project?.title || project?.name,
subtitle: `${dependency.dependency_type}`,
link: buildProjectHref(`/project/${project.slug}`),
link: `/project/${project.slug}`,
}
} else {
return {
@@ -5,7 +5,7 @@
:game-versions="gameVersions"
:versions="versions"
:project="project"
:version-link="(version) => buildProjectHref(`/project/${project.id}/version/${version.id}`)"
:version-link="(version) => `/project/${project.id}/version/${version.id}`"
>
<template #actions="{ version }">
<ButtonStyled circular type="transparent">
@@ -73,7 +73,6 @@ import {
ProjectPageVersions,
} from '@modrinth/ui'
import { ref } from 'vue'
import { useRoute } from 'vue-router'
import { SwapIcon } from '@/assets/icons/index.js'
import { get_game_versions, get_loaders } from '@/helpers/tags.js'
@@ -110,20 +109,6 @@ defineProps({
})
const { handleError } = injectNotificationManager()
const route = useRoute()
function buildProjectHref(path) {
const params = new URLSearchParams()
for (const [key, val] of Object.entries(route.query)) {
if (Array.isArray(val)) {
for (const v of val) params.append(key, v)
} else if (val) {
params.append(key, String(val))
}
}
const qs = params.toString()
return qs ? `${path}?${qs}` : path
}
const [loaders, gameVersions] = await Promise.all([
get_loaders().catch(handleError).then(ref),
@@ -42,8 +42,6 @@ interface ModpackAlreadyInstalledModalRef {
show: (instanceName: string, instancePath: string) => void
}
export type ContentInstallCallback = (versionId?: string, installedProjectIds?: string[]) => void
const LOADER_ORDER = ['vanilla', 'fabric', 'quilt', 'neoforge', 'forge']
const SUPPORTED_LOADERS: Set<string> = new Set(['vanilla', 'forge', 'fabric', 'quilt', 'neoforge'])
const VANILLA_COMPATIBLE_LOADERS: Set<string> = new Set(['minecraft', 'datapack'])
@@ -104,7 +102,7 @@ export interface ContentInstallContext {
versionId?: string | null,
instancePath?: string | null,
source?: string,
callback?: ContentInstallCallback,
callback?: (versionId?: string) => void,
createInstanceCallback?: (profile: string) => void,
hints?: { preferredLoader?: string; preferredGameVersion?: string; showProjectInfo?: boolean },
) => Promise<void>
@@ -258,25 +256,25 @@ export function createContentInstall(opts: {
let incompatibilityWarningModalRef: ModalRef | null = null
let currentProject: Labrinth.Projects.v2.Project | null = null
let currentVersions: Labrinth.Versions.v2.Version[] = []
let currentCallback: ContentInstallCallback = () => {}
let currentCallback: (versionId?: string) => void = () => {}
let profileMap: Record<string, GameInstance> = {}
let incompatibilityWarningInstance: GameInstance | null = null
let incompatibilityWarningProject: Labrinth.Projects.v2.Project | null = null
let incompatibilityWarningCallback: ContentInstallCallback = () => {}
let incompatibilityWarningCallback: (versionId?: string) => void = () => {}
let incompatibilityWarningInstalled = false
let pendingModpackInstall: {
project: Labrinth.Projects.v2.Project
version: string
source: string
callback: ContentInstallCallback
callback: (versionId?: string) => void
createInstanceCallback: (profile: string) => void
} | null = null
async function showModInstallModal(
project: Labrinth.Projects.v2.Project,
versions: Labrinth.Versions.v2.Version[],
onInstall: ContentInstallCallback,
onInstall: (versionId?: string) => void,
hints?: { preferredLoader?: string; preferredGameVersion?: string; showProjectInfo?: boolean },
) {
currentProject = project
@@ -442,7 +440,7 @@ export function createContentInstall(opts: {
if (versionId && storeInstance) {
storeInstance.installed = true
}
currentCallback(versionId, versionId && currentProject ? [currentProject.id] : undefined)
currentCallback(versionId)
}
await showIncompatibilityWarning(
profile,
@@ -489,7 +487,7 @@ export function createContentInstall(opts: {
title: currentProject!.title,
source: 'ProjectInstallModal',
})
currentCallback(version.id, installedProjectIds)
currentCallback(version.id)
} catch (err) {
if (storeInstance) storeInstance.installing = false
opts.handleError(err)
@@ -503,7 +501,7 @@ export function createContentInstall(opts: {
project: Labrinth.Projects.v2.Project,
versions: Labrinth.Versions.v2.Version[],
version: Labrinth.Versions.v2.Version,
callback: ContentInstallCallback,
callback: (versionId?: string) => void,
) {
incompatibilityWarningInstance = instance
incompatibilityWarningProject = project
@@ -544,7 +542,7 @@ export function createContentInstall(opts: {
incompatibilityWarningInstalling.value = false
incompatibilityWarningInstalled = true
incompatibilityWarningCallback(version.id, [incompatibilityWarningProject.id])
incompatibilityWarningCallback(version.id)
incompatibilityWarningModalRef?.hide()
trackEvent('ProjectInstall', {
@@ -632,7 +630,7 @@ export function createContentInstall(opts: {
versionId?: string | null,
instancePath?: string | null,
source: string = 'unknown',
callback: ContentInstallCallback = () => {},
callback: (versionId?: string) => void = () => {},
createInstanceCallback: (profile: string) => void = () => {},
hints?: { preferredLoader?: string; preferredGameVersion?: string; showProjectInfo?: boolean },
) {
@@ -716,7 +714,7 @@ export function createContentInstall(opts: {
title: project.title,
source,
})
callback(version.id, installedProjectIds)
callback(version.id)
} finally {
removeInstallingItems(instancePath, installedProjectIds)
}
+1
View File
@@ -248,6 +248,7 @@ export default new createRouter({
component: Instance.Logs,
meta: {
useRootContext: true,
// renderMode: 'fixed',
breadcrumb: [{ name: '?Instance', link: '/instance/{id}/' }, { name: 'Logs' }],
},
},
+8 -11
View File
@@ -1,7 +1,5 @@
import { defineStore } from 'pinia'
let systemThemeMq: MediaQueryList | null = null
export const DEFAULT_FEATURE_FLAGS = {
project_background: false,
page_path: false,
@@ -55,22 +53,21 @@ export const useTheming = defineStore('themeStore', {
this.setThemeClass()
},
setThemeClass() {
const html = document.getElementsByTagName('html')[0]
for (const theme of THEME_OPTIONS) {
html.classList.remove(`${theme}-mode`)
document.getElementsByTagName('html')[0].classList.remove(`${theme}-mode`)
}
systemThemeMq?.removeEventListener('change', this.setThemeClass)
systemThemeMq = null
let theme = this.selectedTheme
if (this.selectedTheme === 'system') {
systemThemeMq = window.matchMedia('(prefers-color-scheme: dark)')
systemThemeMq.addEventListener('change', this.setThemeClass)
theme = systemThemeMq.matches ? 'dark' : 'light'
const darkThemeMq = window.matchMedia('(prefers-color-scheme: dark)')
if (darkThemeMq.matches) {
theme = 'dark'
} else {
theme = 'light'
}
}
html.classList.add(`${theme}-mode`)
document.getElementsByTagName('html')[0].classList.add(`${theme}-mode`)
},
getFeatureFlag(key: FeatureFlag) {
return this.featureFlags[key] ?? DEFAULT_FEATURE_FLAGS[key]
-1
View File
@@ -117,7 +117,6 @@ fn main() {
"equip_skin",
"remove_custom_skin",
"save_custom_skin",
"set_custom_skin_order",
"unequip_skin",
"flush_pending_skin_change",
"flush_pending_skin_change_for_profile",
-9
View File
@@ -14,7 +14,6 @@ pub fn init<R: tauri::Runtime>() -> tauri::plugin::TauriPlugin<R> {
equip_skin,
remove_custom_skin,
save_custom_skin,
set_custom_skin_order,
unequip_skin,
flush_pending_skin_change,
flush_pending_skin_change_for_profile,
@@ -92,14 +91,6 @@ pub async fn save_custom_skin(
.await?)
}
/// `invoke('plugin:minecraft-skins|set_custom_skin_order', texture_keys)`
///
/// See also: [minecraft_skins::set_custom_skin_order]
#[tauri::command]
pub async fn set_custom_skin_order(texture_keys: Vec<String>) -> Result<()> {
Ok(minecraft_skins::set_custom_skin_order(texture_keys).await?)
}
/// `invoke('plugin:minecraft-skins|unequip_skin')`
///
/// See also: [minecraft_skins::unequip_skin]
-14
View File
@@ -432,20 +432,6 @@ components:
primary_file:
type: string
description: The multipart field name of the primary file
environment:
type: string
enum:
- client_and_server
- client_only
- client_only_server_optional
- singleplayer_only
- server_only
- server_only_client_optional
- dedicated_server_only
- client_or_server
- client_or_server_prefers_both
- unknown
description: The environment that this version is for.
required:
- file_parts
- project_id
@@ -756,16 +756,16 @@ svg.inline-svg {
padding: var(--gap-16);
h2 {
@apply font-semibold;
font-size: var(--text-18);
font-weight: var(--weight-extrabold);
color: var(--color-contrast);
line-height: initial;
margin: 0;
}
h3 {
@apply font-semibold;
font-size: var(--text-16);
font-weight: var(--weight-bold);
color: var(--color-base);
margin: 0;
}
@@ -812,7 +812,6 @@ svg.inline-svg {
}
.details-list__item {
@apply font-normal;
@extend .iconified-list-item;
.details-list__item__text--style-secondary {
@@ -25,7 +25,7 @@
class="inline-flex items-center"
>
<button
v-tooltip="legendEntry.tooltip ?? legendEntry.projectName ?? ''"
v-tooltip="legendEntry.projectName ?? ''"
type="button"
class="inline-flex items-center gap-1.5 px-2 py-0.5 text-sm !outline-0 transition-all focus-within:!outline-0 focus:!outline-0 focus-visible:!outline-0"
:class="[
@@ -64,7 +64,6 @@ export function useAnalyticsChartLegend({
id: dataset.projectId,
name: dataset.label,
projectName: dataset.projectName,
tooltip: dataset.tooltip,
color: dataset.borderColor,
totalValue: getChartDatasetTotal(dataset),
hidden: hiddenDatasetIds.value.has(dataset.projectId),
@@ -119,7 +118,6 @@ export function useAnalyticsChartLegend({
id: getPreviousPeriodDatasetId(entry.id),
name: formatMessage(analyticsChartMessages.previousPeriodSuffix, { name: entry.name }),
projectName: entry.projectName,
tooltip: entry.tooltip,
color: entry.color,
totalValue: previousDataset ? getChartDatasetTotal(previousDataset) : 0,
hidden: hiddenDatasetIds.value.has(getPreviousPeriodDatasetId(entry.id)),
@@ -155,7 +153,6 @@ export function useAnalyticsChartLegend({
name: dataset.label,
}),
projectName: dataset.projectName,
tooltip: dataset.tooltip,
data: previousData,
borderColor: dataset.borderColor,
backgroundColor: dataset.backgroundColor,
@@ -299,7 +296,7 @@ export function useAnalyticsChartLegend({
}
function getLegendEntryTooltip(legendEntry: AnalyticsChartLegendEntry) {
return legendEntry.tooltip ?? legendEntry.projectName ?? ''
return legendEntry.projectName ?? ''
}
function isUnmonetizedLegendEntry(legendEntry: AnalyticsChartLegendEntry) {
@@ -97,7 +97,7 @@
"
/>
<span
v-tooltip="entry.tooltip ?? entry.projectName ?? ''"
v-tooltip="entry.projectName ?? ''"
class="min-w-0 truncate"
:class="{
'line-through': entry.hidden,
@@ -145,7 +145,6 @@ export type AnalyticsChartTooltipEntry = {
projectId: string
name: string
projectName?: string
tooltip?: string
color: string
formattedValue: string
hidden: boolean
@@ -208,7 +208,6 @@ const hoverEntries = computed(() => {
projectId: legendEntry.id,
name: legendEntry.name,
projectName: legendEntry.projectName,
tooltip: legendEntry.tooltip,
color: legendEntry.color,
formattedValue: props.isRatioMode
? `${ratioValue.toFixed(1)}%`
@@ -14,7 +14,6 @@ export type AnalyticsChartLegendEntry = {
id: string
name: string
projectName?: string
tooltip?: string
color: string
totalValue: number
hidden: boolean
@@ -1,24 +1,20 @@
import type { Labrinth } from '@modrinth/api-client'
import {
type AnalyticsBreakdownPreset,
type AnalyticsDashboardProject,
type AnalyticsDashboardStat,
type AnalyticsGroupByPreset,
type AnalyticsSelectedFilters,
doesAnalyticsPointMatchNormalizedFilters,
normalizeAnalyticsSelectedFilters,
import type {
AnalyticsBreakdownPreset,
AnalyticsDashboardProject,
AnalyticsDashboardStat,
AnalyticsGroupByPreset,
} from '~/providers/analytics/analytics'
import type { FormatMessage } from '../analytics-messages'
import {
analyticsChartMessages,
analyticsMessages,
analyticsStatCardMessages,
formatAnalyticsDependentProjectFallbackLabel,
formatAnalyticsDownloadReasonLabel,
formatAnalyticsLoaderLabel,
formatAnalyticsMonetizationLabel,
type FormatMessage,
} from '../analytics-messages'
import {
ALL_BREAKDOWN_VALUE,
@@ -26,8 +22,6 @@ import {
getAnalyticsBreakdownDatasetId,
getAnalyticsBreakdownKey,
getAnalyticsBreakdownValues,
isNoDependentAnalyticsBreakdownValue,
isUnknownAnalyticsBreakdownValue,
UNKNOWN_BREAKDOWN_VALUE,
} from '../breakdown'
import { PREVIOUS_PERIOD_DATASET_ID_PREFIX } from './analytics-chart-constants'
@@ -36,7 +30,6 @@ export type ChartDataset = {
projectId: string
label: string
projectName?: string
tooltip?: string
data: number[]
borderColor: string
backgroundColor: string
@@ -143,7 +136,6 @@ export function formatBreakdownLabel(
breakdownValue: string,
selectedBreakdown: AnalyticsBreakdownPreset,
getVersionDisplayName: ((versionId: string) => string) | undefined,
userNamesById: ReadonlyMap<string, string> | undefined,
formatMessage: FormatMessage,
): string {
const normalizedValue = breakdownValue.trim()
@@ -168,15 +160,6 @@ export function formatBreakdownLabel(
if (selectedBreakdown === 'download_reason') {
return formatAnalyticsDownloadReasonLabel(normalizedLowercaseValue, formatMessage)
}
if (selectedBreakdown === 'dependent_project_download') {
if (isNoDependentAnalyticsBreakdownValue(breakdownValue)) {
return formatMessage(analyticsMessages.noDependent)
}
return breakdownValue
}
if (selectedBreakdown === 'user_id') {
return userNamesById?.get(breakdownValue) ?? breakdownValue
}
if (selectedBreakdown === 'version_id') {
return getVersionDisplayName?.(breakdownValue) ?? breakdownValue
}
@@ -191,34 +174,19 @@ export function formatBreakdownLabels(
breakdownValues: readonly string[],
selectedBreakdowns: readonly AnalyticsBreakdownPreset[],
getVersionDisplayName: ((versionId: string) => string) | undefined,
userNamesById: ReadonlyMap<string, string> | undefined,
formatMessage: FormatMessage,
): string {
const normalizedBreakdowns = selectedBreakdowns.filter((breakdown) => breakdown !== 'none')
const downloadReasonBreakdownIndex = normalizedBreakdowns.indexOf('download_reason')
return collapseRepeatedUnknownBreakdownLabels(
normalizedBreakdowns.map((breakdown, index) => {
const breakdownValue = breakdownValues[index] ?? ''
if (
breakdown === 'dependent_project_download' &&
downloadReasonBreakdownIndex !== -1 &&
isUnknownAnalyticsBreakdownValue(breakdownValue)
) {
return formatAnalyticsDependentProjectFallbackLabel(
breakdownValues[downloadReasonBreakdownIndex],
selectedBreakdowns
.filter((breakdown) => breakdown !== 'none')
.map((breakdown, index) =>
formatBreakdownLabel(
breakdownValues[index] ?? '',
breakdown,
getVersionDisplayName,
formatMessage,
)
}
return formatBreakdownLabel(
breakdownValue,
breakdown,
getVersionDisplayName,
userNamesById,
formatMessage,
)
}),
),
),
formatMessage,
).join(COMBINED_BREAKDOWN_LABEL_SEPARATOR)
}
@@ -290,33 +258,6 @@ type PaletteRankEntry = {
total: number
}
function formatDatasetTooltip(projectName: string | undefined): string | undefined {
return projectName
}
function formatDependentProjectDatasetTooltip(
versionName: string | undefined,
dependentProjectName: string | undefined,
dependencyProjectNames: readonly string[],
formatMessage: FormatMessage,
): string | undefined {
if (dependencyProjectNames.length === 0) {
return undefined
}
if (versionName && dependentProjectName) {
return formatMessage(analyticsChartMessages.dependentProjectVersionTooltip, {
dependentProject: dependentProjectName,
dependencyProject: dependencyProjectNames.join(', '),
version: versionName,
})
}
return formatMessage(analyticsChartMessages.dependentOnProjectTooltip, {
project: dependencyProjectNames.join(', '),
})
}
function getPaletteColorForIndex(index: number, palette: string[]): string {
if (palette.length === 0) return ''
@@ -379,10 +320,6 @@ export function buildChartDatasets(
activeStat: AnalyticsDashboardStat,
palette: string[],
selectedBreakdowns: readonly AnalyticsBreakdownPreset[],
selectedFilters: AnalyticsSelectedFilters,
dependentProjectTypesById: ReadonlyMap<string, readonly string[]>,
projectNamesById: ReadonlyMap<string, string>,
userNamesById: ReadonlyMap<string, string>,
getVersionDisplayName: ((versionId: string) => string) | undefined,
getVersionProjectName: ((versionId: string) => string | undefined) | undefined,
formatMessage: FormatMessage,
@@ -395,43 +332,17 @@ export function buildChartDatasets(
const dataLength = Math.max(sliceCount, timeSlices.length)
const normalizedBreakdowns = selectedBreakdowns.filter((breakdown) => breakdown !== 'none')
const normalizedFilters = normalizeAnalyticsSelectedFilters(selectedFilters)
const projectNamesById = new Map(selectedProjects.map((project) => [project.id, project.name]))
function formatChartBreakdownLabels(breakdownValues: readonly string[]): string {
const downloadReasonBreakdownIndex = normalizedBreakdowns.indexOf('download_reason')
return collapseRepeatedUnknownBreakdownLabels(
normalizedBreakdowns.map((breakdown, index) => {
const breakdownValue = breakdownValues[index] ?? ''
if (breakdown === 'project' || breakdown === 'dependent_project_download') {
if (
breakdown === 'dependent_project_download' &&
isNoDependentAnalyticsBreakdownValue(breakdownValue)
) {
return formatMessage(analyticsMessages.noDependent)
}
if (
breakdown === 'dependent_project_download' &&
isUnknownAnalyticsBreakdownValue(breakdownValue)
) {
return downloadReasonBreakdownIndex === -1
? formatMessage(analyticsMessages.unknown)
: formatAnalyticsDependentProjectFallbackLabel(
breakdownValues[downloadReasonBreakdownIndex],
formatMessage,
)
}
if (breakdown === 'project') {
return projectNamesById.get(breakdownValue) ?? breakdownValue
}
return formatBreakdownLabel(
breakdownValue,
breakdown,
getVersionDisplayName,
userNamesById,
formatMessage,
)
return formatBreakdownLabel(breakdownValue, breakdown, getVersionDisplayName, formatMessage)
}),
formatMessage,
).join(COMBINED_BREAKDOWN_LABEL_SEPARATOR)
@@ -441,27 +352,13 @@ export function buildChartDatasets(
normalizedBreakdowns.length > 0 &&
!(normalizedBreakdowns.length === 1 && normalizedBreakdowns[0] === 'project')
) {
const hasVersionBreakdown = normalizedBreakdowns.includes('version_id')
const hasDependentProjectBreakdown = normalizedBreakdowns.includes('dependent_project_download')
const shouldShowDependentProjectTooltip =
hasDependentProjectBreakdown && (selectedProjects.length > 1 || hasVersionBreakdown)
const dataByBreakdown = new Map<string, number[]>()
const breakdownValuesByKey = new Map<string, string[]>()
const downloadTotalsByBreakdown = new Map<string, number>()
const dependentOnProjectIdsByBreakdown = new Map<string, Set<string>>()
timeSlices.forEach((slice, sliceIndex) => {
for (const point of slice) {
if (!isProjectAnalyticsPointInSelectedProjects(point, selectedProjectIds)) continue
if (
!doesAnalyticsPointMatchNormalizedFilters(
point,
normalizedFilters,
dependentProjectTypesById,
)
) {
continue
}
const breakdownValues = getAnalyticsBreakdownValues(
point,
@@ -477,11 +374,6 @@ export function buildChartDatasets(
dataByBreakdown.set(breakdownKey, new Array(dataLength).fill(0))
breakdownValuesByKey.set(breakdownKey, breakdownValues)
}
if (shouldShowDependentProjectTooltip && point.metric_kind === 'downloads') {
const projectIds = dependentOnProjectIdsByBreakdown.get(breakdownKey) ?? new Set<string>()
projectIds.add(point.source_project)
dependentOnProjectIdsByBreakdown.set(breakdownKey, projectIds)
}
if (point.metric_kind === 'downloads') {
downloadTotalsByBreakdown.set(
@@ -510,42 +402,6 @@ export function buildChartDatasets(
return Array.from(dataByBreakdown.entries()).map(([breakdownKey, data]) => {
const breakdownValues = breakdownValuesByKey.get(breakdownKey) ?? []
const fallbackColor = colorsByBreakdown.get(breakdownKey) ?? ''
const versionBreakdownIndex = normalizedBreakdowns.indexOf('version_id')
const dependentProjectBreakdownIndex = normalizedBreakdowns.indexOf(
'dependent_project_download',
)
const versionName =
hasVersionBreakdown && versionBreakdownIndex !== -1
? getVersionDisplayName?.(breakdownValues[versionBreakdownIndex] ?? '')
: undefined
const dependentProjectId =
dependentProjectBreakdownIndex !== -1
? breakdownValues[dependentProjectBreakdownIndex]
: undefined
const dependentProjectName = dependentProjectId
? isMissingDependentProjectValue(dependentProjectId)
? undefined
: (projectNamesById.get(dependentProjectId) ?? dependentProjectId)
: undefined
const versionProjectName =
normalizedBreakdowns.length === 1 && normalizedBreakdowns[0] === 'version_id'
? getVersionProjectName?.(breakdownValues[0] ?? '')
: undefined
const dependencyProjectNames = [...(dependentOnProjectIdsByBreakdown.get(breakdownKey) ?? [])]
.map((projectId) => projectNamesById.get(projectId) ?? projectId)
.sort((left, right) => left.localeCompare(right))
const dependentProjectTooltip = dependentProjectId
? isNoDependentAnalyticsBreakdownValue(dependentProjectId)
? formatMessage(analyticsMessages.noDependentTooltip)
: isUnknownAnalyticsBreakdownValue(dependentProjectId)
? formatMessage(analyticsMessages.unknown)
: formatDependentProjectDatasetTooltip(
versionName,
dependentProjectName,
dependencyProjectNames,
formatMessage,
)
: undefined
const color =
normalizedBreakdowns.length === 1
? getBreakdownColor(
@@ -558,8 +414,10 @@ export function buildChartDatasets(
return {
projectId: getAnalyticsBreakdownDatasetId(breakdownValues, normalizedBreakdowns),
label: formatChartBreakdownLabels(breakdownValues),
projectName: versionProjectName,
tooltip: dependentProjectTooltip ?? formatDatasetTooltip(versionProjectName),
projectName:
normalizedBreakdowns.length === 1 && normalizedBreakdowns[0] === 'version_id'
? getVersionProjectName?.(breakdownValues[0] ?? '')
: undefined,
data,
borderColor: color,
backgroundColor: color,
@@ -574,15 +432,6 @@ export function buildChartDatasets(
timeSlices.forEach((slice, sliceIndex) => {
for (const point of slice) {
if (!isProjectAnalyticsPointInSelectedProjects(point, selectedProjectIds)) continue
if (
!doesAnalyticsPointMatchNormalizedFilters(
point,
normalizedFilters,
dependentProjectTypesById,
)
) {
continue
}
if (point.metric_kind === 'downloads') {
downloadTotal += getMetricValue(point, 'downloads')
@@ -632,15 +481,6 @@ export function buildChartDatasets(
timeSlices.forEach((slice, sliceIndex) => {
for (const point of slice) {
if (!isProjectAnalyticsPointInSelectedProjects(point, selectedProjectIds)) continue
if (
!doesAnalyticsPointMatchNormalizedFilters(
point,
normalizedFilters,
dependentProjectTypesById,
)
) {
continue
}
const breakdownValues = getAnalyticsBreakdownValues(
point,
@@ -686,10 +526,6 @@ export function buildChartDatasets(
return Array.from(dataByProjectBreakdown.entries()).map(([breakdownKey, data]) => {
const breakdownValues = breakdownValuesByKey.get(breakdownKey) ?? []
const fallbackColor = colorsByBreakdown.get(breakdownKey) ?? ''
const versionProjectName =
normalizedBreakdowns.length === 1 && normalizedBreakdowns[0] === 'version_id'
? getVersionProjectName?.(breakdownValues[0] ?? '')
: undefined
const color =
normalizedBreakdowns.length === 1
? getBreakdownColor(
@@ -702,8 +538,10 @@ export function buildChartDatasets(
return {
projectId: getAnalyticsBreakdownDatasetId(breakdownValues, normalizedBreakdowns),
label: formatChartBreakdownLabels(breakdownValues),
projectName: versionProjectName,
tooltip: formatDatasetTooltip(versionProjectName),
projectName:
normalizedBreakdowns.length === 1 && normalizedBreakdowns[0] === 'version_id'
? getVersionProjectName?.(breakdownValues[0] ?? '')
: undefined,
data,
borderColor: color,
backgroundColor: color,
@@ -711,10 +549,6 @@ export function buildChartDatasets(
})
}
function isMissingDependentProjectValue(value: string | undefined): boolean {
return isUnknownAnalyticsBreakdownValue(value) || isNoDependentAnalyticsBreakdownValue(value)
}
export function getSliceCount(
timeRange: Labrinth.Analytics.v3.TimeRange,
fallback: number,
@@ -1,15 +1,12 @@
import type { Labrinth } from '@modrinth/api-client'
import { useVIntl } from '@modrinth/ui'
import { computed, type ComputedRef, ref, watch } from 'vue'
import { useTheme } from '~/composables/nuxt-accessors'
import { isDarkTheme } from '~/plugins/theme/index.ts'
import type {
AnalyticsBreakdownPreset,
AnalyticsDashboardContextValue,
AnalyticsDashboardProject,
AnalyticsDashboardStat,
AnalyticsSelectedFilters,
} from '~/providers/analytics/analytics'
import {
@@ -50,16 +47,12 @@ export function useAnalyticsChartDatasets(
| 'displayedPreviousTimeSlices'
| 'displayedSelectedGroupBy'
| 'displayedSelectedBreakdowns'
| 'displayedSelectedFilters'
| 'hiddenGraphDatasetIds'
| 'hasExplicitGraphDatasetSelection'
| 'isGraphDatasetSelectionActive'
| 'selectedGraphDatasetIds'
| 'defaultGraphDatasetIds'
| 'topGraphDatasetIds'
| 'projectNamesById'
| 'userNamesById'
| 'dependentProjectTypesById'
| 'getVersionDisplayName'
| 'getVersionProjectName'
>,
@@ -167,10 +160,6 @@ export function useAnalyticsChartDatasets(
selectedProjects.value,
legendPalette.value,
context.displayedSelectedBreakdowns.value,
context.displayedSelectedFilters.value,
context.dependentProjectTypesById.value,
context.projectNamesById.value,
context.userNamesById.value,
context.getVersionDisplayName,
showProjectVersionNames.value ? context.getVersionProjectName : undefined,
formatMessage,
@@ -183,10 +172,6 @@ export function useAnalyticsChartDatasets(
selectedProjects.value,
legendPalette.value,
context.displayedSelectedBreakdowns.value,
context.displayedSelectedFilters.value,
context.dependentProjectTypesById.value,
context.projectNamesById.value,
context.userNamesById.value,
context.getVersionDisplayName,
showProjectVersionNames.value ? context.getVersionProjectName : undefined,
formatMessage,
@@ -372,16 +357,12 @@ export function useAnalyticsChartDatasets(
}
function buildDatasetsByStat(
timeSlices: Labrinth.Analytics.v3.TimeSlice[],
timeSlices: Parameters<typeof buildChartDatasets>[0],
selectedProjects: AnalyticsDashboardProject[],
palette: string[],
selectedBreakdowns: readonly AnalyticsBreakdownPreset[],
selectedFilters: AnalyticsSelectedFilters,
dependentProjectTypesById: ReadonlyMap<string, readonly string[]>,
projectNamesById: ReadonlyMap<string, string>,
userNamesById: ReadonlyMap<string, string>,
getVersionDisplayName: (versionId: string) => string,
getVersionProjectName: ((versionId: string) => string | undefined) | undefined,
selectedBreakdowns: Parameters<typeof buildChartDatasets>[4],
getVersionDisplayName: Parameters<typeof buildChartDatasets>[5],
getVersionProjectName: Parameters<typeof buildChartDatasets>[6],
formatMessage: FormatMessage,
sliceCount: number,
) {
@@ -393,10 +374,6 @@ function buildDatasetsByStat(
stat,
palette,
selectedBreakdowns,
selectedFilters,
dependentProjectTypesById,
projectNamesById,
userNamesById,
getVersionDisplayName,
getVersionProjectName,
formatMessage,
@@ -13,7 +13,6 @@ export type AnalyticsBreakdownItemType =
| 'downloadSource'
| 'gameVersion'
| 'loader'
| 'member'
| 'monetization'
| 'project'
| 'projectVersion'
@@ -84,14 +83,6 @@ export const analyticsMessages = defineMessages({
id: 'analytics.value.unknown',
defaultMessage: 'Unknown',
},
noDependent: {
id: 'analytics.value.no-dependent',
defaultMessage: 'None',
},
noDependentTooltip: {
id: 'analytics.value.no-dependent-tooltip',
defaultMessage: 'Downloaded for reasons other than being a dependency',
},
other: {
id: 'analytics.value.other',
defaultMessage: 'Other',
@@ -184,14 +175,6 @@ export const analyticsMessages = defineMessages({
id: 'analytics.filter.search.project-versions',
defaultMessage: 'Search project versions...',
},
searchDependentProjectsPlaceholder: {
id: 'analytics.filter.search.dependent-projects',
defaultMessage: 'Search projects...',
},
searchMembersPlaceholder: {
id: 'analytics.filter.search.members',
defaultMessage: 'Search members...',
},
searchVersionsPlaceholder: {
id: 'analytics.filter.search.versions',
defaultMessage: 'Search versions...',
@@ -368,22 +351,6 @@ export const analyticsBreakdownMessages = defineMessages({
id: 'analytics.breakdown.download-reason',
defaultMessage: 'Download reason',
},
members: {
id: 'analytics.breakdown.members',
defaultMessage: 'Member',
},
dependentProjectDownload: {
id: 'analytics.breakdown.dependent-project-download',
defaultMessage: 'Dependent project',
},
dependentProjectType: {
id: 'analytics.breakdown.dependent-project-type',
defaultMessage: 'Dependent project type',
},
dependentOn: {
id: 'analytics.breakdown.dependent-on',
defaultMessage: 'Dependent on',
},
versionId: {
id: 'analytics.breakdown.project-version',
defaultMessage: 'Project version',
@@ -550,33 +517,25 @@ export const analyticsChartMessages = defineMessages({
id: 'analytics.chart.action.show-top-eight',
defaultMessage: 'Show top 8',
},
dependentOnProjectTooltip: {
id: 'analytics.chart.tooltip.dependent-on-project',
defaultMessage: 'Dependent on {project}',
},
dependentProjectVersionTooltip: {
id: 'analytics.chart.tooltip.dependent-project-version',
defaultMessage: '{dependentProject} dependent on {dependencyProject}, {version}',
},
tableSelectionLimited: {
id: 'analytics.chart.table-selection.limited',
defaultMessage:
'Showing {limit} {itemType, select, project {{limit, plural, one {project} other {projects}}} country {{limit, plural, one {country} other {countries}}} monetization {{limit, plural, one {monetization value} other {monetization values}}} downloadSource {{limit, plural, one {download source} other {download sources}}} downloadReason {{limit, plural, one {download reason} other {download reasons}}} member {{limit, plural, one {member} other {members}}} projectVersion {{limit, plural, one {project version} other {project versions}}} loader {{limit, plural, one {loader} other {loaders}}} gameVersion {{limit, plural, one {game version} other {game versions}}} other {{limit, plural, one {item} other {items}}}} from table',
'Showing {limit} {itemType, select, project {{limit, plural, one {project} other {projects}}} country {{limit, plural, one {country} other {countries}}} monetization {{limit, plural, one {monetization value} other {monetization values}}} downloadSource {{limit, plural, one {download source} other {download sources}}} downloadReason {{limit, plural, one {download reason} other {download reasons}}} projectVersion {{limit, plural, one {project version} other {project versions}}} loader {{limit, plural, one {loader} other {loaders}}} gameVersion {{limit, plural, one {game version} other {game versions}}} other {{limit, plural, one {item} other {items}}}} from table',
},
tableSelectionAll: {
id: 'analytics.chart.table-selection.all',
defaultMessage:
'Showing all {itemType, select, project {{count, plural, one {project} other {projects}}} country {{count, plural, one {country} other {countries}}} monetization {{count, plural, one {monetization value} other {monetization values}}} downloadSource {{count, plural, one {download source} other {download sources}}} downloadReason {{count, plural, one {download reason} other {download reasons}}} member {{count, plural, one {member} other {members}}} projectVersion {{count, plural, one {project version} other {project versions}}} loader {{count, plural, one {loader} other {loaders}}} gameVersion {{count, plural, one {game version} other {game versions}}} other {{count, plural, one {item} other {items}}}} from table',
'Showing all {itemType, select, project {{count, plural, one {project} other {projects}}} country {{count, plural, one {country} other {countries}}} monetization {{count, plural, one {monetization value} other {monetization values}}} downloadSource {{count, plural, one {download source} other {download sources}}} downloadReason {{count, plural, one {download reason} other {download reasons}}} projectVersion {{count, plural, one {project version} other {project versions}}} loader {{count, plural, one {loader} other {loaders}}} gameVersion {{count, plural, one {game version} other {game versions}}} other {{count, plural, one {item} other {items}}}} from table',
},
tableSelectionTop: {
id: 'analytics.chart.table-selection.top',
defaultMessage:
'Showing top {count} {itemType, select, project {{count, plural, one {project} other {projects}}} country {{count, plural, one {country} other {countries}}} monetization {{count, plural, one {monetization value} other {monetization values}}} downloadSource {{count, plural, one {download source} other {download sources}}} downloadReason {{count, plural, one {download reason} other {download reasons}}} member {{count, plural, one {member} other {members}}} projectVersion {{count, plural, one {project version} other {project versions}}} loader {{count, plural, one {loader} other {loaders}}} gameVersion {{count, plural, one {game version} other {game versions}}} other {{count, plural, one {item} other {items}}}} from table',
'Showing top {count} {itemType, select, project {{count, plural, one {project} other {projects}}} country {{count, plural, one {country} other {countries}}} monetization {{count, plural, one {monetization value} other {monetization values}}} downloadSource {{count, plural, one {download source} other {download sources}}} downloadReason {{count, plural, one {download reason} other {download reasons}}} projectVersion {{count, plural, one {project version} other {project versions}}} loader {{count, plural, one {loader} other {loaders}}} gameVersion {{count, plural, one {game version} other {game versions}}} other {{count, plural, one {item} other {items}}}} from table',
},
tableSelectionCount: {
id: 'analytics.chart.table-selection.count',
defaultMessage:
'Showing {count} {itemType, select, project {{count, plural, one {project} other {projects}}} country {{count, plural, one {country} other {countries}}} monetization {{count, plural, one {monetization value} other {monetization values}}} downloadSource {{count, plural, one {download source} other {download sources}}} downloadReason {{count, plural, one {download reason} other {download reasons}}} member {{count, plural, one {member} other {members}}} projectVersion {{count, plural, one {project version} other {project versions}}} loader {{count, plural, one {loader} other {loaders}}} gameVersion {{count, plural, one {game version} other {game versions}}} other {{count, plural, one {item} other {items}}}} from table',
'Showing {count} {itemType, select, project {{count, plural, one {project} other {projects}}} country {{count, plural, one {country} other {countries}}} monetization {{count, plural, one {monetization value} other {monetization values}}} downloadSource {{count, plural, one {download source} other {download sources}}} downloadReason {{count, plural, one {download reason} other {download reasons}}} projectVersion {{count, plural, one {project version} other {project versions}}} loader {{count, plural, one {loader} other {loaders}}} gameVersion {{count, plural, one {game version} other {game versions}}} other {{count, plural, one {item} other {items}}}} from table',
},
lineView: {
id: 'analytics.chart.view.line',
@@ -822,10 +781,6 @@ export function formatAnalyticsBreakdownLabel(
return formatMessage(analyticsBreakdownMessages.userAgent)
case 'download_reason':
return formatMessage(analyticsBreakdownMessages.downloadReason)
case 'user_id':
return formatMessage(analyticsBreakdownMessages.members)
case 'dependent_project_download':
return formatMessage(analyticsBreakdownMessages.dependentProjectDownload)
case 'version_id':
return formatMessage(analyticsBreakdownMessages.versionId)
case 'loader':
@@ -855,10 +810,6 @@ export function getAnalyticsBreakdownItemType(
return 'downloadSource'
case 'download_reason':
return 'downloadReason'
case 'user_id':
return 'member'
case 'dependent_project_download':
return 'project'
case 'version_id':
return 'projectVersion'
case 'loader':
@@ -902,18 +853,6 @@ export function formatAnalyticsDownloadReasonLabel(
}
}
export function formatAnalyticsDependentProjectFallbackLabel(
downloadReason: string | undefined,
formatMessage: FormatMessage,
): string {
const normalizedReason = downloadReason?.trim().toLowerCase()
if (normalizedReason === 'standalone' || normalizedReason === 'update') {
return formatMessage(analyticsMessages.noDependent)
}
return formatMessage(analyticsMessages.unknown)
}
export function formatAnalyticsDownloadSourceLabel(
source: string,
formatMessage: FormatMessage,
@@ -19,8 +19,6 @@ import type {
MutableRouteQuery,
} from '~/providers/analytics/analytics-types'
import { getAnalyticsBreakdownsWithSharedStats } from './query-builder/query-filter-utils'
export const DEFAULT_TIMEFRAME_PRESET: AnalyticsTimeframePreset = 'last_30_days'
export const DEFAULT_TIMEFRAME_MODE: AnalyticsTimeframeMode = 'preset'
export const DEFAULT_LAST_TIMEFRAME_AMOUNT = 1
@@ -75,11 +73,9 @@ const BREAKDOWN_PRESET_VALUES: AnalyticsBreakdownPreset[] = [
'monetization',
'user_agent',
'download_reason',
'user_id',
'version_id',
'loader',
'game_version',
'dependent_project_download',
]
const ANALYTICS_DASHBOARD_STAT_VALUES: AnalyticsDashboardStat[] = [
@@ -93,18 +89,15 @@ const ANALYTICS_GRAPH_VIEW_MODE_VALUES: AnalyticsGraphViewMode[] = ['line', 'are
const ANALYTICS_TABLE_SORT_COLUMN_VALUES: AnalyticsTableSortColumn[] = [
'date',
'project',
'dependent_on',
'breakdown',
'breakdown_project',
'breakdown_country',
'breakdown_monetization',
'breakdown_user_agent',
'breakdown_download_reason',
'breakdown_user_id',
'breakdown_version_id',
'breakdown_loader',
'breakdown_game_version',
'breakdown_dependent_project_download',
'views',
'downloads',
'revenue',
@@ -138,12 +131,9 @@ const QUERY_KEY_FILTER_MONETIZATION = 'a_monetization'
const QUERY_KEY_FILTER_USER_AGENT = 'a_user_agent'
const QUERY_KEY_FILTER_LEGACY_DOWNLOAD_SOURCE = 'a_download_source'
const QUERY_KEY_FILTER_DOWNLOAD_REASON = 'a_download_reason'
const QUERY_KEY_FILTER_USER_ID = 'a_user_id'
const QUERY_KEY_FILTER_VERSION_ID = 'a_version_id'
const QUERY_KEY_FILTER_GAME_VERSION = 'a_game_version'
const QUERY_KEY_FILTER_LOADER_TYPE = 'a_loader_type'
const QUERY_KEY_FILTER_DEPENDENT_PROJECT_ID = 'a_dependent_project_id'
const QUERY_KEY_FILTER_DEPENDENT_PROJECT_TYPE = 'a_dependent_project_type'
const QUERY_KEY_STAT = 'a_stat'
const QUERY_KEY_GRAPH_VIEW_MODE = 'a_chart'
const QUERY_KEY_GRAPH_RATIO_MODE = 'a_ratio'
@@ -164,12 +154,9 @@ const URL_FILTER_CATEGORIES: Exclude<AnalyticsQueryFilterCategory, 'project'>[]
'monetization',
'user_agent',
'download_reason',
'user_id',
'version_id',
'game_version',
'loader_type',
'dependent_project_id',
'dependent_project_type',
]
const FILTER_QUERY_KEY_BY_CATEGORY: Record<
@@ -181,12 +168,9 @@ const FILTER_QUERY_KEY_BY_CATEGORY: Record<
monetization: QUERY_KEY_FILTER_MONETIZATION,
user_agent: QUERY_KEY_FILTER_USER_AGENT,
download_reason: QUERY_KEY_FILTER_DOWNLOAD_REASON,
user_id: QUERY_KEY_FILTER_USER_ID,
version_id: QUERY_KEY_FILTER_VERSION_ID,
game_version: QUERY_KEY_FILTER_GAME_VERSION,
loader_type: QUERY_KEY_FILTER_LOADER_TYPE,
dependent_project_id: QUERY_KEY_FILTER_DEPENDENT_PROJECT_ID,
dependent_project_type: QUERY_KEY_FILTER_DEPENDENT_PROJECT_TYPE,
}
const ANALYTICS_QUERY_KEYS = [
@@ -205,12 +189,9 @@ const ANALYTICS_QUERY_KEYS = [
QUERY_KEY_FILTER_USER_AGENT,
QUERY_KEY_FILTER_LEGACY_DOWNLOAD_SOURCE,
QUERY_KEY_FILTER_DOWNLOAD_REASON,
QUERY_KEY_FILTER_USER_ID,
QUERY_KEY_FILTER_VERSION_ID,
QUERY_KEY_FILTER_GAME_VERSION,
QUERY_KEY_FILTER_LOADER_TYPE,
QUERY_KEY_FILTER_DEPENDENT_PROJECT_ID,
QUERY_KEY_FILTER_DEPENDENT_PROJECT_TYPE,
QUERY_KEY_STAT,
QUERY_KEY_GRAPH_VIEW_MODE,
QUERY_KEY_GRAPH_RATIO_MODE,
@@ -231,12 +212,9 @@ export function buildEmptySelectedFilters(): AnalyticsSelectedFilters {
monetization: [],
user_agent: [],
download_reason: [],
user_id: [],
version_id: [],
game_version: [],
loader_type: [],
dependent_project_id: [],
dependent_project_type: [],
}
}
@@ -277,7 +255,7 @@ function normalizeFilterQueryValues(
.filter((value) => PROJECT_STATUS_FILTER_VALUES.includes(value))
}
if (category !== 'loader_type' && category !== 'dependent_project_type') {
if (category !== 'loader_type') {
return values
}
@@ -478,7 +456,7 @@ export function getAnalyticsBreakdownPresetsForProjectSelection(
}
}
return getAnalyticsBreakdownsWithSharedStats(normalizedBreakdowns)
return normalizedBreakdowns
}
export function getAnalyticsBreakdownPresetForProjectSelection(
@@ -798,12 +776,6 @@ export function hasAnalyticsProjectSelectionQuery(query: LocationQuery): boolean
return parseListQueryValue(query[QUERY_KEY_PROJECT_IDS]).length > 0
}
export function hasAnalyticsAllProjectSelectionQuery(query: LocationQuery): boolean {
return parseListQueryValue(query[QUERY_KEY_PROJECT_IDS]).includes(
PROJECT_SELECTION_ALL_QUERY_VALUE,
)
}
export function hasAnalyticsGraphProjectEventsVisibilityQuery(query: LocationQuery): boolean {
return query[QUERY_KEY_GRAPH_PROJECT_EVENTS_VISIBILITY] !== undefined
}
@@ -1,62 +0,0 @@
<template>
<div class="mr-2.5 flex min-w-0 items-center gap-2">
<span
v-if="!hideIcon"
v-tooltip="iconTooltip"
class="flex size-6 shrink-0 items-center justify-center overflow-hidden rounded text-primary"
>
<img
v-if="iconUrl"
:src="iconUrl"
:alt="formatMessage(analyticsMessages.projectIconAlt, { name: label })"
class="h-6 w-6 rounded object-cover"
/>
<BoxIcon v-else class="h-full w-full" />
</span>
<component
:is="labelHref ? 'a' : 'span'"
v-tooltip="labelTooltip"
:href="labelHref"
:target="labelHref ? '_blank' : undefined"
:rel="labelHref ? 'noopener noreferrer' : undefined"
class="line-clamp-2 min-w-0 truncate text-wrap font-semibold leading-tight text-primary"
:class="{ 'hover:underline': labelHref }"
:title="label"
>
{{ label }}
</component>
<component
:is="organizationHref ? 'a' : 'span'"
v-if="organizationTooltip"
v-tooltip="organizationTooltip"
:href="organizationHref"
:target="organizationHref ? '_blank' : undefined"
:rel="organizationHref ? 'noopener noreferrer' : undefined"
:aria-label="organizationTooltip"
class="flex size-4 shrink-0 items-center text-primary"
:class="{ 'hover:underline': organizationHref }"
>
<OrganizationIcon class="size-4" />
</component>
</div>
</template>
<script setup lang="ts">
import { BoxIcon, OrganizationIcon } from '@modrinth/assets'
import { useVIntl } from '@modrinth/ui'
import { analyticsMessages } from '../analytics-messages.ts'
defineProps<{
label: string
iconUrl?: string
iconTooltip?: string
hideIcon?: boolean
labelHref?: string
labelTooltip?: string
organizationHref?: string
organizationTooltip?: string
}>()
const { formatMessage } = useVIntl()
</script>
@@ -23,6 +23,7 @@ type BuildAnalyticsTableColumnsOptions = {
selectedBreakdowns: readonly AnalyticsTableBreakdownPreset[]
selectedFilters: AnalyticsSelectedFilters
showBreakdownColumn: boolean
showProjectVersionProjectColumn: boolean
formatMessage: FormatMessage
getRelevantAnalyticsDashboardStats: (
breakdowns: readonly AnalyticsBreakdownPreset[],
@@ -42,6 +43,7 @@ export function buildAnalyticsTableColumns({
selectedBreakdowns,
selectedFilters,
showBreakdownColumn,
showProjectVersionProjectColumn,
formatMessage,
getRelevantAnalyticsDashboardStats,
}: BuildAnalyticsTableColumnsOptions): TableColumn<AnalyticsTableColumnKey>[] {
@@ -64,11 +66,20 @@ export function buildAnalyticsTableColumns({
key: getAnalyticsTableBreakdownColumnKey(breakdown),
label: getAnalyticsTableBreakdownColumnLabel(breakdown, formatMessage),
enableSorting: true,
width: breakdown === 'project' && selectedBreakdowns.length === 1 ? '45%' : undefined,
width: breakdown === 'project' ? '25%' : undefined,
})
}
}
if (showProjectVersionProjectColumn) {
nextColumns.push({
key: 'project',
label: formatAnalyticsBreakdownLabel('project', formatMessage),
enableSorting: true,
width: '25%',
})
}
for (const stat of stats) {
const column = getAnalyticsTableMetricColumn(stat, formatMessage)
if (column) {
@@ -91,7 +102,6 @@ export function getAnalyticsTableMetricColumn(
enableSorting: true,
defaultSortDirection: 'desc',
align: 'right',
width: '20%',
}
case 'downloads':
return {
@@ -100,7 +110,6 @@ export function getAnalyticsTableMetricColumn(
enableSorting: true,
defaultSortDirection: 'desc',
align: 'right',
width: '20%',
}
case 'revenue':
return {
@@ -109,7 +118,6 @@ export function getAnalyticsTableMetricColumn(
enableSorting: true,
defaultSortDirection: 'desc',
align: 'right',
width: '20%',
}
case 'playtime':
return {
@@ -118,7 +126,6 @@ export function getAnalyticsTableMetricColumn(
enableSorting: true,
defaultSortDirection: 'desc',
align: 'right',
width: '20%',
}
default:
return null
@@ -67,8 +67,6 @@ function getAnalyticsTableCsvCellValue(
return row.date
case 'project':
return row.project
case 'dependent_on':
return row.dependent_on
case 'breakdown':
return row.breakdownDisplay
case 'views':
@@ -1,11 +1,8 @@
import type { Labrinth } from '@modrinth/api-client'
import {
type AnalyticsBreakdownPreset,
type AnalyticsDashboardStat,
type AnalyticsSelectedFilters,
doesAnalyticsPointMatchNormalizedFilters,
normalizeAnalyticsSelectedFilters,
import type {
AnalyticsBreakdownPreset,
AnalyticsDashboardStat,
} from '~/providers/analytics/analytics'
import {
@@ -15,18 +12,13 @@ import {
getSliceCount,
} from '../analytics-chart/analytics-chart-utils'
import type { FormatMessage } from '../analytics-messages'
import {
analyticsMessages,
formatAnalyticsDependentProjectFallbackLabel,
} from '../analytics-messages'
import { analyticsMessages } from '../analytics-messages'
import {
ALL_BREAKDOWN_VALUE,
COMBINED_BREAKDOWN_LABEL_SEPARATOR,
getAnalyticsBreakdownDatasetId,
getAnalyticsBreakdownKey,
getAnalyticsBreakdownValues,
isNoDependentAnalyticsBreakdownValue,
isUnknownAnalyticsBreakdownValue,
} from '../breakdown'
import { getAnalyticsTableBreakdownColumnKey } from './analytics-table-columns'
import type {
@@ -44,12 +36,8 @@ type BuildAnalyticsTableRowsOptions = {
timeSlices: Labrinth.Analytics.v3.TimeSlice[]
selectedBreakdowns: readonly AnalyticsTableBreakdownPreset[]
selectedProjectIds: ReadonlySet<string>
selectedFilters: AnalyticsSelectedFilters
dependentProjectTypesById: ReadonlyMap<string, readonly string[]>
includeDependentProjectTooltipContext: boolean
relevantStats: ReadonlySet<AnalyticsDashboardStat>
projectNamesById: ReadonlyMap<string, string>
userNamesById: ReadonlyMap<string, string>
getVersionDisplayName: (versionId: string) => string
getVersionProjectName: (versionId: string) => string | undefined
showTimeInBucketLabel: boolean
@@ -63,12 +51,8 @@ export function buildAnalyticsTableRows({
timeSlices,
selectedBreakdowns,
selectedProjectIds,
selectedFilters,
dependentProjectTypesById,
includeDependentProjectTooltipContext,
relevantStats,
projectNamesById,
userNamesById,
getVersionDisplayName,
getVersionProjectName,
showTimeInBucketLabel,
@@ -88,7 +72,6 @@ export function buildAnalyticsTableRows({
const projectDisplayValues = new Map<string, string>()
const nextRows = new Map<string, AnalyticsTableRow>()
const bucketLabelsBySliceIndex = new Map<number, { date: string; dateMs: number }>()
const normalizedFilters = normalizeAnalyticsSelectedFilters(selectedFilters)
function getBreakdownDisplayValue(
breakdownValue: string,
@@ -101,7 +84,6 @@ export function buildAnalyticsTableRows({
breakdownValue,
breakdown,
projectNamesById,
userNamesById,
getVersionDisplayName,
formatMessage,
)
@@ -129,15 +111,6 @@ export function buildAnalyticsTableRows({
return displayValue
}
function getProjectVersionIdForBreakdownValues(breakdownValues: readonly string[]) {
const versionBreakdownIndex = selectedBreakdowns.indexOf('version_id')
if (versionBreakdownIndex === -1) {
return ''
}
return breakdownValues[versionBreakdownIndex] ?? ''
}
function getBreakdownDisplays(breakdownValues: readonly string[]) {
const displays: AnalyticsTableBreakdownDisplayValues = {}
@@ -145,19 +118,6 @@ export function buildAnalyticsTableRows({
displays[breakdown] = getBreakdownDisplayValue(breakdownValues[index] ?? '', breakdown)
})
const dependentProjectBreakdownIndex = selectedBreakdowns.indexOf('dependent_project_download')
const downloadReasonBreakdownIndex = selectedBreakdowns.indexOf('download_reason')
if (
dependentProjectBreakdownIndex !== -1 &&
downloadReasonBreakdownIndex !== -1 &&
isUnknownAnalyticsBreakdownValue(breakdownValues[dependentProjectBreakdownIndex])
) {
displays.dependent_project_download = formatAnalyticsDependentProjectFallbackLabel(
breakdownValues[downloadReasonBreakdownIndex],
formatMessage,
)
}
return displays
}
@@ -197,7 +157,6 @@ export function buildAnalyticsTableRows({
function createRow(
rowId: string,
breakdownValues: readonly string[],
dependentOnProjectId?: string,
bucketLabel?: { date: string; dateMs: number },
) {
const breakdownKey =
@@ -210,12 +169,6 @@ export function buildAnalyticsTableRows({
date: bucketLabel?.date ?? '',
dateMs: bucketLabel?.dateMs ?? 0,
project: getProjectDisplayValueForBreakdownValues(breakdownValues),
projectVersionId: getProjectVersionIdForBreakdownValues(breakdownValues),
dependent_on: dependentOnProjectId
? (projectNamesById.get(dependentOnProjectId) ?? dependentOnProjectId)
: '',
dependentOnProjectId: dependentOnProjectId ?? '',
dependentOnProjectIds: dependentOnProjectId ? [dependentOnProjectId] : [],
breakdown: breakdownKey,
breakdownValues: Object.fromEntries(
selectedBreakdowns.map((breakdown, index) => [breakdown, breakdownValues[index] ?? '']),
@@ -237,18 +190,6 @@ export function buildAnalyticsTableRows({
return row
}
function addDependentOnProjectIdToRow(row: AnalyticsTableRow, projectId: string | undefined) {
if (!projectId || row.dependentOnProjectIds.includes(projectId)) {
return
}
row.dependentOnProjectIds.push(projectId)
if (!row.dependentOnProjectId) {
row.dependentOnProjectId = projectId
row.dependent_on = projectNamesById.get(projectId) ?? projectId
}
}
if (!includeDate && selectedBreakdowns.length === 0) {
createRow(ALL_PROJECTS_BREAKDOWN_VALUE, [])
}
@@ -270,15 +211,6 @@ export function buildAnalyticsTableRows({
if (!selectedProjectIds.has(point.source_project)) {
continue
}
if (
!doesAnalyticsPointMatchNormalizedFilters(
point,
normalizedFilters,
dependentProjectTypesById,
)
) {
continue
}
const pointStat = getAnalyticsTableStatForMetric(point.metric_kind)
if (!pointStat || !relevantStats.has(pointStat)) {
@@ -294,21 +226,12 @@ export function buildAnalyticsTableRows({
}
const nextBucketLabel = includeDate ? (bucketLabel ?? getBucketLabel(sliceIndex)) : undefined
const dependentOnProjectId = includeDependentProjectTooltipContext
? point.source_project
: undefined
const dependentTooltipProjectId = selectedBreakdowns.includes('dependent_project_download')
? point.source_project
: undefined
const breakdownKey =
breakdownValues.length === 0
? ALL_PROJECTS_BREAKDOWN_VALUE
: getAnalyticsBreakdownKey(breakdownValues)
const rowId = includeDate ? `${nextBucketLabel?.dateMs ?? 0}::${breakdownKey}` : breakdownKey
const row =
nextRows.get(rowId) ??
createRow(rowId, breakdownValues, dependentOnProjectId, nextBucketLabel)
addDependentOnProjectIdToRow(row, dependentTooltipProjectId)
const row = nextRows.get(rowId) ?? createRow(rowId, breakdownValues, nextBucketLabel)
addAnalyticsMetricToTableRow(row, point)
}
})
@@ -369,21 +292,11 @@ function formatAnalyticsTableBreakdownDisplayValue(
value: string,
breakdown: AnalyticsTableBreakdownPreset,
projectNamesById: ReadonlyMap<string, string>,
userNamesById: ReadonlyMap<string, string>,
getVersionDisplayName: (versionId: string) => string,
formatMessage: FormatMessage,
): string {
if (breakdown === 'project' || breakdown === 'dependent_project_download') {
if (breakdown === 'dependent_project_download') {
if (isNoDependentAnalyticsBreakdownValue(value)) {
return formatMessage(analyticsMessages.noDependent)
}
if (isUnknownAnalyticsBreakdownValue(value)) {
return formatMessage(analyticsMessages.unknown)
}
}
if (breakdown === 'project') {
return projectNamesById.get(value) ?? value
}
return formatBreakdownLabel(value, breakdown, getVersionDisplayName, userNamesById, formatMessage)
return formatBreakdownLabel(value, breakdown, getVersionDisplayName, formatMessage)
}
@@ -3,7 +3,7 @@ import type { TableColumn } from '@modrinth/ui'
import { isAnalyticsTableBreakdownColumnKey } from './analytics-table-columns'
import type { AnalyticsTableColumnKey, AnalyticsTableRow } from './analytics-table-types'
const SEARCHABLE_COLUMN_KEYS = new Set<AnalyticsTableColumnKey>(['date', 'project', 'dependent_on'])
const SEARCHABLE_COLUMN_KEYS = new Set<AnalyticsTableColumnKey>(['date', 'project'])
export function getAnalyticsTableSearchableColumns(
columns: TableColumn<AnalyticsTableColumnKey>[],
@@ -39,8 +39,6 @@ function getAnalyticsTableSearchableCellValue(
return row.date
case 'project':
return row.project
case 'dependent_on':
return row.dependent_on
case 'breakdown':
return row.breakdownDisplay
default:
@@ -129,15 +129,6 @@ function getAnalyticsTableRowComparator(
directionFactor,
sortCollator,
)
case 'dependent_on':
return (left, right) =>
compareAnalyticsTableRows(
left,
right,
sortCollator.compare(left.dependent_on, right.dependent_on),
directionFactor,
sortCollator,
)
case 'breakdown':
return (left, right) =>
compareAnalyticsTableRows(
@@ -18,15 +18,11 @@ export type AnalyticsTableSortState = {
export type AnalyticsTableSortDirectionValue = AnalyticsTableSortDirection
export type AnalyticsTableRow = {
[key: string]: string | number | string[] | AnalyticsTableBreakdownDisplayValues
[key: string]: string | number | AnalyticsTableBreakdownDisplayValues
id: string
date: string
dateMs: number
project: string
projectVersionId: string
dependent_on: string
dependentOnProjectId: string
dependentOnProjectIds: string[]
breakdown: string
breakdownValues: AnalyticsTableBreakdownDisplayValues
breakdownDisplays: AnalyticsTableBreakdownDisplayValues
@@ -55,92 +55,32 @@
<template #cell-date="{ value }">
<span class="text-primary">{{ value }}</span>
</template>
<template #cell-breakdown_project="{ row, value }">
<ProjectCell
:label="getProjectCellLabel(value)"
:icon-url="getProjectIconUrl(row.breakdownValues.project)"
:icon-tooltip="getProjectCellLabel(value)"
:label-href="getProjectPageHref(row.breakdownValues.project)"
:organization-href="getProjectOrganizationPageHref(row.breakdownValues.project)"
:organization-tooltip="getProjectOrganizationName(row.breakdownValues.project)"
/>
<template #cell-breakdown_project="{ value }">
<span class="text-primary">{{ value }}</span>
</template>
<template #cell-breakdown_country="{ value }">
<span class="mr-2.5 text-primary">{{ value }}</span>
<span class="text-primary">{{ value }}</span>
</template>
<template #cell-breakdown_monetization="{ value }">
<span class="mr-2.5 text-primary">{{ value }}</span>
<span class="text-primary">{{ value }}</span>
</template>
<template #cell-breakdown_user_agent="{ value }">
<span class="mr-2.5 text-primary">{{ value }}</span>
<span class="text-primary">{{ value }}</span>
</template>
<template #cell-breakdown_download_reason="{ value }">
<span class="mr-2.5 text-primary">{{ value }}</span>
<span class="text-primary">{{ value }}</span>
</template>
<template #cell-breakdown_user_id="{ row, value }">
<component
:is="getUserPageHref(row.breakdownValues.user_id) ? 'a' : 'span'"
:href="getUserPageHref(row.breakdownValues.user_id)"
:target="getUserPageHref(row.breakdownValues.user_id) ? '_blank' : undefined"
:rel="getUserPageHref(row.breakdownValues.user_id) ? 'noopener noreferrer' : undefined"
class="mr-2.5 flex min-w-0 items-center gap-2 text-primary"
:class="{ 'hover:underline': getUserPageHref(row.breakdownValues.user_id) }"
>
<span
v-tooltip="getUserCellLabel(value)"
class="flex size-6 shrink-0 items-center justify-center overflow-hidden rounded-full text-primary"
>
<img
v-if="getUserAvatarUrl(row.breakdownValues.user_id)"
:src="getUserAvatarUrl(row.breakdownValues.user_id)"
:alt="getUserCellLabel(value)"
class="h-6 w-6 rounded-full object-cover"
/>
<UserIcon v-else class="h-full w-full" />
</span>
<span class="min-w-0 truncate font-semibold leading-tight text-primary">
{{ value }}
</span>
</component>
</template>
<template #cell-breakdown_dependent_project_download="{ row, value }">
<ProjectCell
:label="getProjectCellLabel(value)"
:icon-url="
isMissingDependentProjectValue(row.breakdownValues.dependent_project_download)
? undefined
: getProjectIconUrl(row.breakdownValues.dependent_project_download)
"
:icon-tooltip="getProjectCellLabel(value)"
:hide-icon="
isMissingDependentProjectValue(row.breakdownValues.dependent_project_download)
"
:label-href="
isMissingDependentProjectValue(row.breakdownValues.dependent_project_download)
? undefined
: getProjectPageHref(row.breakdownValues.dependent_project_download)
"
:label-tooltip="getDependentProjectTooltip(row)"
/>
</template>
<template #cell-breakdown_version_id="{ row, value }">
<component
:is="getVersionPageHref(row.projectVersionId) ? 'a' : 'span'"
v-tooltip="getVersionProjectName(row.projectVersionId)"
:href="getVersionPageHref(row.projectVersionId)"
:target="getVersionPageHref(row.projectVersionId) ? '_blank' : undefined"
:rel="getVersionPageHref(row.projectVersionId) ? 'noopener noreferrer' : undefined"
class="mr-2.5 text-primary"
:class="{ 'hover:underline': getVersionPageHref(row.projectVersionId) }"
>
{{ value }}
</component>
<template #cell-breakdown_version_id="{ value }">
<span class="text-primary">{{ value }}</span>
</template>
<template #cell-breakdown_loader="{ value }">
<span class="mr-2.5 text-primary">{{ value }}</span>
<span class="text-primary">{{ value }}</span>
</template>
<template #cell-breakdown_game_version="{ value }">
<span class="mr-2.5 text-primary">{{ value }}</span>
<span class="text-primary">{{ value }}</span>
</template>
<template #cell-project="{ value }">
<span class="text-primary">{{ value }}</span>
</template>
<template #cell-views="{ row }">
<span>{{ formatInteger(row.views) }}</span>
@@ -190,7 +130,7 @@
</template>
<script setup lang="ts">
import { DownloadIcon, DropdownIcon, SearchIcon, UserIcon } from '@modrinth/assets'
import { DownloadIcon, DropdownIcon, SearchIcon } from '@modrinth/assets'
import {
ButtonStyled,
OverflowMenu,
@@ -219,15 +159,10 @@ import {
} from '../analytics-chart/analytics-chart-utils.ts'
import {
analyticsBreakdownMessages,
analyticsChartMessages,
analyticsMessages,
analyticsTableMessages,
} from '../analytics-messages.ts'
import AnalyticsLoadingBar from '../AnalyticsLoadingBar.vue'
import {
isNoDependentAnalyticsBreakdownValue,
isUnknownAnalyticsBreakdownValue,
} from '../breakdown.ts'
import {
buildAnalyticsTableColumns,
getAnalyticsTableBreakdownColumnLabel,
@@ -259,10 +194,8 @@ import { sortAnalyticsTableRows } from './analytics-table-sorting.ts'
import type {
AnalyticsTableColumnKey,
AnalyticsTableMode,
AnalyticsTableRow,
AnalyticsTableSortDirectionValue,
} from './analytics-table-types.ts'
import ProjectCell from './ProjectCell.vue'
import { useAnalyticsTableGraphSelection } from './use-analytics-table-graph-selection.ts'
import { useAnalyticsTablePagination } from './use-analytics-table-pagination.ts'
import { useAnalyticsTableRowCache } from './use-analytics-table-row-cache.ts'
@@ -288,15 +221,7 @@ const {
getRelevantAnalyticsDashboardStats,
isLoading,
versionNumbersById,
versionProjectIdsById,
versionProjectNamesById,
projectNamesById,
projectIconUrlsById,
projectOrganizationIdsById,
projectOrganizationNamesById,
userNamesById,
userAvatarUrlsById,
dependentProjectTypesById,
getVersionDisplayName,
getVersionProjectName,
} = injectAnalyticsDashboardContext()
@@ -338,10 +263,11 @@ const showGraphDatasetSelection = computed(() =>
? selectedProjectIdSet.value.size > 1
: selectedBreakdowns.value.length > 0,
)
const includeDependentProjectTooltipContext = computed(
const showProjectVersionProjectColumn = computed(
() =>
selectedBreakdownSet.value.has('dependent_project_download') &&
!selectedBreakdownSet.value.has('project'),
selectedBreakdownSet.value.has('version_id') &&
!selectedBreakdownSet.value.has('project') &&
selectedProjectIdSet.value.size > 1,
)
const includeDateColumn = computed(
() =>
@@ -388,6 +314,9 @@ const csvExportOptions = computed<OverflowMenuOption[]>(() => {
},
]
})
const projectNamesById = computed(
() => new Map(projects.value.map((project) => [project.id, project.name])),
)
const hasAvailableProjects = computed(() => projects.value.length > 0)
const analyticsPointCount = computed(() =>
timeSlices.value.reduce((sum, slice) => sum + slice.length, 0),
@@ -431,12 +360,8 @@ function buildTableRows(mode: AnalyticsTableMode) {
timeSlices: timeSlices.value,
selectedBreakdowns: selectedBreakdowns.value,
selectedProjectIds: selectedProjectIdSet.value,
selectedFilters: selectedFilters.value,
dependentProjectTypesById: dependentProjectTypesById.value,
includeDependentProjectTooltipContext: includeDependentProjectTooltipContext.value,
relevantStats: relevantStats.value,
projectNamesById: projectNamesById.value,
userNamesById: userNamesById.value,
getVersionDisplayName,
getVersionProjectName,
showTimeInBucketLabel: showTimeInBucketLabel.value,
@@ -454,86 +379,12 @@ function buildColumns(includeDate: boolean) {
selectedBreakdowns: selectedBreakdowns.value,
selectedFilters: selectedFilters.value,
showBreakdownColumn: showBreakdownColumn.value,
showProjectVersionProjectColumn: showProjectVersionProjectColumn.value,
formatMessage,
getRelevantAnalyticsDashboardStats,
})
}
function getProjectIconUrl(projectId: string | undefined) {
return projectId ? projectIconUrlsById.value.get(projectId) : undefined
}
function getProjectOrganizationName(projectId: string | undefined) {
return projectId ? projectOrganizationNamesById.value.get(projectId) : undefined
}
function getProjectCellLabel(value: unknown) {
return typeof value === 'string' ? value : String(value ?? '')
}
function getUserAvatarUrl(userId: string | undefined) {
return userId ? userAvatarUrlsById.value.get(userId) : undefined
}
function getUserCellLabel(value: unknown) {
return typeof value === 'string' ? value : String(value ?? '')
}
function getUserPageHref(userId: string | undefined) {
if (!userId) return undefined
const username = userNamesById.value.get(userId) ?? userId
return `/user/${encodeURIComponent(username)}`
}
function getProjectPageHref(projectId: string | undefined) {
return projectId ? `/project/${encodeURIComponent(projectId)}` : undefined
}
function getProjectOrganizationPageHref(projectId: string | undefined) {
if (!projectId) return undefined
const organizationId = projectOrganizationIdsById.value.get(projectId)
if (!organizationId) return undefined
return `/organization/${encodeURIComponent(organizationId)}`
}
function getVersionPageHref(versionId: string | undefined) {
if (!versionId) return undefined
const projectId = versionProjectIdsById.value.get(versionId)
if (!projectId) return undefined
return `/project/${encodeURIComponent(projectId)}/version/${encodeURIComponent(versionId)}`
}
function isMissingDependentProjectValue(value: string | undefined) {
return isUnknownAnalyticsBreakdownValue(value) || isNoDependentAnalyticsBreakdownValue(value)
}
function getDependentProjectTooltip(row: AnalyticsTableRow) {
if (isNoDependentAnalyticsBreakdownValue(row.breakdownValues.dependent_project_download)) {
return formatMessage(analyticsMessages.noDependentTooltip)
}
if (isUnknownAnalyticsBreakdownValue(row.breakdownValues.dependent_project_download)) {
return formatMessage(analyticsMessages.unknown)
}
const dependencyProjectIds = new Set(row.dependentOnProjectIds)
if (row.dependentOnProjectId) {
dependencyProjectIds.add(row.dependentOnProjectId)
}
const dependencyProjectNames = [...dependencyProjectIds]
.map((projectId) => projectNamesById.value.get(projectId) ?? projectId)
.sort((left, right) => left.localeCompare(right))
return dependencyProjectNames.length > 0
? formatMessage(analyticsChartMessages.dependentOnProjectTooltip, {
project: dependencyProjectNames.join(', '),
})
: undefined
}
watch(
activeColumns,
(nextColumns) => {
@@ -594,9 +445,6 @@ watch(
selectedBreakdowns,
selectedFilters,
projects,
dependentProjectTypesById,
projectNamesById,
userNamesById,
versionNumbersById,
versionProjectNamesById,
],
@@ -7,10 +7,6 @@ import type {
AnalyticsSelectedBreakdowns,
} from '~/providers/analytics/analytics'
import {
isNoDependentAnalyticsBreakdownValue,
isUnknownAnalyticsBreakdownValue,
} from '../breakdown'
import { getAnalyticsTableMetricSortedGraphDatasetIds } from './analytics-table-sorting'
import type { AnalyticsTableColumnKey, AnalyticsTableRow } from './analytics-table-types'
@@ -62,9 +58,6 @@ export function useAnalyticsTableGraphSelection({
const filteredSelectableGraphDatasetIds = computed(() =>
getAnalyticsTableSelectableGraphDatasetIds(filteredRows.value),
)
const excludedGraphDatasetIds = computed(() =>
getAnalyticsTableExcludedGraphDatasetIds(sortedRows.value),
)
const sortedMetricGraphDatasetIds = computed(() =>
getAnalyticsTableMetricSortedGraphDatasetIds(sortedRows.value, sortColumn.value, sortCollator),
)
@@ -72,9 +65,7 @@ export function useAnalyticsTableGraphSelection({
const sortedMetricIds = sortedMetricGraphDatasetIds.value
const defaultIds =
sortedMetricIds.length > 0 ? sortedMetricIds : selectableGraphDatasetIds.value
return defaultIds
.filter((id) => !excludedGraphDatasetIds.value.has(id))
.slice(0, graphDatasetSelectionLimit)
return defaultIds.slice(0, graphDatasetSelectionLimit)
})
const tableSelectedGraphDatasetIds = computed<unknown[]>({
get: () => selectedGraphDatasetIds.value,
@@ -158,9 +149,7 @@ export function useAnalyticsTableGraphSelection({
defaultGraphDatasetIds.value = nextShowGraphDatasetSelection
? [...nextDefaultGraphDatasetIds]
: []
topGraphDatasetIds.value = nextShowGraphDatasetSelection
? nextTopGraphDatasetIds.filter((id) => !excludedGraphDatasetIds.value.has(id))
: []
topGraphDatasetIds.value = nextShowGraphDatasetSelection ? [...nextTopGraphDatasetIds] : []
},
{ immediate: true },
)
@@ -195,20 +184,6 @@ export function useAnalyticsTableGraphSelection({
return Array.from(new Set(rows.map((row) => row.graphDatasetId)))
}
function getAnalyticsTableExcludedGraphDatasetIds(rows: AnalyticsTableRow[]): Set<string> {
return new Set(
rows
.filter((row) =>
Object.values(row.breakdownValues).some(
(value) =>
isUnknownAnalyticsBreakdownValue(value) ||
isNoDependentAnalyticsBreakdownValue(value),
),
)
.map((row) => row.graphDatasetId),
)
}
return {
filteredSelectableGraphDatasetIds,
tableSelectedGraphDatasetIds,
@@ -6,7 +6,6 @@ import { formatAnalyticsDownloadSourceLabel, type FormatMessage } from './analyt
export const ALL_BREAKDOWN_VALUE = '__all__'
export const UNKNOWN_BREAKDOWN_VALUE = '__unknown__'
export const NO_DEPENDENT_BREAKDOWN_VALUE = '__no_dependent__'
export const COMBINED_BREAKDOWN_LABEL_SEPARATOR = ' + '
export const COMBINED_BREAKDOWN_DATASET_ID_PREFIX = 'breakdowns:'
@@ -42,19 +41,6 @@ export function getAnalyticsBreakdownValue(
'reason' in point ? point.reason : undefined,
UNKNOWN_BREAKDOWN_VALUE,
)
case 'user_id':
return normalizeBreakdownValue('user_id' in point ? point.user_id : undefined)
case 'dependent_project_download': {
const dependentProjectId = normalizeBreakdownValue(
'dependent_project_id' in point ? point.dependent_project_id : undefined,
UNKNOWN_BREAKDOWN_VALUE,
)
const downloadReason = 'reason' in point ? point.reason?.trim().toLowerCase() : undefined
return dependentProjectId === UNKNOWN_BREAKDOWN_VALUE &&
(downloadReason === 'standalone' || downloadReason === 'update')
? NO_DEPENDENT_BREAKDOWN_VALUE
: dependentProjectId
}
case 'version_id':
return normalizeBreakdownValue('version_id' in point ? point.version_id : undefined)
case 'loader':
@@ -108,30 +94,16 @@ export function getDownloadSourceLabel(value: string, formatMessage: FormatMessa
return formatAnalyticsDownloadSourceLabel(value, formatMessage)
}
export function isUnknownAnalyticsBreakdownValue(value: string | null | undefined): boolean {
const normalized = value?.trim()
if (!normalized) {
return false
}
const normalizedLowercase = normalized.toLowerCase()
return (
normalized === UNKNOWN_BREAKDOWN_VALUE ||
normalizedLowercase === 'unknown' ||
normalizedLowercase === 'other'
)
}
export function isNoDependentAnalyticsBreakdownValue(value: string | null | undefined): boolean {
return value?.trim() === NO_DEPENDENT_BREAKDOWN_VALUE
}
function normalizeBreakdownValue(
value: string | undefined,
fallback = ALL_BREAKDOWN_VALUE,
): string {
const normalized = value?.trim()
if (fallback === UNKNOWN_BREAKDOWN_VALUE && isUnknownAnalyticsBreakdownValue(normalized)) {
const normalizedLowercase = normalized?.toLowerCase()
if (
fallback === UNKNOWN_BREAKDOWN_VALUE &&
(normalizedLowercase === 'unknown' || normalizedLowercase === 'other')
) {
return fallback
}
return normalized && normalized.length > 0 ? normalized : fallback
@@ -25,25 +25,10 @@
<template #option="{ category, option, selected }">
<div class="flex min-w-0 flex-1 items-center gap-2">
<span
v-if="category.key === 'user_id'"
v-tooltip="option.label"
class="flex size-6 shrink-0 items-center justify-center overflow-hidden rounded-full text-primary"
:class="selected ? 'text-contrast' : 'text-primary'"
>
<img
v-if="getUserAvatarUrl(option.value)"
:src="getUserAvatarUrl(option.value)"
:alt="option.label"
class="h-6 w-6 rounded-full object-cover"
/>
<UserIcon v-else class="h-full w-full" />
</span>
<template
v-for="metadata in getFilterOptionProjectMetadata(category.key, option.value)"
:key="`${category.key}-${option.value}-${metadata.name}`"
>
<template v-if="category.key === 'version_id'">
<span
v-for="metadata in getProjectVersionOptionProjectMetadata(option.value)"
:key="`${option.value}-${metadata.name}`"
v-tooltip="metadata.name"
class="flex size-6 shrink-0 items-center justify-center overflow-hidden rounded text-primary"
>
@@ -57,8 +42,6 @@
</span>
</template>
<span
:ref="(element) => setFilterOptionLabelRef(category.key, option.value, element)"
v-tooltip="getFilterOptionLabelTooltip(category.key, option.value, option.label)"
class="min-w-0 truncate font-semibold leading-tight"
:class="selected ? 'text-contrast' : 'text-primary'"
>
@@ -196,24 +179,16 @@
</template>
<script setup lang="ts">
import { BoxIcon, UserIcon } from '@modrinth/assets'
import { BoxIcon } from '@modrinth/assets'
import {
buildDependentsSearchFilters,
DropdownFilterBar,
type DropdownFilterBarCategory,
type DropdownFilterBarOption,
injectModrinthClient,
injectNotificationManager,
type ProjectType,
Tabs,
type TabsTab,
type TabsValue,
truncatedTooltip,
useVIntl,
} from '@modrinth/ui'
import { formatProjectType } from '@modrinth/utils'
import { useQuery } from '@tanstack/vue-query'
import type { ComponentPublicInstance } from 'vue'
import { useFormattedCountries } from '@/composables/country.ts'
import {
@@ -283,8 +258,6 @@ const props = withDefaults(
)
const { formatMessage } = useVIntl()
const client = injectModrinthClient()
const { addNotification } = injectNotificationManager()
const {
hasProjectContext,
projects,
@@ -304,9 +277,6 @@ const {
versionPublishedDatesById,
versionProjectNamesById,
versionProjectIconUrlsById,
projectNamesById,
userNamesById,
userAvatarUrlsById,
getVersionDisplayName,
} = injectAnalyticsDashboardContext()
const formattedCountries = useFormattedCountries()
@@ -320,17 +290,6 @@ const gameVersionTypeTabs = computed<TabsTab[]>(() => [
{ value: 'release', label: formatMessage(analyticsMessages.releaseTab) },
{ value: 'all', label: formatMessage(analyticsMessages.allTab) },
])
const dependentProjectSearchProjectTypes: readonly ProjectType[] = [
'mod',
'modpack',
'resourcepack',
'shader',
'datapack',
'plugin',
]
const dependentProjectInitialSearchLimit = 100
const dependentProjectQuerySearchLimit = 500
const dependentProjectSearchDebounceMs = 250
const resolvedAddLabel = computed(
() => props.addLabel ?? formatMessage(analyticsMessages.addButton),
)
@@ -347,16 +306,14 @@ const projectStatusFilterOptions = computed<DropdownFilterBarOption[]>(() =>
})),
)
const selectedProjectIdSet = computed(() => new Set(selectedProjectIds.value))
const effectiveSelectedProjectIds = computed(() =>
projects.value
.filter(
const effectiveSelectedProjectCount = computed(
() =>
projects.value.filter(
(project) =>
selectedProjectIdSet.value.has(project.id) &&
doesProjectStatusMatchFilters(project.status, selectedFilters.value),
)
.map((project) => project.id),
).length,
)
const effectiveSelectedProjectCount = computed(() => effectiveSelectedProjectIds.value.length)
const showProjectVersionProjectIcons = computed(() => effectiveSelectedProjectCount.value > 1)
const defaultSelectedBreakdown = computed(() =>
getDefaultAnalyticsBreakdownPresets(selectedProjectIds.value),
@@ -373,72 +330,12 @@ const projectVersionFilterOptions = shallowRef<ProjectVersionFilterOption[]>([])
const projectVersionFilterOptionProjectMetadataById = shallowRef(
new Map<string, ProjectVersionFilterOptionProjectMetadata[]>(),
)
const dependentProjectSearchInput = ref('')
const dependentProjectSearchQuery = ref('')
const draftSelectedFilters = ref<AnalyticsSelectedFilters>(
cloneSelectedFilters(selectedFilters.value),
)
let selectedFiltersCommitRequestId = 0
let projectVersionFilterOptionsCacheKey = ''
let projectVersionFilterOptionProjectMetadataCacheKey = ''
let dependentProjectSearchDebounceTimeout: ReturnType<typeof setTimeout> | null = null
const filterOptionLabelElements = new Map<string, HTMLElement>()
const filterOptionLabelRefUpdateToken = ref(0)
const dependentProjectSearchFilters = computed(() =>
buildDependentsSearchFilters(
dependentProjectSearchProjectTypes,
effectiveSelectedProjectIds.value,
),
)
const dependentProjectSearchQueryValue = computed(() => dependentProjectSearchQuery.value.trim())
const dependentProjectSearchResultLimit = computed(() =>
dependentProjectSearchQueryValue.value.length > 0
? dependentProjectQuerySearchLimit
: dependentProjectInitialSearchLimit,
)
const selectedDependentProjectIds = computed(() => [
...new Set([
...selectedFilters.value.dependent_project_id,
...draftSelectedFilters.value.dependent_project_id,
]),
])
const { data: dependentProjectSearchResults, error: dependentProjectSearchError } = useQuery({
queryKey: computed(() => [
'analytics',
'query-filter',
'dependent-project-search',
dependentProjectSearchFilters.value,
dependentProjectSearchQueryValue.value,
dependentProjectSearchResultLimit.value,
]),
queryFn: () =>
client.labrinth.projects_v2.search({
query: dependentProjectSearchQueryValue.value || undefined,
new_filters: dependentProjectSearchFilters.value,
limit: dependentProjectSearchResultLimit.value,
index: 'downloads',
}),
enabled: computed(
() =>
effectiveSelectedProjectIds.value.length > 0 &&
dependentProjectSearchFilters.value.length > 0,
),
placeholderData: (previousData) => previousData,
refetchOnWindowFocus: false,
})
const { data: selectedDependentProjects } = useQuery({
queryKey: computed(() => [
'analytics',
'query-filter',
'selected-dependent-projects',
selectedDependentProjectIds.value,
]),
queryFn: () => client.labrinth.projects_v2.getMultiple(selectedDependentProjectIds.value),
enabled: computed(() => selectedDependentProjectIds.value.length > 0),
placeholderData: [],
refetchOnWindowFocus: false,
})
const selectedFilterValue = computed<Record<string, string[]>>({
get: () => getSelectedFilterBarValue(),
@@ -477,27 +374,6 @@ watch(queryResetToken, () => {
clearDownloadsThresholds()
})
watch(dependentProjectSearchInput, (query) => {
if (dependentProjectSearchDebounceTimeout) {
clearTimeout(dependentProjectSearchDebounceTimeout)
}
dependentProjectSearchDebounceTimeout = setTimeout(() => {
dependentProjectSearchQuery.value = query.trim()
dependentProjectSearchDebounceTimeout = null
}, dependentProjectSearchDebounceMs)
})
watch(dependentProjectSearchError, (error) => {
if (!error) return
addNotification({
title: 'Dependent projects failed to load',
text: getDependentProjectSearchErrorMessage(error),
type: 'error',
})
})
watch(
selectedFilters,
(nextFilters, previousFilters) => {
@@ -599,12 +475,6 @@ watch(
{ immediate: true },
)
onBeforeUnmount(() => {
if (dependentProjectSearchDebounceTimeout) {
clearTimeout(dependentProjectSearchDebounceTimeout)
}
})
async function scheduleSelectedFiltersCommit() {
const requestId = ++selectedFiltersCommitRequestId
const nextFilters = cloneSelectedFilters(draftSelectedFilters.value)
@@ -707,40 +577,6 @@ const filterCategories = computed<DropdownFilterBarCategory[]>(() => {
label: formatMessage(analyticsBreakdownMessages.loader),
options: withSelectedOptions('loader_type', loaderTypeFilterOptions.value),
},
{
key: 'dependent_project_id',
label: formatMessage(analyticsBreakdownMessages.dependentProjectDownload),
searchable: true,
disableLocalOptionsFilter: true,
searchPlaceholder: formatMessage(analyticsMessages.searchDependentProjectsPlaceholder),
emptyOptionsLabel: analyticsFilterOptionsEmptyLabel.value,
emptySearchLabel: analyticsFilterOptionsEmptyLabel.value,
onSearchQueryChange: setDependentProjectSearchInput,
options: withSelectedOptions('dependent_project_id', dependentProjectFilterOptions.value),
submenuClass: 'w-fit min-w-[18rem]',
previewDropdownWidth: 'fit-content',
previewDropdownMinWidth: '18rem',
},
{
key: 'dependent_project_type',
label: formatMessage(analyticsBreakdownMessages.dependentProjectType),
options: withSelectedOptions(
'dependent_project_type',
dependentProjectTypeFilterOptions.value,
),
},
{
key: 'user_id',
label: formatMessage(analyticsBreakdownMessages.members),
searchable: memberFilterOptions.value.length > 6,
searchPlaceholder: formatMessage(analyticsMessages.searchMembersPlaceholder),
emptyOptionsLabel: analyticsFilterOptionsEmptyLabel.value,
emptySearchLabel: analyticsFilterOptionsEmptyLabel.value,
options: withSelectedOptions('user_id', memberFilterOptions.value),
submenuClass: 'w-fit min-w-[14rem]',
previewDropdownWidth: 'fit-content',
previewDropdownMinWidth: '14rem',
},
)
return categories.filter((category) =>
@@ -800,16 +636,6 @@ const downloadReasonFilterOptions = computed<DropdownFilterBarOption[]>(() =>
})),
)
const memberFilterOptions = computed<DropdownFilterBarOption[]>(() =>
filterOptions.value.userIds
.map((userId) => ({
value: userId,
label: getUserFilterOptionLabel(userId),
searchTerms: [userId],
}))
.sort((left, right) => left.label.localeCompare(right.label)),
)
const gameVersionFilterOptions = computed<DropdownFilterBarOption[]>(() =>
filterOptions.value.gameVersions
.filter((gameVersion) => {
@@ -842,45 +668,6 @@ const loaderTypeFilterOptions = computed<DropdownFilterBarOption[]>(() =>
.sort((left, right) => left.label.localeCompare(right.label)),
)
const dependentProjectSearchResultsById = computed(
() =>
new Map(
(dependentProjectSearchResults.value?.hits ?? []).map((project) => [
project.project_id,
project,
]),
),
)
const selectedDependentProjectsById = computed(
() => new Map((selectedDependentProjects.value ?? []).map((project) => [project.id, project])),
)
const dependentProjectFilterOptions = computed<DropdownFilterBarOption[]>(() => {
const optionsById = new Map<string, DropdownFilterBarOption>()
for (const project of dependentProjectSearchResults.value?.hits ?? []) {
optionsById.set(project.project_id, {
value: project.project_id,
label: project.title,
searchTerms: [project.project_id, project.slug, project.author].filter(
(term): term is string => Boolean(term),
),
})
}
return [...optionsById.values()]
})
const dependentProjectTypeFilterOptions = computed<DropdownFilterBarOption[]>(() =>
filterOptions.value.dependentProjectTypes
.map((projectType) => ({
value: projectType,
label: getProjectTypeFilterOptionLabel(projectType),
searchTerms: [projectType],
}))
.sort((left, right) => left.label.localeCompare(right.label)),
)
function isAnalyticsFilterValueCategory(
categoryKey: string,
): categoryKey is AnalyticsFilterValueCategory {
@@ -910,77 +697,15 @@ function getMissingSelectedOptionLabel(
if (categoryKey === 'download_reason') {
return getDownloadReasonFilterOptionLabel
}
if (categoryKey === 'user_id') {
return getUserFilterOptionLabel
}
if (categoryKey === 'user_agent') {
return (value) => getDownloadSourceLabel(value, formatMessage)
}
if (categoryKey === 'loader_type') {
return getLoaderTypeFilterOptionLabel
}
if (categoryKey === 'dependent_project_id') {
return getDependentProjectFilterOptionLabel
}
if (categoryKey === 'dependent_project_type') {
return getProjectTypeFilterOptionLabel
}
return undefined
}
function getUserAvatarUrl(userId: string): string | undefined {
return userAvatarUrlsById.value.get(userId)
}
function setFilterOptionLabelRef(
categoryKey: string,
optionValue: string,
element: Element | ComponentPublicInstance | null,
) {
const key = getFilterOptionLabelElementKey(categoryKey, optionValue)
if (element instanceof HTMLElement) {
if (filterOptionLabelElements.get(key) === element) {
return
}
filterOptionLabelElements.set(key, element)
filterOptionLabelRefUpdateToken.value++
return
}
if (filterOptionLabelElements.delete(key)) {
filterOptionLabelRefUpdateToken.value++
}
}
function getFilterOptionLabelTooltip(
categoryKey: string,
optionValue: string,
label: string,
): string | undefined {
void filterOptionLabelRefUpdateToken.value
return truncatedTooltip(
filterOptionLabelElements.get(getFilterOptionLabelElementKey(categoryKey, optionValue)),
label,
)
}
function getFilterOptionLabelElementKey(categoryKey: string, optionValue: string) {
return `${categoryKey}\x1f${optionValue}`
}
function getFilterOptionProjectMetadata(categoryKey: string, optionValue: string) {
if (categoryKey === 'version_id') {
return getProjectVersionOptionProjectMetadata(optionValue)
}
if (categoryKey === 'dependent_project_id') {
return getDependentProjectOptionProjectMetadata(optionValue)
}
return []
}
function getProjectVersionOptionProjectMetadata(versionId: string) {
if (!showProjectVersionProjectIcons.value) {
return []
@@ -989,24 +714,6 @@ function getProjectVersionOptionProjectMetadata(versionId: string) {
return projectVersionFilterOptionProjectMetadataById.value.get(versionId) ?? []
}
function getDependentProjectOptionProjectMetadata(projectId: string) {
const searchResult = dependentProjectSearchResultsById.value.get(projectId)
const selectedProject = selectedDependentProjectsById.value.get(projectId)
const name =
searchResult?.title ??
selectedProject?.title ??
projectNamesById.value.get(projectId) ??
projectId
const iconUrl = searchResult?.icon_url ?? selectedProject?.icon_url
return [
{
name,
...(iconUrl ? { iconUrl } : {}),
},
]
}
function getCountryFilterOptionLabel(countryCode: string): string {
const normalizedCode = countryCode.trim().toUpperCase()
if (normalizedCode === 'XX') {
@@ -1024,51 +731,10 @@ function getLoaderTypeFilterOptionLabel(loaderType: string): string {
return formatAnalyticsLoaderLabel(loaderType, formatMessage)
}
function getProjectTypeFilterOptionLabel(projectType: string): string {
return formatProjectType(projectType)
}
function getDependentProjectFilterOptionLabel(projectId: string): string {
return (
dependentProjectSearchResultsById.value.get(projectId)?.title ??
selectedDependentProjectsById.value.get(projectId)?.title ??
projectNamesById.value.get(projectId) ??
projectId
)
}
function getDownloadReasonFilterOptionLabel(reason: string): string {
return formatAnalyticsDownloadReasonLabel(reason, formatMessage)
}
function getUserFilterOptionLabel(userId: string): string {
return userNamesById.value.get(userId) ?? userId
}
function setDependentProjectSearchInput(query: string) {
dependentProjectSearchInput.value = query
}
function getDependentProjectSearchErrorMessage(error: unknown): string {
if (error && typeof error === 'object') {
const dataDescription = (error as { data?: { description?: unknown } }).data?.description
if (typeof dataDescription === 'string' && dataDescription.length > 0) {
return dataDescription
}
const message = (error as { message?: unknown }).message
if (typeof message === 'string' && message.length > 0) {
return message
}
}
if (typeof error === 'string' && error.length > 0) {
return error
}
return 'Please try searching again or changing the selected projects.'
}
function getDateTimestamp(date: string | undefined): number | undefined {
if (!date) {
return undefined
@@ -12,7 +12,6 @@
:placeholder="formatMessage(analyticsMessages.selectProjects)"
:no-options-message="noProjectsMessage"
:searchable="projectOptions.length > 6"
fuzzy-search
:max-tag-rows="1"
:trigger-class="analyticsQueryChipTriggerClass"
fit-content
@@ -80,7 +79,7 @@
@keydown.enter.stop
@keydown.space.stop
>
<UserIcon
<LayersIcon
class="h-5 w-5 shrink-0 text-primary"
:class="isUserProjectsOptionSelected ? 'text-contrast' : 'text-primary'"
/>
@@ -212,7 +211,6 @@
:placeholder="formatMessage(analyticsMessages.selectProjects)"
:no-options-message="noProjectsMessage"
:searchable="projectOptions.length > 6"
fuzzy-search
:max-tag-rows="1"
checkbox-position="right"
show-selection-actions
@@ -292,7 +290,7 @@
@keydown.enter.stop
@keydown.space.stop
>
<UserIcon
<LayersIcon
class="h-5 w-5 shrink-0 text-primary"
:class="isUserProjectsOptionSelected ? 'text-contrast' : 'text-primary'"
/>
@@ -451,7 +449,6 @@ import {
ClockIcon,
FolderOpenIcon,
LayersIcon,
UserIcon,
} from '@modrinth/assets'
import {
ButtonStyled,
@@ -502,7 +499,7 @@ import {
import TimeFramePicker from './TimeframePicker.vue'
const QUERY_BUILDER_DROPDOWN_MAX_HEIGHT = 500
const QUERY_BUILDER_DROPDOWN_MIN_WIDTH = '14rem'
const QUERY_BUILDER_DROPDOWN_MIN_WIDTH = '12rem'
const analyticsQueryChipTriggerClass = 'h-10 '
const analyticsQueryAddFilterButtonClass = '!h-10 max-w-full !w-max !px-3.5 flex !gap-2'
const projectOptionCollator = new Intl.Collator(undefined, { numeric: true, sensitivity: 'base' })
@@ -547,7 +544,7 @@ function getProjectOption(
return {
value: project.id,
label: project.name,
searchTerms: [project.id, groupTitle].filter((term): term is string => Boolean(term)),
searchTerms: groupTitle ? [groupTitle] : undefined,
}
}
@@ -1011,34 +1008,15 @@ const breakdownOptions = computed<MultiSelectOption<Exclude<AnalyticsBreakdownPr
value: 'game_version',
label: formatAnalyticsBreakdownLabel('game_version', formatMessage),
},
{
value: 'dependent_project_download',
label: formatAnalyticsBreakdownLabel('dependent_project_download', formatMessage),
},
{
value: 'user_id',
label: formatAnalyticsBreakdownLabel('user_id', formatMessage),
},
)
return options.map((option) => {
const isSelected = selectedBreakdownSet.has(option.value)
return {
...option,
disabled:
!isSelected && (hasReachedBreakdownLimit || !canSelectBreakdownOption(option.value)),
}
})
return options.map((option) => ({
...option,
disabled: hasReachedBreakdownLimit && !selectedBreakdownSet.has(option.value),
}))
},
)
function canSelectBreakdownOption(breakdown: Exclude<AnalyticsBreakdownPreset, 'none'>): boolean {
return getAnalyticsBreakdownPresetsForProjectSelection(
[...selectedBreakdownValue.value, breakdown],
selectedProjectIds.value,
).includes(breakdown)
}
function getBreakdownOptionLabel(breakdown: Exclude<AnalyticsBreakdownPreset, 'none'>): string {
return (
breakdownOptions.value.find((option) => option.value === breakdown)?.label ??
@@ -1158,17 +1136,6 @@ function withBreakdownFields(
downloads.push('reason')
}
break
case 'user_id':
if (includesStat(breakdownStats, 'revenue') && includesStat(enabledStats, 'revenue')) {
revenue.push('user_id')
}
break
case 'dependent_project_download':
if (includesStat(breakdownStats, 'downloads') && includesStat(enabledStats, 'downloads')) {
downloads.push('dependent_project_id')
downloads.push('reason')
}
break
case 'version_id':
if (includesStat(breakdownStats, 'downloads') && includesStat(enabledStats, 'downloads')) {
downloads.push('version_id')
@@ -1297,12 +1264,6 @@ function buildMetricFilters(
filters.game_version,
),
loader: getFilterValuesForStat('loader_type', 'downloads', enabledStats, filters.loader_type),
dependent_project_id: getFilterValuesForStat(
'dependent_project_id',
'downloads',
enabledStats,
filters.dependent_project_id,
),
},
playtime: {
country: getFilterValuesForStat('country', 'playtime', enabledStats, filters.country),
@@ -1320,9 +1281,7 @@ function buildMetricFilters(
),
loader: getFilterValuesForStat('loader_type', 'playtime', enabledStats, filters.loader_type),
},
revenue: {
user_id: getFilterValuesForStat('user_id', 'revenue', enabledStats, filters.user_id),
},
revenue: {},
}
}
@@ -1336,15 +1295,6 @@ const fetchRequest = computed<Labrinth.Analytics.v3.FetchRequest>(() => {
const bucketBy = withBreakdownFields(selectedBreakdowns.value, selectedFilters.value)
const filterBy = buildMetricFilters(selectedBreakdowns.value, selectedFilters.value)
if (
includesStat(
getEnabledAnalyticsStatsForState(selectedBreakdowns.value, selectedFilters.value),
'revenue',
) &&
!bucketBy.revenue.includes('user_id')
) {
bucketBy.revenue.push('user_id')
}
const filteredProjectIds = getProjectIdsMatchingStatusFilter(
selectedProjectIds.value,
projectStatusById.value,
@@ -2,7 +2,6 @@ import type {
AnalyticsBreakdownPreset,
AnalyticsDashboardStat,
AnalyticsQueryFilterCategory,
AnalyticsSelectedBreakdowns,
AnalyticsSelectedFilters,
} from '~/providers/analytics/analytics'
@@ -14,10 +13,6 @@ export type AnalyticsDashboardDimension =
| 'monetization'
| 'user_agent'
| 'download_reason'
| 'user_id'
| 'dependent_project_download'
| 'dependent_project_id'
| 'dependent_project_type'
| 'game_version'
| 'loader_type'
@@ -28,12 +23,9 @@ export const FILTER_VALUE_CATEGORIES: Exclude<AnalyticsQueryFilterCategory, 'pro
'monetization',
'user_agent',
'download_reason',
'user_id',
'version_id',
'game_version',
'loader_type',
'dependent_project_id',
'dependent_project_type',
]
const ANALYTICS_DASHBOARD_STAT_ORDER: AnalyticsDashboardStat[] = [
@@ -53,12 +45,8 @@ const ANALYTICS_STATS_BY_DIMENSION: Record<
monetization: ['views', 'downloads'],
user_agent: ['downloads'],
download_reason: ['downloads'],
user_id: ['revenue'],
dependent_project_download: ['downloads'],
dependent_project_type: ['downloads'],
game_version: ['downloads', 'playtime'],
loader_type: ['downloads', 'playtime'],
dependent_project_id: ['downloads'],
project_status: ANALYTICS_DASHBOARD_STAT_ORDER,
}
@@ -72,8 +60,6 @@ const ANALYTICS_DIMENSION_BY_BREAKDOWN: Record<
monetization: 'monetization',
user_agent: 'user_agent',
download_reason: 'download_reason',
user_id: 'user_id',
dependent_project_download: 'dependent_project_download',
version_id: 'version_id',
loader: 'loader_type',
game_version: 'game_version',
@@ -88,12 +74,9 @@ const ANALYTICS_DIMENSION_BY_FILTER_CATEGORY: Record<
monetization: 'monetization',
user_agent: 'user_agent',
download_reason: 'download_reason',
user_id: 'user_id',
version_id: 'version_id',
game_version: 'game_version',
loader_type: 'loader_type',
dependent_project_id: 'dependent_project_id',
dependent_project_type: 'dependent_project_type',
}
const ANALYTICS_FILTER_CATEGORY_BY_BREAKDOWN: Record<
@@ -106,8 +89,6 @@ const ANALYTICS_FILTER_CATEGORY_BY_BREAKDOWN: Record<
monetization: 'monetization',
user_agent: 'user_agent',
download_reason: 'download_reason',
user_id: 'user_id',
dependent_project_download: null,
version_id: 'version_id',
loader: 'loader_type',
game_version: 'game_version',
@@ -305,33 +286,6 @@ export function getAnalyticsStatsForBreakdowns(
return stats
}
export function getAnalyticsBreakdownsWithSharedStats(
breakdowns: AnalyticsBreakdownInput,
): AnalyticsSelectedBreakdowns {
const normalizedBreakdowns = normalizeAnalyticsBreakdowns(breakdowns)
const compatibleBreakdowns: AnalyticsSelectedBreakdowns = []
let sharedStats: readonly AnalyticsDashboardStat[] | null = null
for (const breakdown of normalizedBreakdowns) {
const breakdownStats = getAnalyticsStatsForBreakdown(breakdown)
if (sharedStats === null) {
compatibleBreakdowns.push(breakdown)
sharedStats = breakdownStats
continue
}
const nextSharedStats = intersectAnalyticsStats(sharedStats, breakdownStats)
if (nextSharedStats.length === 0) {
continue
}
compatibleBreakdowns.push(breakdown)
sharedStats = nextSharedStats
}
return compatibleBreakdowns
}
export function getAnalyticsStatsForFilterCategory(
category: AnalyticsQueryFilterCategory,
): readonly AnalyticsDashboardStat[] {
@@ -377,20 +331,12 @@ export function getVisibleAnalyticsFilterCategoriesForState(
breakdowns: AnalyticsBreakdownInput,
filters: AnalyticsSelectedFilters,
): readonly Exclude<AnalyticsQueryFilterCategory, 'project'>[] {
const normalizedBreakdowns = normalizeAnalyticsBreakdowns(breakdowns)
return FILTER_VALUE_CATEGORIES.filter((category) => {
if (
category === 'dependent_project_type' &&
!normalizedBreakdowns.includes('dependent_project_download')
) {
return false
}
return haveAnalyticsStatOverlap(
return FILTER_VALUE_CATEGORIES.filter((category) =>
haveAnalyticsStatOverlap(
getAnalyticsStatsForFilterScope(breakdowns, filters, category),
getAnalyticsStatsForFilterCategory(category),
)
})
),
)
}
export function sanitizeAnalyticsSelectedFilters(
@@ -399,7 +345,6 @@ export function sanitizeAnalyticsSelectedFilters(
): AnalyticsSelectedFilters {
const nextFilters = cloneSelectedFilters(filters)
let availableStats = [...getAnalyticsStatsForBreakdowns(breakdowns)]
const normalizedBreakdowns = normalizeAnalyticsBreakdowns(breakdowns)
for (const category of FILTER_VALUE_CATEGORIES) {
if (filters[category].length === 0) {
@@ -407,11 +352,7 @@ export function sanitizeAnalyticsSelectedFilters(
}
const categoryStats = getAnalyticsStatsForFilterCategory(category)
if (
!haveAnalyticsStatOverlap(availableStats, categoryStats) ||
(category === 'dependent_project_type' &&
!normalizedBreakdowns.includes('dependent_project_download'))
) {
if (!haveAnalyticsStatOverlap(availableStats, categoryStats)) {
nextFilters[category] = []
continue
}
@@ -430,12 +371,9 @@ export function cloneSelectedFilters(filters: AnalyticsSelectedFilters): Analyti
monetization: [...filters.monetization],
user_agent: [...filters.user_agent],
download_reason: [...filters.download_reason],
user_id: [...filters.user_id],
version_id: [...filters.version_id],
game_version: [...filters.game_version],
loader_type: [...filters.loader_type],
dependent_project_id: [...filters.dependent_project_id],
dependent_project_type: [...filters.dependent_project_type],
}
}
@@ -518,7 +456,7 @@ export function normalizeSelectedValues(
.map((value) => value.trim().toLowerCase())
.filter(isProjectStatusFilterValue)
}
if (categoryKey === 'loader_type' || categoryKey === 'dependent_project_type') {
if (categoryKey === 'loader_type') {
return Array.from(
new Set(
selectedValues
@@ -6,7 +6,6 @@ import {
areStringArraysEqual,
buildAnalyticsQueryBuilderRouteQuery,
getAnalyticsBreakdownPresetsForProjectSelection,
hasAnalyticsAllProjectSelectionQuery,
hasAnalyticsQueryBuilderRouteChange,
readAnalyticsGraphState,
readAnalyticsQueryBuilderState,
@@ -55,7 +54,6 @@ export interface UseAnalyticsRouteSyncOptions {
graph: AnalyticsGraphRefs
availableProjectIds: Ref<string[]>
defaultProjectIds: Ref<string[]>
areProjectsLoaded: Ref<boolean>
sanitizeSelectedFilters: (
breakdowns: readonly AnalyticsBreakdownPreset[],
filters: AnalyticsSelectedFilters,
@@ -63,14 +61,8 @@ export interface UseAnalyticsRouteSyncOptions {
}
export function useAnalyticsRouteSync(options: UseAnalyticsRouteSyncOptions) {
const {
queryBuilder,
graph,
availableProjectIds,
defaultProjectIds,
areProjectsLoaded,
sanitizeSelectedFilters,
} = options
const { queryBuilder, graph, availableProjectIds, defaultProjectIds, sanitizeSelectedFilters } =
options
const route = useRoute()
const router = useRouter()
@@ -121,10 +113,6 @@ export function useAnalyticsRouteSync(options: UseAnalyticsRouteSyncOptions) {
return
}
if (hasAnalyticsAllProjectSelectionQuery(route.query) && !areProjectsLoaded.value) {
return
}
const nextRouteQuery = buildAnalyticsQueryBuilderRouteQuery(
route.query,
getSelectedAnalyticsQueryBuilderState(),
@@ -159,10 +147,6 @@ export function useAnalyticsRouteSync(options: UseAnalyticsRouteSyncOptions) {
}
function applyRouteQueryToState(nextQuery: LocationQuery) {
if (hasAnalyticsAllProjectSelectionQuery(nextQuery) && !areProjectsLoaded.value) {
return
}
const nextQueryState = readAnalyticsQueryBuilderState(
nextQuery,
availableProjectIds.value,
@@ -1,189 +0,0 @@
<template>
<div class="relative overflow-clip rounded-xl bg-bg px-4 py-3">
<div
class="absolute bottom-0 left-0 top-0 w-1"
:class="
charge.type === 'refund' ? 'bg-purple' : (chargeStatuses[charge.status]?.color ?? 'bg-blue')
"
/>
<div class="grid w-full grid-cols-[1fr_auto] items-center gap-4">
<div class="flex flex-col gap-2">
<span>
<span class="font-bold text-contrast">
<template v-if="charge.status === 'succeeded'"> Succeeded </template>
<template v-else-if="charge.status === 'failed'"> Failed </template>
<template v-else-if="charge.status === 'cancelled'"> Cancelled </template>
<template v-else-if="charge.status === 'processing'"> Processing </template>
<template v-else-if="charge.status === 'open'"> Upcoming </template>
<template v-else-if="charge.status === 'expiring'"> Expiring </template>
<template v-else> {{ charge.status }} </template>
</span>
<span class="text-secondary opacity-50"></span>
<span>
<template v-if="charge.type === 'refund'"> Refund </template>
<template v-else-if="charge.type === 'subscription'">
<template v-if="charge.status === 'cancelled'"> Subscription </template>
<template v-else-if="isLatestCharge"> Started subscription </template>
<template v-else> Subscription renewal </template>
</template>
<template v-else-if="charge.type === 'one-time'"> One-time charge </template>
<template v-else-if="charge.type === 'proration'"> Proration charge </template>
<template v-else> {{ charge.status }} </template>
</span>
<template v-if="charge.status !== 'cancelled'">
<span class="text-secondary opacity-50"></span>
{{ formatPrice(charge.amount, charge.currency_code) }}
</template>
</span>
<span
v-if="productMetadata && productMetadata.type === 'pyro'"
class="flex items-center gap-1 text-sm text-secondary"
>
<span class="font-bold">Product:</span>
<span v-if="productMetadata.ram">{{ productMetadata.ram / 1024 }}GB RAM</span>
<span v-else>Unknown RAM</span>
<span class="text-secondary opacity-50"></span>
<span v-if="productMetadata.cpu">{{ productMetadata.cpu }} vCPU</span>
<span v-else>Unknown CPU</span>
<span class="text-secondary opacity-50"></span>
<span v-if="productMetadata.storage">{{ productMetadata.storage / 1024 }}GB Storage</span>
<span v-else>Unknown Storage</span>
<span class="text-secondary opacity-50"></span>
<span v-if="productMetadata.swap">{{ productMetadata.swap }}MB Swap</span>
<span v-else>Unknown Swap</span>
</span>
<span class="text-sm text-secondary">
<span
v-if="charge.status === 'cancelled' && dayjs(charge.due).isBefore(dayjs())"
class="font-bold"
>
Ended:
</span>
<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>
{{ 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>
{{ formatDateTime(charge.last_attempt) }}
<span class="text-secondary">({{ formatRelativeTime(charge.last_attempt) }}) </span>
</span>
<div class="flex w-full items-center gap-1 text-xs text-secondary">
{{ charge.status }}
<span class="text-secondary opacity-50"></span>
{{ charge.type }}
<span class="text-secondary opacity-50"></span>
{{ formatPrice(charge.amount, charge.currency_code) }}
<span class="text-secondary opacity-50"></span>
{{ formatDateTimeShort(charge.due) }}
<template v-if="charge.subscription_interval">
<span class="text-secondary opacity-50"></span>
{{ charge.subscription_interval }}
</template>
</div>
</div>
<div class="flex gap-2">
<ButtonStyled v-if="isRefunded">
<div class="button-like disabled"><CheckIcon /> Charge refunded</div>
</ButtonStyled>
<ButtonStyled
v-else-if="charge.status === 'succeeded' && charge.type !== 'refund'"
color="red"
color-fill="text"
>
<button @click="emit('refund', charge)">
<CurrencyIcon />
Refund options
</button>
</ButtonStyled>
<ButtonStyled
v-else-if="charge.status === 'failed' || charge.status === 'open'"
color="red"
color-fill="text"
>
<button @click="emit('modify', charge, subscription)">
<CurrencyIcon />
Modify charge
</button>
</ButtonStyled>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import type { Labrinth } from '@modrinth/api-client'
import { CheckIcon, CurrencyIcon } from '@modrinth/assets'
import { ButtonStyled, useFormatDateTime, useFormatPrice, useRelativeTime } from '@modrinth/ui'
import dayjs from 'dayjs'
import { products } from '~/generated/state.json'
const props = defineProps<{
charge: Labrinth.Billing.Internal.Charge
subscription: Labrinth.Billing.Internal.UserSubscription
allCharges: Labrinth.Billing.Internal.Charge[]
chargeIndex: number
chargeCount: number
}>()
const emit = defineEmits<{
refund: [charge: Labrinth.Billing.Internal.Charge]
modify: [
charge: Labrinth.Billing.Internal.Charge,
subscription: Labrinth.Billing.Internal.UserSubscription,
]
}>()
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 formatRelativeTime = useRelativeTime()
const isLatestCharge = computed(() => props.chargeIndex === props.chargeCount - 1)
const isRefunded = computed(() =>
props.allCharges.some(
(charge) => charge.type === 'refund' && charge.parent_charge_id === props.charge.id,
),
)
const productMetadata = computed(
() =>
products.find((product) => product.prices.some((price) => price.id === props.charge.price_id))
?.metadata,
)
const chargeStatuses = {
open: {
color: 'bg-blue',
},
processing: {
color: 'bg-orange',
},
succeeded: {
color: 'bg-green',
},
failed: {
color: 'bg-red',
},
cancelled: {
color: 'bg-red',
},
expiring: {
color: 'bg-orange',
},
}
</script>
@@ -89,8 +89,6 @@ import {
} from '@modrinth/ui'
import { ref } from 'vue'
import { generateUrlSlug } from '~/utils/slugs'
import CreateLimitAlert from './CreateLimitAlert.vue'
const router = useNativeRouter()
@@ -150,7 +148,7 @@ async function createOrganization(): Promise<void> {
const value = {
name: name.value.trim(),
description: description.value.trim(),
slug: slug.value.trim(),
slug: slug.value.trim().replace(/ +/g, ''),
}
const result: any = await useBaseFetch('organization', {
@@ -185,7 +183,12 @@ function hide(): void {
function updateSlug(): void {
if (!manualSlug.value) {
slug.value = generateUrlSlug(name.value)
slug.value = name.value
.trim()
.toLowerCase()
.replaceAll(' ', '-')
.replaceAll(/[^a-zA-Z0-9!@$()`.+,_"-]/g, '')
.replaceAll(/--+/gm, '-')
}
}
@@ -145,8 +145,6 @@ import {
} from '@modrinth/ui'
import { computed, defineAsyncComponent, h } from 'vue'
import { generateUrlSlug } from '~/utils/slugs'
import CreateLimitAlert from './CreateLimitAlert.vue'
type ProjectTypes = 'server' | 'project'
@@ -463,7 +461,12 @@ async function show(event?: MouseEvent, options?: ShowOptions) {
function updatedName() {
if (!manualSlug.value) {
slug.value = generateUrlSlug(name.value)
slug.value = name.value
.trim()
.toLowerCase()
.replaceAll(' ', '-')
.replaceAll(/[^a-zA-Z0-9!@$()`.+,_"-]/g, '')
.replaceAll(/--+/gm, '-')
}
}
</script>
@@ -24,7 +24,7 @@
</div>
<div class="input-group">
<ButtonStyled circular>
<button :class="{ '[&>svg]:rotate-180': !collapsed }" @click="$emit('toggleCollapsed')">
<button :class="!collapsed && '[&>svg]:rotate-180'" @click="$emit('toggleCollapsed')">
<DropdownIcon class="duration-250 transition-transform ease-in-out" />
</button>
</ButtonStyled>
@@ -59,7 +59,7 @@
</ButtonStyled>
<ButtonStyled v-if="visibleQuickReplies.length > 0">
<OverflowMenu :options="visibleQuickReplies">
Quick reply
Quick Reply
<ChevronDownIcon />
</OverflowMenu>
</ButtonStyled>
@@ -48,6 +48,7 @@ export const DEFAULT_FEATURE_FLAGS = validateValues({
useV1ContentTabAPI: true,
labrinthApiCanary: false,
dismissedExternalProjectsInfo: false,
modpackPermissionsPage: false,
showAllBanners: false,
alwaysIgnoreErrorBanner: false,
showViewProdRouteBanner: false,
@@ -55,8 +56,6 @@ export const DEFAULT_FEATURE_FLAGS = validateValues({
showModeratorPrivateMessageHighlight: true,
archonApiStaging: false,
showHostingAccessInstanceAuditLog: false,
versionDevInfoCollapsed: true,
alwaysShowVersionDevInfo: false,
} as const)
export type FeatureFlag = keyof typeof DEFAULT_FEATURE_FLAGS
@@ -8,11 +8,4 @@ export const versionQueryOptions = {
queryFn: () => client.labrinth.versions_v3.getVersion(versionId),
staleTime: STALE_TIME,
}),
fromProject: (projectId: string, versionIdOrNumber: string, client: AbstractModrinthClient) => ({
queryKey: ['project', projectId, 'version', 'v3', versionIdOrNumber] as const,
queryFn: () =>
client.labrinth.versions_v3.getVersionFromIdOrNumber(projectId, versionIdOrNumber),
staleTime: STALE_TIME,
}),
}
@@ -132,39 +132,23 @@ export function createLoaderParsers(
),
}
},
// Fabric (or Babric for mc version beta 1.7.3)
// Fabric
'fabric.mod.json': (file: string): InferredVersionInfo => {
const metadata = JSON.parse(file) as any
const mcDependency = metadata.depends?.minecraft
const mcDependencies = Array.isArray(mcDependency) ? mcDependency : [mcDependency]
let detectedGameVersions = metadata.depends
const detectedGameVersions = metadata.depends
? getGameVersionsMatchingSemverRange(metadata.depends.minecraft, simplifiedGameVersions)
: []
const loaders: string[] = []
// Detect Beta 1.7.3 -> Babric
const hasBabricVersion = mcDependencies.some(
(version: string | undefined) => version?.includes('1.0.0-beta.7.3'), // this is fabric's normalized mc version format
)
// Detect 1.3-1.13 -> legacy-fabric
const hasLegacyVersions = detectedGameVersions.some((version) => {
const match = version.match(/^1\.(\d+)/)
return match && parseInt(match[1]) >= 3 && parseInt(match[1]) <= 13
})
if (hasBabricVersion) {
loaders.push('babric')
detectedGameVersions = gameVersions
.filter((version) => version.version === 'b1.7.3')
.map((version) => version.version)
} else if (hasLegacyVersions) {
loaders.push('legacy-fabric')
} else {
loaders.push('fabric')
}
if (hasLegacyVersions) loaders.push('legacy-fabric')
else loaders.push('fabric')
return {
name: `${project.title} ${metadata.version}`,
@@ -50,15 +50,15 @@ export function getGameVersionsMatchingMavenRange(
ranges.push(range)
}
const LESS_THAN_EQUAL = /^\(,([^,]*)]$/
const LESS_THAN = /^\(,([^,]*)\)$/
const EQUAL = /^\[([^,]*)]$/
const GREATER_THAN_EQUAL = /^\[([^,]*),\)$/
const GREATER_THAN = /^\(([^,]*),\)$/
const BETWEEN = /^\(([^,]*),([^,]*)\)$/
const BETWEEN_EQUAL = /^\[([^,]*),([^,]*)]$/
const BETWEEN_LESS_THAN_EQUAL = /^\(([^,]*),([^,]*)]$/
const BETWEEN_GREATER_THAN_EQUAL = /^\[([^,]*),([^,]*)\)$/
const LESS_THAN_EQUAL = /^\(,(.*)]$/
const LESS_THAN = /^\(,(.*)\)$/
const EQUAL = /^\[(.*)]$/
const GREATER_THAN_EQUAL = /^\[(.*),\)$/
const GREATER_THAN = /^\((.*),\)$/
const BETWEEN = /^\((.*),(.*)\)$/
const BETWEEN_EQUAL = /^\[(.*),(.*)]$/
const BETWEEN_LESS_THAN_EQUAL = /^\((.*),(.*)]$/
const BETWEEN_GREATER_THAN_EQUAL = /^\[(.*),(.*)\)$/
const semverRanges = []
-105
View File
@@ -8,108 +8,6 @@
"admin.billing.error.not-found": {
"message": "المستخدم غير موجود"
},
"analytics.action.add": {
"message": "إضافة"
},
"analytics.action.cancel": {
"message": "إلغاء"
},
"analytics.action.refresh": {
"message": "تحديث"
},
"analytics.action.reset": {
"message": "إعادة ضبط"
},
"analytics.breakdown.country": {
"message": "الدولة"
},
"analytics.breakdown.download-reason": {
"message": "سبب التنزيل"
},
"analytics.breakdown.download-source": {
"message": "مصدر التنزيل"
},
"analytics.breakdown.game-version": {
"message": "إصدار اللُعبة"
},
"analytics.breakdown.project": {
"message": "المشروع"
},
"analytics.breakdown.project-status": {
"message": "حالة المشروع"
},
"analytics.breakdown.project-version": {
"message": "إصدار المشروع"
},
"analytics.chart.action.show-all": {
"message": "أظهار الكل"
},
"analytics.chart.axis.playtime-hours": {
"message": "{hours} ساعة"
},
"analytics.chart.controls.display": {
"message": "عرض"
},
"analytics.chart.tooltip.total": {
"message": "مجموع"
},
"analytics.downloads.suffix": {
"message": "التنزيلات"
},
"analytics.filter.game-version-type.all": {
"message": "الكل"
},
"analytics.group-by.week": {
"message": "الأسبوع"
},
"analytics.group-by.year": {
"message": "السنة"
},
"analytics.loading.fetching-results": {
"message": "جلب البيانات..."
},
"analytics.options.loading": {
"message": "تحميل..."
},
"analytics.project-event.project-approved": {
"message": "المشروع قُبل"
},
"analytics.project-event.project-private": {
"message": "وضع المشروع خاص"
},
"analytics.project-event.project-status-changed": {
"message": "تم تغيير حالة المشروع"
},
"analytics.project-event.version-released": {
"message": ""
},
"analytics.project-status.draft": {
"message": "مسودة"
},
"analytics.project-status.private": {
"message": "خاص"
},
"analytics.project-status.rejected": {
"message": "رُفض المشروع"
},
"analytics.project.your": {
"message": "مشاريعك"
},
"analytics.query.label.project": {
"message": "المشروع:"
},
"analytics.stat.downloads": {
"message": "التنزيلات"
},
"analytics.stat.playtime": {
"message": "وقت اللعب"
},
"analytics.stat.playtime-hours": {
"message": "{hours} ساعة"
},
"analytics.table.search.placeholder": {
"message": "بحث..."
},
"app-marketing.download.description": {
"message": "تطبيقنا لسطح المكتب متاح على جميع المنصات، اختر الإصدار الذي تريده."
},
@@ -509,9 +407,6 @@
"create-project-version.create-modal.stage.add-files.admonition": {
"message": "الملفات التكميلية تستخدم لدعم الموارد مثل كود المصدر، وليس للإصدارات أو الأنواع البديلة."
},
"create.collection.collection-info": {
"message": "سيتم إنشاء مجموعتك الجديدة كمجموعة عامة تحتوي على {count, plural,\n one {# مشروع}\n other {# مشاريع}\n}."
},
"create.collection.create-collection": {
"message": "إنشاء مجموعة"
},
+2 -128
View File
@@ -71,87 +71,6 @@
"analytics.chart.controls.annotations": {
"message": "Anotace"
},
"analytics.chart.controls.button": {
"message": "Ovládání"
},
"analytics.chart.events.project-title": {
"message": "<project>{projectName}</project>: {title}"
},
"analytics.chart.tooltip.hide-entry": {
"message": "Skrýt {name} v grafu"
},
"analytics.chart.tooltip.show-entry": {
"message": "Zobrazit {name} v grafu"
},
"analytics.chart.view.area": {
"message": "Oblast"
},
"analytics.download-reason.update": {
"message": "Aktualizace"
},
"analytics.downloads.suffix": {
"message": "stažení"
},
"analytics.empty.no-projects": {
"message": "Žádné dostupné projekty"
},
"analytics.empty.no-projects-for-analytics": {
"message": "Žádné dostupné projekty pro analýzu "
},
"analytics.empty.select-project": {
"message": "Vyber alespoň jeden projekt pro zobrazení dat"
},
"analytics.group-by.1h": {
"message": "1h"
},
"analytics.group-by.6h": {
"message": "6h"
},
"analytics.group-by.date": {
"message": "Datum"
},
"analytics.group-by.day": {
"message": "Den"
},
"analytics.group-by.month": {
"message": "Měsíc"
},
"analytics.group-by.week": {
"message": "Týden "
},
"analytics.group-by.year": {
"message": "Rok"
},
"analytics.options.loading": {
"message": "Načítání..."
},
"analytics.project-status.approved": {
"message": "Schválený"
},
"analytics.project-status.archived": {
"message": "Archivovaný "
},
"analytics.project-status.other": {
"message": "Jiné"
},
"analytics.project-status.private": {
"message": "Soukromý "
},
"analytics.project-status.unlisted": {
"message": "Neveřejný "
},
"analytics.project-status.withheld": {
"message": "Pozastavený"
},
"analytics.project.all": {
"message": "Všechny projekty"
},
"analytics.query.label.project": {
"message": "Projekt:"
},
"analytics.value.other": {
"message": "Jiné"
},
"app-marketing.download.description": {
"message": "Naše aplikace je dostupná pro všechny počítačové platformy, vyberte si požadovanou verzi."
},
@@ -548,12 +467,6 @@
"conversation-thread.action.add-private-note": {
"message": "Přidat soukromou poznámku"
},
"conversation-thread.action.approve": {
"message": "Schválit"
},
"conversation-thread.action.approve-with-reply": {
"message": "Schválit s odpovědí"
},
"conversation-thread.action.close-thread": {
"message": "Uzavřít vlákno"
},
@@ -575,12 +488,6 @@
"conversation-thread.action.reply-to-thread": {
"message": "Odpovědět vláknu"
},
"conversation-thread.action.resubmit-for-review": {
"message": "Znovu odeslat ke kontrole"
},
"conversation-thread.action.resubmit-for-review-with-reply": {
"message": "Znovu odeslat ke kontrole s odpovědí "
},
"conversation-thread.action.send": {
"message": "Odeslat"
},
@@ -590,12 +497,6 @@
"conversation-thread.action.send-to-review-with-reply": {
"message": "Odeslat ke kontrole s odpovědí"
},
"conversation-thread.action.withhold": {
"message": "Pozastavit "
},
"conversation-thread.action.withhold-with-reply": {
"message": "Pozastavit s odpovědí"
},
"conversation-thread.reply-editor.placeholder.reply": {
"message": "Odpovědět vláknu..."
},
@@ -608,9 +509,6 @@
"create-project-version.create-modal.stage.add-files.admonition": {
"message": "Doplňkové soubory slouží jako podpůrné materiály (např. zdrojový kód), ne jako alternativní verze či varianty."
},
"create.collection.collection-info": {
"message": "Tvá nová kolekce bude vytvořena jako veřejná a bude obsahovat {count, plural, =0 {žádné projekty} one {# projekt} few {# projekty} other {# projektů}}."
},
"create.collection.create-collection": {
"message": "Vytvořit kolekci"
},
@@ -785,9 +683,6 @@
"dashboard.collections.sort.recently-updated": {
"message": "Nedávno aktualizováno"
},
"dashboard.creator-tax-form-modal.confirmation.download-button": {
"message": "Stáhnout {formType}"
},
"dashboard.creator-tax-form-modal.confirmation.title": {
"message": "Hotovo! 🎉"
},
@@ -822,7 +717,7 @@
"message": "Vyhladaj štát..."
},
"dashboard.creator-withdraw-modal.method-selection.region": {
"message": "Oblast"
"message": "Región"
},
"dashboard.creator-withdraw-modal.muralpay-details.account-owner": {
"message": "Majitel účtu"
@@ -842,9 +737,6 @@
"dashboard.creator-withdraw-modal.muralpay-details.document-number-tax-id-placeholder": {
"message": "Zadejte DIČ"
},
"dashboard.creator-withdraw-modal.muralpay-details.network": {
"message": "Síť"
},
"dashboard.creator-withdraw-modal.paypal-details.account": {
"message": "Účet"
},
@@ -864,7 +756,7 @@
"message": "Přihlásit přes PayPal"
},
"dashboard.creator-withdraw-modal.stage.tax-form": {
"message": "Daňový formulář"
"message": ""
},
"dashboard.creator-withdraw-modal.tremendous-details.enter-denomination-placeholder": {
"message": "Zadejte množství"
@@ -887,9 +779,6 @@
"dashboard.creator-withdraw-modal.withdraw-limit-used": {
"message": "Vyčerpali jste svůj limit pro výběr <b>{withdrawLimit}</b>. Pro vyšší výběr musíte vyplnit daňový formulář."
},
"dashboard.discord-roles.role.creator": {
"message": "Tvůrce"
},
"dashboard.organizations.button.create": {
"message": "Vytvořit organizaci"
},
@@ -899,18 +788,12 @@
"dashboard.organizations.error.fetch": {
"message": "Nepodařilo se načíst organizace"
},
"dashboard.organizations.title": {
"message": "Organizace"
},
"dashboard.overview.notifications.button.mark-all-as-read": {
"message": "Označit vše jako přečtené"
},
"dashboard.overview.notifications.button.view-history": {
"message": "Ukázat historii"
},
"dashboard.overview.notifications.empty.no-unread": {
"message": "Nemáš žádná nepřečtená oznámení."
},
"dashboard.overview.notifications.history.label": {
"message": "Historie"
},
@@ -923,9 +806,6 @@
"dashboard.projects.head-title": {
"message": "Projekty"
},
"dashboard.projects.links.button.edit": {
"message": "Upravit odkazy"
},
"dashboard.projects.links.discord-invite.label": {
"message": "Discord pozvánka"
},
@@ -944,9 +824,6 @@
"dashboard.projects.table.status": {
"message": "Stav"
},
"dashboard.report.title": {
"message": "Nahlásit {id}"
},
"dashboard.revenue.available-now": {
"message": "K dispozici nyní"
},
@@ -1391,9 +1268,6 @@
"profile.meta.description-with-bio": {
"message": "{bio} - Stáhněte si projekty od {username} na Modrinthu"
},
"profile.stats.projects-followers": {
"message": "{count, plural, one {<stat>{count}</stat> sledující projektu} few {<stat>{count}</stat> sledující projektu} other {<stat>{count}</stat> sledujících projektu}}"
},
"profile.user-id": {
"message": "ID uživatele: {id}"
},
+21 -33
View File
@@ -134,6 +134,18 @@
"analytics.chart.render-limit.header": {
"message": "Alle {count} Linien im Diagramm anzeigen?"
},
"analytics.chart.table-selection.all": {
"message": "Zeige alle {itemType, select, project {{count, plural, one {Projekt} other {Projekte}}} country {{count, plural, one {Land} other {Länder}}} monetization {{count, plural, one {Monetarisierungswert} other {Monetarisierungswerte}}} downloadSource {{count, plural, one {Download-Quelle} other {Download-Quellen}}} downloadReason {{count, plural, one {Download-Grund} other {Download-Gründe}}} projectVersion {{count, plural, one {Projektversion} other {Projektversionen}}} loader {{count, plural, one {Loader} other {Loader}}} gameVersion {{count, plural, one {Spielversion} other {Spielversionen}}} other {{count, plural, one {Element} other {Elemente}}}} aus der Tabelle an"
},
"analytics.chart.table-selection.count": {
"message": "Zeige {count} {itemType, select, project {{count, plural, one {Projekt} other {Projekte}}} country {{count, plural, one {Land} other {Länder}}} monetization {{count, plural, one {Monetarisierungswert} other {Monetarisierungswerte}}} downloadSource {{count, plural, one {Download-Quelle} other {Download-Quellen}}} downloadReason {{count, plural, one {Download-Grund} other {Download-Gründe}}} projectVersion {{count, plural, one {Projektversion} other {Projektversionen}}} loader {{count, plural, one {Loader} other {Loader}}} gameVersion {{count, plural, one {Spielversion} other {Spielversionen}}} other {{count, plural, one {Element} other {Elemente}}}} aus der Tabelle an"
},
"analytics.chart.table-selection.limited": {
"message": "Zeige {limit} {itemType, select, project {{limit, plural, one {Projekt} other {Projekte}}} country {{limit, plural, one {Land} other {Länder}}} monetization {{limit, plural, one {Monetarisierungswert} other {Monetarisierungswerte}}} downloadSource {{limit, plural, one {Download-Quelle} other {Download-Quellen}}} downloadReason {{limit, plural, one {Download-Grund} other {Download-Gründe}}} projectVersion {{limit, plural, one {Projektversion} other {Projektversionen}}} loader {{limit, plural, one {Loader} other {Loader}}} gameVersion {{limit, plural, one {Spielversion} other {Spielversionen}}} other {{limit, plural, one {Element} other {Elemente}}}} aus der Tabelle an"
},
"analytics.chart.table-selection.top": {
"message": "Zeige die Top {count} {itemType, select, project {{count, plural, one {Projekt} other {Projekte}}} country {{count, plural, one {Land} other {Länder}}} monetization {{count, plural, one {Monetarisierungswert} other {Monetarisierungswerte}}} downloadSource {{count, plural, one {Download-Quelle} other {Download-Quellen}}} downloadReason {{count, plural, one {Download-Grund} other {Download-Gründe}}} projectVersion {{count, plural, one {Projektversion} other {Projektversionen}}} loader {{count, plural, one {Loader} other {Loader}}} gameVersion {{count, plural, one {Spielversion} other {Spielversionen}}} other {{count, plural, one {Element} other {Elemente}}}} aus der Tabelle an"
},
"analytics.chart.tooltip.duration.days": {
"message": "{count, plural, one {# Tag} other {# Tage}}"
},
@@ -338,12 +350,6 @@
"analytics.project.select": {
"message": "Projekte auswählen"
},
"analytics.project.user": {
"message": "Projekte von {username}"
},
"analytics.project.your": {
"message": "Deine Projekte"
},
"analytics.query.filter.add": {
"message": "Filter hinzufügen"
},
@@ -1437,7 +1443,7 @@
"message": "Unverifizierte E-Mail"
},
"dashboard.creator-withdraw-modal.tremendous-details.unverified-email-message": {
"message": "Die Zustellungs-E-Mail, die du eingegeben hast, ist nicht mit deinem Modrinth-Konto verbunden. Belohnungen, welche an eine falsche E-Mail-Adresse gesendet werden, können von Modrinth nicht wiederhergestellt werden."
"message": "Die Zustellungs-E-Mail die du eingegeben hast ist nicht mit deinem Modrinth-Konto verbunden. Belohnungen, welche an eine falsche E-Mail-Adresse gesendet werden können von Modrinth nicht wiederhergestellt werden."
},
"dashboard.creator-withdraw-modal.tremendous-details.usd-paypal-warning-header": {
"message": "Weniger Gebühren verfügbar"
@@ -1454,24 +1460,6 @@
"dashboard.creator-withdraw-modal.withdraw-limit-used": {
"message": "Du hast dein Auszahlungslimit von <b>{withdrawLimit}</b> aufgebraucht. Du musst ein Steuerformular ausfüllen, um merh abzuheben."
},
"dashboard.discord-roles.banner.body": {
"message": "Du bist berechtigt für {roles}. Verknüpfe dein Discord-Konto über Modrinth und wir synchronisieren sie automatisch."
},
"dashboard.discord-roles.banner.cta": {
"message": "Discord verknüpfen"
},
"dashboard.discord-roles.banner.title": {
"message": "Beanspruche deine Discord-Rollen"
},
"dashboard.discord-roles.role.big-creator": {
"message": "1 Mio.+ Downloads"
},
"dashboard.discord-roles.role.creator": {
"message": "Creator"
},
"dashboard.discord-roles.role.pride": {
"message": "Pride 2026"
},
"dashboard.head-title": {
"message": "Dashboard"
},
@@ -1524,7 +1512,7 @@
"message": "Benachrichtigungen werden geladen..."
},
"dashboard.projects.bulk-edit-hint": {
"message": "Du kannst mehrere Projekte gleichzeitig bearbeiten, indem du sie unten auswählst."
"message": "Du kannst mehrere Projekte gleichzeitig bearbeiten, indem du sie unden auswählst."
},
"dashboard.projects.bulk-edit.server-disabled": {
"message": "Serverprojekte understützen Massenbearbeitung nicht"
@@ -2303,9 +2291,6 @@
"landing.subheading": {
"message": "Entdecke, Spiele und teile Minecraft-Inhalte durch unsere quelloffene Platform, welche für die Community entwickelt wird."
},
"layout.action.analytics-events": {
"message": "Analytics-Ergebnisse"
},
"layout.action.change-theme": {
"message": "Farbschema ändern"
},
@@ -2861,9 +2846,6 @@
"profile.bio.fallback.user": {
"message": "Ein Modrinth Benutzer."
},
"profile.button.analytics": {
"message": "Nutzungsstatistik anzeigen"
},
"profile.button.billing": {
"message": "Nutzer Zahlungen verwalten"
},
@@ -3156,7 +3138,7 @@
"message": "Platform: {platform}"
},
"project.download.platform-error": {
"message": "Fehler: Keine Plattform gefunden"
"message": "Fehler: Keine Platform gefunden"
},
"project.download.platform-tooltip": {
"message": "{title} ist nur für {platform} verfügbar"
@@ -3290,6 +3272,9 @@
"project.moderation.thread.help-center-note.2": {
"message": "Wenn du Hilfe benötigst oder weitere Fragen hast, besuche bitte das <help-center-link>Hilfezentrum von Modrinth</help-center-link> und klicke auf die blaue Sprechblase, um den Support zu kontaktieren."
},
"project.moderation.thread.moderator-see-user-ui-toggle": {
"message": "Benutzer-UI anzeigen"
},
"project.moderation.thread.private-description": {
"message": "Dies ist ein privater Unterhaltungsthread mit den Modrinth-Moderatoren. Sie können dich bei Problemen bezüglich dieses Projekts kontaktieren."
},
@@ -3419,6 +3404,9 @@
"project.versions.withheld-versions-warning.description": {
"message": "{count, plural, one {Diese Version ist} other {Diese Versionen sind}} derzeit zurückgehalten und nicht öffentlich gelistet. Bitte stelle einen Nachweis bereit, dass du die Erlaubnis hast, bestimmte Dateien weiterzuverbreiten, die in {count, plural, one {der Modpack-Version} other {den Modpack-Versionen}} enthalten sind."
},
"project.versions.withheld-versions-warning.resolve-button": {
"message": "Beheben"
},
"project.versions.withheld-versions-warning.title": {
"message": "{count, plural, one {Version {version_name}} other {Versionen}} zurückgehalten aufgrund von unbekannten eingebetteten Inhalten"
},
+19 -34
View File
@@ -134,6 +134,18 @@
"analytics.chart.render-limit.header": {
"message": "Alle {count} Zeilen im Graphen anzeigen?"
},
"analytics.chart.table-selection.all": {
"message": "Zeige alle {itemType, select, project {{count, plural, one {Projekt} other {Projekte}}} country {{count, plural, one {Land} other {Länder}}} monetization {{count, plural, one {Monetarisierungswert} other {Monetarisierungswerte}}} downloadSource {{count, plural, one {Downloadquelle} other {Downloadquellen}}} downloadReason {{count, plural, one {Downloadgrund} other {Downloadgründe}}} projectVersion {{count, plural, one {Projektversion} other {Projektversionen}}} loader {{count, plural, one {Loader} other {Loader}}} gameVersion {{count, plural, one {Spielversion} other {Spielversionen}}} other {{count, plural, one {Artikel} other {Artikel}}}} aus der Tabelle"
},
"analytics.chart.table-selection.count": {
"message": "Zeige {count} {itemType, select, project {{count, plural, one {Projekt} other {Projekte}}} country {{count, plural, one {Land} other {Länder}}} monetization {{count, plural, one {Monetarisierungswert} other {Monetarisierungswerte}}} downloadSource {{count, plural, one {Downloadquelle} other {Downloadquellen}}} downloadReason {{count, plural, one {Downloadgrund} other {Downloadgründe}}} projectVersion {{count, plural, one {Projektversion} other {Projektversionen}}} loader {{count, plural, one {Loader} other {Loader}}} gameVersion {{count, plural, one {Spielversion} other {Spielversionen}}} other {{count, plural, one {Artikel} other {Artikel}}}} aus der Tabelle"
},
"analytics.chart.table-selection.limited": {
"message": "Zeige {limit} {itemType, select, project {{limit, plural, one {Projekt} other {Projekte}}} country {{limit, plural, one {Land} other {Länder}}} monetization {{limit, plural, one {Monetarisierungswert} other {Monetarisierungswerte}}} downloadSource {{limit, plural, one {Downloadquelle} other {Downloadquellen}}} downloadReason {{limit, plural, one {Downloadgrund} other {Downloadgründe}}} projectVersion {{limit, plural, one {Projektversion} other {Projektversionen}}} loader {{limit, plural, one {Loader} other {Loader}}} gameVersion {{limit, plural, one {Spielversion} other {Spielversionen}}} other {{limit, plural, one {Artikel} other {Artikel}}}} aus der Tabelle"
},
"analytics.chart.table-selection.top": {
"message": "Zeige die top {count} {itemType, select, project {{count, plural, one {Projekt} other {Projekte}}} country {{count, plural, one {Land} other {Länder}}} monetization {{count, plural, one {Monetarisierungswert} other {Monetarisierungswerte}}} downloadSource {{count, plural, one {Downloadquelle} other {Downloadquellen}}} downloadReason {{count, plural, one {Downloadgrund} other {Downloadgründe}}} projectVersion {{count, plural, one {Projektversion} other {Projektversionen}}} loader {{count, plural, one {Loader} other {Loader}}} gameVersion {{count, plural, one {Spielversion} other {Spielversionen}}} other {{count, plural, one {Artikel} other {Artikel}}}} aus der Tabelle"
},
"analytics.chart.tooltip.duration.days": {
"message": "{count, plural, one {# Tag} other {# Tage}}"
},
@@ -204,7 +216,7 @@
"message": "Keine Projekte für die Analyse verfügbar"
},
"analytics.empty.select-project": {
"message": "Wähle mindestens ein Projekt aus, um Daten anzuzeigen"
"message": "Wählen mindestens ein Projekt aus, um Daten anzuzeigen"
},
"analytics.filter.game-version-type": {
"message": "Art der Spielversion"
@@ -338,12 +350,6 @@
"analytics.project.select": {
"message": "Projekte auswählen"
},
"analytics.project.user": {
"message": "Projekte von {username}"
},
"analytics.project.your": {
"message": "Deine Projekte"
},
"analytics.query.filter.add": {
"message": "Filter hinzufügen"
},
@@ -1454,24 +1460,6 @@
"dashboard.creator-withdraw-modal.withdraw-limit-used": {
"message": "Du hast dein Auszahlungslimit von <b>{withdrawLimit}</b> aufgebraucht. Du musst ein Steuerformular ausfüllen, um merh abzuheben."
},
"dashboard.discord-roles.banner.body": {
"message": "Du bist für {roles} qualifiziert. Verknüpfe dein Discord-Konto über Modrinth, dann synchronisieren wir diese automatisch."
},
"dashboard.discord-roles.banner.cta": {
"message": "Discord verbinden"
},
"dashboard.discord-roles.banner.title": {
"message": "Hol dir deine Discord-Rollen"
},
"dashboard.discord-roles.role.big-creator": {
"message": "1M+ Downloads"
},
"dashboard.discord-roles.role.creator": {
"message": "Ersteller"
},
"dashboard.discord-roles.role.pride": {
"message": "Pride 2026"
},
"dashboard.head-title": {
"message": "Dashboard"
},
@@ -2303,9 +2291,6 @@
"landing.subheading": {
"message": "Entdecke, spiele und teile Minecraft-Inhalte über unsere Open-Source-Plattform, die für die Community entwickelt wurde."
},
"layout.action.analytics-events": {
"message": "Analytics-Events"
},
"layout.action.change-theme": {
"message": "Farbschema ändern"
},
@@ -2861,9 +2846,6 @@
"profile.bio.fallback.user": {
"message": "Ein Modrinth-Benutzer."
},
"profile.button.analytics": {
"message": "Benutzeranalysen anzeigen"
},
"profile.button.billing": {
"message": "Benutzerabrechnungen verwalten"
},
@@ -3290,6 +3272,9 @@
"project.moderation.thread.help-center-note.2": {
"message": "Falls du Hilfe benötigst oder weitere Fragen hast, besuche bitte das <help-center-link>Hilfezentrum von Modrinth</help-center-link> und klicke auf die blaue Sprechblase, um den Support zu kontaktieren."
},
"project.moderation.thread.moderator-see-user-ui-toggle": {
"message": "Mitglieder-UI anzeigen"
},
"project.moderation.thread.private-description": {
"message": "Dies ist eine private Konversation mit den Moderatoren von Modrinth. Diese können dir Nachrichten zu Problemen bezüglich dieses Projekts senden."
},
@@ -3407,9 +3392,6 @@
"project.stats.downloads-label": {
"message": "{count, plural, one {Download} other {Downloads}}"
},
"project.stats.followers-label": {
"message": "{count, plural, one {Follower} other {Follower}}"
},
"project.status.archived.message": {
"message": "{title} wurde archiviert. {title} wird keine weiteren Updates erhalten, es sei denn, der Autor entscheidet sich das Projekt zu dearchivieren."
},
@@ -3419,6 +3401,9 @@
"project.versions.withheld-versions-warning.description": {
"message": "{count, plural, one {Diese Version ist} other {Diese Versionen sind}} derzeit zurückgehalten und nicht öffentlich gelistet. Bitte stelle einen Nachweis bereit, dass du die Erlaubnis hast, bestimmte Dateien weiterzuverbreiten, die im Modpack in {count, plural, one {Version} other {Versionen}} enthalten sind."
},
"project.versions.withheld-versions-warning.resolve-button": {
"message": "Beheben"
},
"project.versions.withheld-versions-warning.title": {
"message": "{count, plural, one {Version {version_name} wurde} other {Versionen wurden}} aufgrund unbekannter eingebetteter Inhalte zurückgehalten"
},

Some files were not shown because too many files have changed in this diff Show More