Compare commits

..
Author SHA1 Message Date
Calum H.andGitHub a77e4af0ed Merge branch 'main' into test 2026-06-26 17:23:35 +01:00
Michael H. d019ac7fcf test: remove blacksmith 2026-06-26 17:45:37 +02:00
Michael H. 076aaac3eb test: remove mold 2026-06-26 17:40:16 +02:00
Michael H. 7ae71d04a6 test: remove sccache 2026-06-26 17:31:45 +02:00
962 changed files with 22685 additions and 94569 deletions
-1
View File
@@ -6,7 +6,6 @@ on:
env:
CARGO_TERM_COLOR: always
CARGO_INCREMENTAL: 0
SQLX_OFFLINE: true
jobs:
-22
View File
@@ -73,10 +73,6 @@ jobs:
docker:
runs-on: ${{ needs.skip-if-clean.outputs.internal == 'true' && 'blacksmith-4vcpu-ubuntu-2404' || 'ubuntu-latest' }}
env:
SCCACHE_DIR: ${{ needs.skip-if-clean.outputs.internal == 'true' && '/mnt/sccache' || '' }}
SCCACHE_CACHE_SIZE: ${{ needs.skip-if-clean.outputs.internal == 'true' && '10G' || '' }}
RUSTC_WRAPPER: ${{ needs.skip-if-clean.outputs.internal == 'true' && 'sccache' || '' }}
needs: [skip-if-clean]
if: ${{ needs.skip-if-clean.outputs.skip != 'true' }}
steps:
@@ -89,9 +85,6 @@ jobs:
rustflags: ''
cache: false
- name: Setup mold
uses: rui314/setup-mold@9c9c13bf4c3f1adef0cc596abc155580bcb04444 # v1 / Mold 2.41.0
- name: Cache Cargo registry and index
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
@@ -101,21 +94,6 @@ jobs:
~/.cargo/bin
key: ${{ runner.os }}-${{ runner.arch }}-cargo-${{ hashFiles('**/Cargo.lock') }}
- name: Ensure sccache directory exists
if: needs.skip-if-clean.outputs.internal == 'true'
run: sudo mkdir -p /mnt/sccache && sudo chown $(id -u):$(id -g) /mnt/sccache
- name: Mount sccache disk cache
if: needs.skip-if-clean.outputs.internal == 'true'
uses: useblacksmith/stickydisk@13af8883542ca949a717e70fef89d15edbb29d88 # v1.2.0
with:
key: ${{ github.repository }}-daedalus-sccache
path: /mnt/sccache
- name: Setup sccache
if: needs.skip-if-clean.outputs.internal == 'true'
uses: mozilla-actions/sccache-action@9e7fa8a12102821edf02ca5dbea1acd0f89a2696 # v0.0.10
- name: Build daedalus_client
run: cargo build --release --package daedalus_client
+22 -75
View File
@@ -16,10 +16,10 @@ on:
workflow_dispatch:
workflow_call:
inputs:
preview:
required: false
type: boolean
description: 'Whether this is a preview deployment'
environment:
required: true
type: string
description: 'The environment to deploy to (staging-preview or production-preview)'
head_sha:
required: false
type: string
@@ -28,13 +28,9 @@ on:
required: false
type: string
description: 'The branch name to deploy (defaults to github.ref_name)'
branch_alias:
required: false
type: string
description: 'The persistent preview alias for the branch'
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}-${{ inputs.preview && 'preview' || 'push' }}
group: ${{ github.workflow }}-${{ github.ref }}-${{ inputs.environment || 'push' }}
cancel-in-progress: ${{ github.ref != 'refs/heads/main' && github.ref != 'refs/heads/prod' }}
jobs:
@@ -53,31 +49,23 @@ jobs:
- name: Configure environment
id: meta
env:
PREVIEW_INPUT: ${{ inputs.preview }}
ENV_INPUT: ${{ inputs.environment }}
REF: ${{ github.ref }}
DEPLOY_SHA: ${{ inputs.head_sha || github.sha }}
BRANCH_ALIAS: ${{ inputs.branch_alias }}
run: |
echo "cmd=deploy" >> $GITHUB_OUTPUT
SHA_SHORT="${DEPLOY_SHA::8}"
if [ "$PREVIEW_INPUT" = "true" ]; then
URL="https://git-$SHA_SHORT-frontend.modrinth.workers.dev"
STAGING_URL="https://git-$SHA_SHORT-staging-frontend.modrinth.workers.dev"
if [ "$ENV_INPUT" = "staging-preview" ]; then
echo "env=staging" >> $GITHUB_OUTPUT
echo "url=https://git-$SHA_SHORT-frontend-staging.modrinth.workers.dev" >> $GITHUB_OUTPUT
echo "cmd=versions upload --preview-alias git-$SHA_SHORT --var PREVIEW:true" >> $GITHUB_OUTPUT
elif [ "$ENV_INPUT" = "production-preview" ]; then
echo "env=production" >> $GITHUB_OUTPUT
echo "url=$URL" >> $GITHUB_OUTPUT
echo "staging_url=$STAGING_URL" >> $GITHUB_OUTPUT
echo "cmd=versions upload --preview-alias git-$SHA_SHORT --var PREVIEW:true --var CF_PAGES_URL:$URL" >> $GITHUB_OUTPUT
STAGING_VARS=$(jq -r '.env.staging.vars | to_entries[] | "--var \(.key):\(.value)"' ./apps/frontend/wrangler.jsonc | tr '\n' ' ')
echo "staging_cmd=versions upload --preview-alias git-$SHA_SHORT-staging ${STAGING_VARS}--var PREVIEW:true --var CF_PAGES_URL:$STAGING_URL" >> $GITHUB_OUTPUT
if [ -n "$BRANCH_ALIAS" ]; then
echo "branch_alias_url=https://$BRANCH_ALIAS-frontend.modrinth.workers.dev" >> $GITHUB_OUTPUT
echo "staging_branch_alias_url=https://$BRANCH_ALIAS-staging-frontend.modrinth.workers.dev" >> $GITHUB_OUTPUT
echo "staging_branch_alias_cmd=versions upload --preview-alias $BRANCH_ALIAS-staging ${STAGING_VARS}--var PREVIEW:true --var CF_PAGES_URL:https://$BRANCH_ALIAS-staging-frontend.modrinth.workers.dev" >> $GITHUB_OUTPUT
fi
echo "url=https://git-$SHA_SHORT-frontend.modrinth.workers.dev" >> $GITHUB_OUTPUT
echo "cmd=versions upload --preview-alias git-$SHA_SHORT --var PREVIEW:true" >> $GITHUB_OUTPUT
elif [ "$REF" = "refs/heads/main" ]; then
echo "env=staging" >> $GITHUB_OUTPUT
@@ -132,7 +120,7 @@ jobs:
CF_PAGES_COMMIT_SHA: ${{ inputs.head_sha || github.sha }}
CF_PAGES_URL: ${{ steps.meta.outputs.url }}
BUILD_ENV: ${{ steps.meta.outputs.env }}
PREVIEW: ${{ inputs.preview && 'true' || 'false' }}
PREVIEW: ${{ inputs.environment != '' && 'true' || 'false' }}
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
- name: Create Sentry release and upload sourcemaps
@@ -158,39 +146,6 @@ jobs:
wranglerVersion: '4.54.0'
command: ${{ steps.meta.outputs.cmd }}
- name: Deploy staging Cloudflare Worker preview
if: ${{ inputs.preview }}
uses: cloudflare/wrangler-action@9acf94ace14e7dc412b076f2c5c20b8ce93c79cd # v3.15.0
with:
apiToken: ${{ secrets.CF_API_TOKEN }}
accountId: ${{ secrets.CF_ACCOUNT_ID }}
workingDirectory: ./apps/frontend
packageManager: pnpm
wranglerVersion: '4.54.0'
command: ${{ steps.meta.outputs.staging_cmd }}
- name: Deploy persistent Cloudflare Worker preview
if: ${{ inputs.preview && inputs.branch_alias != '' }}
uses: cloudflare/wrangler-action@9acf94ace14e7dc412b076f2c5c20b8ce93c79cd # v3.15.0
with:
apiToken: ${{ secrets.CF_API_TOKEN }}
accountId: ${{ secrets.CF_ACCOUNT_ID }}
workingDirectory: ./apps/frontend
packageManager: pnpm
wranglerVersion: '4.54.0'
command: versions upload --preview-alias ${{ inputs.branch_alias }} --var PREVIEW:true --var CF_PAGES_URL:${{ steps.meta.outputs.branch_alias_url }}
- name: Deploy persistent staging Cloudflare Worker preview
if: ${{ inputs.preview && inputs.branch_alias != '' }}
uses: cloudflare/wrangler-action@9acf94ace14e7dc412b076f2c5c20b8ce93c79cd # v3.15.0
with:
apiToken: ${{ secrets.CF_API_TOKEN }}
accountId: ${{ secrets.CF_ACCOUNT_ID }}
workingDirectory: ./apps/frontend
packageManager: pnpm
wranglerVersion: '4.54.0'
command: ${{ steps.meta.outputs.staging_branch_alias_cmd }}
- name: Purge cache
if: github.ref == 'refs/heads/prod'
run: |
@@ -200,22 +155,14 @@ jobs:
--data '{"hosts": ["modrinth.com", "www.modrinth.com", "staging.modrinth.com"]}' \
https://api.cloudflare.com/client/v4/zones/e39df17b9c4ef44cbce2646346ee6d33/purge_cache
- name: Write deployment URLs to files
if: ${{ inputs.preview }}
- name: Write deployment URL to file
if: ${{ inputs.environment != '' }}
run: |
echo "${{ steps.meta.outputs.url }}" > deployment-url-production.txt
echo "${{ steps.meta.outputs.staging_url }}" > deployment-url-staging.txt
echo "${{ steps.meta.outputs.url }}" > deployment-url-${{ inputs.environment }}.txt
- name: Upload production deployment URL
if: ${{ inputs.preview }}
- name: Upload deployment URL
if: ${{ inputs.environment != '' }}
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: deployment-url-production
path: deployment-url-production.txt
- name: Upload staging deployment URL
if: ${{ inputs.preview }}
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: deployment-url-staging
path: deployment-url-staging.txt
name: deployment-url-${{ inputs.environment }}
path: deployment-url-${{ inputs.environment }}.txt
+17 -68
View File
@@ -12,33 +12,23 @@ on:
- '.github/workflows/frontend-preview.yml'
jobs:
metadata:
if: github.repository_owner == 'modrinth' && github.event.pull_request.head.repo.full_name == github.repository
runs-on: ubuntu-latest
outputs:
branch-alias: ${{ steps.meta.outputs.branch_alias }}
steps:
- name: Configure branch alias
id: meta
run: echo "branch_alias=pr-${{ github.event.pull_request.number }}" >> $GITHUB_OUTPUT
deploy:
if: github.repository_owner == 'modrinth' && github.event.pull_request.head.repo.full_name == github.repository
needs: metadata
uses: ./.github/workflows/frontend-deploy.yml
secrets: inherit
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}-deploy
group: ${{ github.workflow }}-${{ github.ref }}-${{ matrix.environment }}
cancel-in-progress: true
strategy:
matrix:
environment: [staging-preview, production-preview]
with:
preview: true
environment: ${{ matrix.environment }}
head_sha: ${{ github.event.pull_request.head.sha }}
head_ref: ${{ github.event.pull_request.head.ref }}
branch_alias: ${{ needs.metadata.outputs.branch-alias }}
deploy-storybook:
if: github.repository_owner == 'modrinth' && github.event.pull_request.head.repo.full_name == github.repository
needs: metadata
runs-on: blacksmith-2vcpu-ubuntu-2404
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}-storybook
@@ -76,8 +66,6 @@ jobs:
- name: Build Storybook
working-directory: ./packages/ui
env:
NODE_OPTIONS: --max-old-space-size=6144
run: pnpm run build-storybook
- name: Configure short SHA
@@ -96,26 +84,10 @@ jobs:
wranglerVersion: '4.54.0'
command: versions upload --preview-alias git-${{ steps.meta.outputs.sha_short }}
- name: Deploy persistent Storybook preview
uses: cloudflare/wrangler-action@9acf94ace14e7dc412b076f2c5c20b8ce93c79cd # v3.15.0
env:
NODE_OPTIONS: --max-old-space-size=6144
with:
apiToken: ${{ secrets.CF_API_TOKEN }}
accountId: ${{ secrets.CF_ACCOUNT_ID }}
workingDirectory: ./packages/ui
packageManager: pnpm
wranglerVersion: '4.54.0'
command: versions upload --preview-alias ${{ needs.metadata.outputs.branch-alias }}
comment:
if: |
!cancelled()
&& github.repository_owner == 'modrinth'
&& github.event.pull_request.head.repo.full_name == github.repository
&& needs.deploy.result == 'success'
if: github.repository_owner == 'modrinth' && github.event.pull_request.head.repo.full_name == github.repository
runs-on: ubuntu-latest
needs: [metadata, deploy, deploy-storybook]
needs: [deploy, deploy-storybook]
steps:
- name: Download deployment URLs
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
@@ -127,29 +99,16 @@ jobs:
id: urls
env:
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
BRANCH_ALIAS: ${{ needs.metadata.outputs.branch-alias }}
STORYBOOK_RESULT: ${{ needs.deploy-storybook.result }}
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
run: |
STAGING_SHA_URL=$(cat deployment-url-staging.txt)
PRODUCTION_SHA_URL=$(cat deployment-url-production.txt)
STAGING_BRANCH_URL="https://$BRANCH_ALIAS-staging-frontend.modrinth.workers.dev"
PRODUCTION_BRANCH_URL="https://$BRANCH_ALIAS-frontend.modrinth.workers.dev"
STAGING_PREVIEW_URL=$(cat deployment-url-staging-preview.txt)
PRODUCTION_PREVIEW_URL=$(cat deployment-url-production-preview.txt)
if [ "$STORYBOOK_RESULT" = "success" ]; then
STORYBOOK_SHA_URL="https://git-${HEAD_SHA::8}-storybook.modrinth.workers.dev"
STORYBOOK_BRANCH_URL="https://$BRANCH_ALIAS-storybook.modrinth.workers.dev"
else
STORYBOOK_SHA_URL="⚠️ Deployment $STORYBOOK_RESULT ([logs]($RUN_URL))"
STORYBOOK_BRANCH_URL="⚠️ Deployment $STORYBOOK_RESULT ([logs]($RUN_URL))"
fi
echo "Production preview URL: $PRODUCTION_PREVIEW_URL"
echo "Staging preview URL: $STAGING_PREVIEW_URL"
echo "staging-sha-url=$STAGING_SHA_URL" >> $GITHUB_OUTPUT
echo "production-sha-url=$PRODUCTION_SHA_URL" >> $GITHUB_OUTPUT
echo "storybook-sha-url=$STORYBOOK_SHA_URL" >> $GITHUB_OUTPUT
echo "staging-branch-url=$STAGING_BRANCH_URL" >> $GITHUB_OUTPUT
echo "production-branch-url=$PRODUCTION_BRANCH_URL" >> $GITHUB_OUTPUT
echo "storybook-branch-url=$STORYBOOK_BRANCH_URL" >> $GITHUB_OUTPUT
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
- name: Find comment
if: github.event_name == 'pull_request'
@@ -172,19 +131,9 @@ jobs:
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 }})
### Persistent
| Environment | URL |
|-------------|-----|
| staging | ${{ steps.urls.outputs.staging-branch-url }} |
| production | ${{ steps.urls.outputs.production-branch-url }} |
| storybook | ${{ steps.urls.outputs.storybook-branch-url }} |
### Latest commit
| Environment | URL |
|-------------|-----|
| staging | ${{ steps.urls.outputs.staging-sha-url }} |
| production | ${{ steps.urls.outputs.production-sha-url }} |
| storybook | ${{ steps.urls.outputs.storybook-sha-url }} |
| staging | ${{ steps.urls.outputs.staging-preview-url }} |
| production | ${{ steps.urls.outputs.production-preview-url }} |
| storybook | ${{ steps.urls.outputs.storybook-preview-url }} |
edit-mode: replace
+4 -27
View File
@@ -76,10 +76,6 @@ jobs:
env:
SQLX_OFFLINE: 'true'
GIT_HASH: ${{ github.sha }}
SCCACHE_DIR: ${{ needs.skip-if-clean.outputs.internal == 'true' && '/mnt/sccache' || '' }}
SCCACHE_CACHE_SIZE: ${{ needs.skip-if-clean.outputs.internal == 'true' && '10G' || '' }}
RUSTC_WRAPPER: ${{ needs.skip-if-clean.outputs.internal == 'true' && 'sccache' || '' }}
RUST_MIN_STACK: '16777216'
steps:
- name: Check out code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
@@ -90,15 +86,11 @@ jobs:
rustflags: ''
cache: false
- name: Setup mold
uses: rui314/setup-mold@9c9c13bf4c3f1adef0cc596abc155580bcb04444 # v1 / Mold 2.41.0
- name: Install build dependencies
run: sudo apt-get update && sudo apt-get install -y --no-install-recommends cmake libcurl4-openssl-dev
#uses: awalsh128/cache-apt-pkgs-action@acb598e5ddbc6f68a970c5da0688d2f3a9f04d05 # v1.6.0
#with:
# packages: cmake libcurl4-openssl-dev
# version: v2 # cache key
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
@@ -109,21 +101,6 @@ jobs:
~/.cargo/bin
key: ${{ runner.os }}-${{ runner.arch }}-cargo-${{ hashFiles('**/Cargo.lock') }}
- name: Ensure sccache directory exists
if: needs.skip-if-clean.outputs.internal == 'true'
run: sudo mkdir -p /mnt/sccache && sudo chown $(id -u):$(id -g) /mnt/sccache
- name: Mount sccache disk cache
if: needs.skip-if-clean.outputs.internal == 'true'
uses: useblacksmith/stickydisk@13af8883542ca949a717e70fef89d15edbb29d88 # v1.2.0
with:
key: ${{ github.repository }}-labrinth-sccache
path: /mnt/sccache
- name: Setup sccache
if: needs.skip-if-clean.outputs.internal == 'true'
uses: mozilla-actions/sccache-action@9e7fa8a12102821edf02ca5dbea1acd0f89a2696 # v0.0.10
- name: Build labrinth
run: cargo build --profile release-labrinth --package labrinth
-17
View File
@@ -44,16 +44,6 @@ jobs:
name: Build
env:
VITE_STRIPE_PUBLISHABLE_KEY: pk_live_51JbFxJJygY5LJFfKLVVldb10HlLt24p421OWRsTOWc5sXYFOnFUXWieSc6HD3PHo25ktx8db1WcHr36XGFvZFVUz00V9ixrCs5
# SCCACHE_DIR: '/mnt/sccache'
# SCCACHE_CACHE_SIZE: '10G'
# SCCACHE_MULTILEVEL_CHAIN: 'disk,s3'
SCCACHE_S3_KEY_PREFIX: '${{ github.repository }}/'
SCCACHE_BUCKET: ${{ secrets.SCCACHE_BUCKET }}
SCCACHE_REGION: ${{ secrets.SCCACHE_REGION }}
SCCACHE_ENDPOINT: ${{ secrets.SCCACHE_ENDPOINT }}
AWS_ACCESS_KEY_ID: ${{ secrets.SCCACHE_S3_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.SCCACHE_S3_SECRET_ACCESS_KEY }}
RUSTC_WRAPPER: 'sccache'
strategy:
fail-fast: false
matrix:
@@ -84,13 +74,6 @@ jobs:
rustflags: ''
target: ${{ contains(matrix.platform, 'macos') && 'x86_64-apple-darwin' || '' }}
- name: Setup mold
if: contains(matrix.platform, 'ubuntu')
uses: rui314/setup-mold@9c9c13bf4c3f1adef0cc596abc155580bcb04444 # v1 / Mold 2.41.0
- name: Setup sccache
uses: mozilla-actions/sccache-action@9e7fa8a12102821edf02ca5dbea1acd0f89a2696 # v0.0.10
- name: Enable Corepack
run: corepack enable
+2 -43
View File
@@ -18,25 +18,11 @@ jobs:
runs-on: ubuntu-latest
outputs:
skip: ${{ steps.check.outputs.skip }}
internal: ${{ steps.check-internal.outputs.internal }}
if: ${{ always() }}
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Check if workflow runs on an internal branch
id: check-internal
env:
EVENT_NAME: ${{ github.event_name }}
HEAD_REPO: ${{ github.event.pull_request.head.repo.full_name }}
REPO: ${{ github.repository }}
run: |
if [ "$EVENT_NAME" != "pull_request" ] || [ "$HEAD_REPO" = "$REPO" ]; then
echo "internal=true" >> $GITHUB_OUTPUT
else
echo "internal=false" >> $GITHUB_OUTPUT
fi
- name: Merge Queue CI Check Skipper
id: merge-queue-ci-skipper
uses: ./.github/merge-queue-ci-skipper
@@ -62,7 +48,7 @@ jobs:
build:
name: Lint and Test
runs-on: ${{ needs.skip-if-clean.outputs.internal == 'true' && 'blacksmith-4vcpu-ubuntu-2404' || 'ubuntu-latest' }}
runs-on: ubuntu-latest
needs: [skip-if-clean]
if: ${{ needs.skip-if-clean.outputs.skip != 'true' }}
@@ -76,19 +62,6 @@ jobs:
# since we don't want warnings to become errors
# while developing)
RUSTFLAGS: -Dwarnings
# sccache config (only populated for internal branches; secrets aren't
# available to forked PRs, and blacksmith stickydisk requires a
# blacksmith runner)
SCCACHE_DIR: ${{ needs.skip-if-clean.outputs.internal == 'true' && '/mnt/sccache' || '' }}
SCCACHE_CACHE_SIZE: ${{ needs.skip-if-clean.outputs.internal == 'true' && '10G' || '' }}
RUSTC_WRAPPER: ${{ needs.skip-if-clean.outputs.internal == 'true' && 'sccache' || '' }}
CARGO_INCREMENTAL: 0
# Redis config for tests
REDIS_TOPOLOGY: cluster
REDIS_CONNECTION_TYPE: multiplexed
REDIS_URL: redis://127.0.0.1:7000,redis://127.0.0.1:7001,redis://127.0.0.1:7002,redis://127.0.0.1:7003,redis://127.0.0.1:7004,redis://127.0.0.1:7005
# Avoid stack overflows in tests
RUST_MIN_STACK: 16777216
steps:
- name: Check out code
@@ -129,9 +102,6 @@ jobs:
components: clippy, rustfmt
cache: false
- name: Setup mold
uses: rui314/setup-mold@9c9c13bf4c3f1adef0cc596abc155580bcb04444 # v1 / Mold 2.41.0
- name: Cache Cargo registry and index
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae #v5.0.5
with:
@@ -141,17 +111,6 @@ jobs:
~/.cargo/bin
key: ${{ runner.os }}-${{ runner.arch }}-cargo-${{ hashFiles('**/Cargo.lock') }}
- name: Mount sccache disk cache
if: needs.skip-if-clean.outputs.internal == 'true'
uses: useblacksmith/stickydisk@13af8883542ca949a717e70fef89d15edbb29d88 # v1.2.0
with:
key: ${{ github.repository }}-turbo-sccache
path: /mnt/sccache
- name: Setup sccache
if: needs.skip-if-clean.outputs.internal == 'true'
uses: mozilla-actions/sccache-action@9e7fa8a12102821edf02ca5dbea1acd0f89a2696 # v0.0.10
- name: Setup binstall
uses: cargo-bins/cargo-binstall@dc19f1e48450eefe5a29b8da6c6b00a87d730b37 # v1.18.1
@@ -192,7 +151,7 @@ jobs:
- name: Start services
if: steps.check-labrinth.outputs.needs_services == 'true'
run: docker compose --profile clustered-redis up --wait
run: docker compose up --wait
- name: Setup labrinth environment and database
if: steps.check-labrinth.outputs.needs_services == 'true'
Generated
+105 -356
View File
@@ -59,9 +59,9 @@ dependencies = [
[[package]]
name = "actix-http"
version = "3.13.1"
version = "3.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48e2faa3e7418ed780cca54829d32782a4008a077230f67457caa063415e99c2"
checksum = "7926860314cbe2fb5d1f13731e387ab43bd32bca224e82e6e2db85de0a3dba49"
dependencies = [
"actix-codec",
"actix-rt",
@@ -75,7 +75,7 @@ dependencies = [
"derive_more 2.1.1",
"encoding_rs",
"flate2",
"foldhash 0.2.0",
"foldhash 0.1.5",
"futures-core",
"h2 0.3.27",
"http 0.2.12",
@@ -87,8 +87,8 @@ dependencies = [
"mime",
"percent-encoding",
"pin-project-lite",
"rand 0.10.1",
"sha1 0.11.0",
"rand 0.9.2",
"sha1",
"smallvec",
"tokio",
"tokio-util",
@@ -146,9 +146,9 @@ dependencies = [
[[package]]
name = "actix-router"
version = "0.5.4"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14f8c75c51892f18d9c46150c5ac7beb81c95f78c8b83a634d49f4ca32551fe7"
checksum = "13d324164c51f63867b57e73ba5936ea151b8a41a1d23d1031eeb9f70d0236f8"
dependencies = [
"bytestring",
"cfg-if",
@@ -209,9 +209,9 @@ dependencies = [
[[package]]
name = "actix-web"
version = "4.14.0"
version = "4.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df09e2d9239703dd64056359c920c7f3fba6535ec61a0059e0f44e095ffe02b4"
checksum = "a597b77b5c6d6a1e1097fddde329a83665e25c5437c696a3a9a4aa514a614dea"
dependencies = [
"actix-codec",
"actix-http",
@@ -228,7 +228,7 @@ dependencies = [
"cookie 0.16.2",
"derive_more 2.1.1",
"encoding_rs",
"foldhash 0.2.0",
"foldhash 0.1.5",
"futures-core",
"futures-util",
"impl-more",
@@ -244,7 +244,7 @@ dependencies = [
"serde_json",
"serde_urlencoded",
"smallvec",
"socket2 0.6.5",
"socket2 0.5.10",
"time",
"tracing",
"url",
@@ -458,12 +458,6 @@ version = "1.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457"
[[package]]
name = "arcstr"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "03918c3dbd7701a85c6b9887732e2921175f26c350b4563841d0958c21d57e6d"
[[package]]
name = "arg_enum_proc_macro"
version = "0.3.4"
@@ -1133,7 +1127,7 @@ dependencies = [
"http-body-util",
"md-5",
"pin-project-lite",
"sha1 0.10.6",
"sha1",
"sha2",
"tracing",
]
@@ -1505,7 +1499,7 @@ version = "0.10.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe"
dependencies = [
"digest 0.10.7",
"digest",
]
[[package]]
@@ -1517,15 +1511,6 @@ dependencies = [
"generic-array",
]
[[package]]
name = "block-buffer"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2f6c7dbe95a6ed67ad9f18e57daf93a2f034c524b99fd2b76d18fdfeb6660aa"
dependencies = [
"hybrid-array",
]
[[package]]
name = "block2"
version = "0.5.1"
@@ -1634,31 +1619,6 @@ dependencies = [
"serde_with",
]
[[package]]
name = "bon"
version = "3.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a602c73c7b0148ec6d12af6fd5cc7a46e2eacc8878271a999abac56eed12f561"
dependencies = [
"bon-macros",
"rustversion",
]
[[package]]
name = "bon-macros"
version = "3.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6dee98b0db6a962de883bf5d20362dee4d7ca0d12fe39a7c6c73c844e1cd7c1f"
dependencies = [
"darling 0.23.0",
"ident_case",
"prettyplease",
"proc-macro2",
"quote",
"rustversion",
"syn 2.0.106",
]
[[package]]
name = "borsh"
version = "1.5.7"
@@ -2139,15 +2099,6 @@ dependencies = [
"cc",
]
[[package]]
name = "cobs"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fa961b519f0b462e3a3b4a34b64d119eeaca1d59af726fe450bbba07a9fc0a1"
dependencies = [
"thiserror 2.0.17",
]
[[package]]
name = "color-eyre"
version = "0.6.5"
@@ -2273,12 +2224,6 @@ version = "0.9.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8"
[[package]]
name = "const-oid"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a6ef517f0926dd24a1582492c791b6a4818a4d94e789a334894aa15b0d12f55c"
[[package]]
name = "const-random"
version = "0.1.18"
@@ -2479,17 +2424,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2fd92aca2c6001b1bf5ba0ff84ee74ec8501b52bbef0cac80bf25a6c1d87a83d"
dependencies = [
"crc",
"digest 0.10.7",
"digest",
"rustversion",
"spin 0.10.0",
]
[[package]]
name = "crc16"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "338089f42c427b86394a5ee60ff321da23a5c89c9d89514c829687b26359fcff"
[[package]]
name = "crc32fast"
version = "1.5.0"
@@ -2576,15 +2515,6 @@ dependencies = [
"typenum",
]
[[package]]
name = "crypto-common"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce6e4c961d6cd6c9a86db418387425e8bdeaf05b3c8bc1411e6dca4c252f1453"
dependencies = [
"hybrid-array",
]
[[package]]
name = "cssparser"
version = "0.29.6"
@@ -2813,7 +2743,7 @@ checksum = "be1e0bca6c3637f992fc1cc7cbc52a78c1ef6db076dbf1059c4323d6a2048376"
[[package]]
name = "deadpool"
version = "0.12.3"
source = "git+https://github.com/modrinth/deadpool?rev=c7209b086829572f368f1194aba54bdea8dc540b#c7209b086829572f368f1194aba54bdea8dc540b"
source = "git+https://github.com/modrinth/deadpool?rev=db5fb00b036ecc8fe5f18853c559b745ffe47bde#db5fb00b036ecc8fe5f18853c559b745ffe47bde"
dependencies = [
"deadpool-runtime",
"num_cpus",
@@ -2823,8 +2753,8 @@ dependencies = [
[[package]]
name = "deadpool-redis"
version = "0.23.0"
source = "git+https://github.com/modrinth/deadpool?rev=c7209b086829572f368f1194aba54bdea8dc540b#c7209b086829572f368f1194aba54bdea8dc540b"
version = "0.22.1"
source = "git+https://github.com/modrinth/deadpool?rev=db5fb00b036ecc8fe5f18853c559b745ffe47bde#db5fb00b036ecc8fe5f18853c559b745ffe47bde"
dependencies = [
"deadpool",
"redis",
@@ -2834,7 +2764,7 @@ dependencies = [
[[package]]
name = "deadpool-runtime"
version = "0.1.5"
source = "git+https://github.com/modrinth/deadpool?rev=c7209b086829572f368f1194aba54bdea8dc540b#c7209b086829572f368f1194aba54bdea8dc540b"
source = "git+https://github.com/modrinth/deadpool?rev=db5fb00b036ecc8fe5f18853c559b745ffe47bde#db5fb00b036ecc8fe5f18853c559b745ffe47bde"
dependencies = [
"tokio",
]
@@ -2861,7 +2791,7 @@ version = "0.7.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb"
dependencies = [
"const-oid 0.9.6",
"const-oid",
"pem-rfc7468",
"zeroize",
]
@@ -2974,23 +2904,12 @@ version = "0.10.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
dependencies = [
"block-buffer 0.10.4",
"const-oid 0.9.6",
"crypto-common 0.1.6",
"block-buffer",
"const-oid",
"crypto-common",
"subtle",
]
[[package]]
name = "digest"
version = "0.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2"
dependencies = [
"block-buffer 0.12.1",
"const-oid 0.10.2",
"crypto-common 0.2.2",
]
[[package]]
name = "directories"
version = "6.0.0"
@@ -3208,7 +3127,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca"
dependencies = [
"der",
"digest 0.10.7",
"digest",
"elliptic-curve",
"rfc6979",
"signature",
@@ -3232,7 +3151,7 @@ checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47"
dependencies = [
"base16ct",
"crypto-bigint",
"digest 0.10.7",
"digest",
"ff",
"generic-array",
"group",
@@ -3280,18 +3199,6 @@ version = "1.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ef6b89e5b37196644d8796de5268852ff179b44e96276cf4290264843743bb7"
[[package]]
name = "embedded-io"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef1a6892d9eef45c8fa6b9e0086428a2cca8491aca8f787c534a3d6d0bcb3ced"
[[package]]
name = "embedded-io"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d"
[[package]]
name = "encode_unicode"
version = "1.0.0"
@@ -3868,12 +3775,6 @@ version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988"
[[package]]
name = "futures-timer"
version = "3.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af43fadb8a98512d547e37b4e92e0ced13e205c061b87b4623eff01d918d6968"
[[package]]
name = "futures-util"
version = "0.3.31"
@@ -4185,29 +4086,6 @@ dependencies = [
"system-deps",
]
[[package]]
name = "governor"
version = "0.10.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9efcab3c1958580ff1f25a2a41be1668f7603d849bb63af523b208a3cc1223b8"
dependencies = [
"cfg-if",
"dashmap",
"futures-sink",
"futures-timer",
"futures-util",
"getrandom 0.3.3",
"hashbrown 0.16.0",
"nonzero_ext",
"parking_lot",
"portable-atomic",
"quanta",
"rand 0.9.2",
"smallvec",
"spinning_top",
"web-time",
]
[[package]]
name = "group"
version = "0.13.0"
@@ -4524,7 +4402,7 @@ version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e"
dependencies = [
"digest 0.10.7",
"digest",
]
[[package]]
@@ -4654,15 +4532,6 @@ version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
[[package]]
name = "hybrid-array"
version = "0.4.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "818356c5132c1fede50f837ca96afbe78ff42413047f4abb886217845e1b6c8c"
dependencies = [
"typenum",
]
[[package]]
name = "hyper"
version = "0.14.32"
@@ -4806,7 +4675,7 @@ dependencies = [
"libc",
"percent-encoding",
"pin-project-lite",
"socket2 0.5.10",
"socket2 0.6.1",
"system-configuration",
"tokio",
"tower-service",
@@ -5024,9 +4893,9 @@ checksum = "e7c5cedc30da3a610cac6b4ba17597bdf7152cf974e8aab3afb3d54455e371c8"
[[package]]
name = "impl-more"
version = "0.3.1"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "35a84fd5aa25fae5c0f4a33d9cac2ca017fc622cbd089be2229993514990f870"
checksum = "e8a5a9a0ff0086c7a148acb942baaabeadf9504d10400b5a05645853729b9cd2"
[[package]]
name = "indenter"
@@ -5147,6 +5016,17 @@ dependencies = [
"syn 2.0.106",
]
[[package]]
name = "io-uring"
version = "0.7.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4d09b98f7eace8982db770e4408e7470b028ce513ac28fecdc6bf4c30fe92b62"
dependencies = [
"bitflags 2.9.4",
"cfg-if",
"libc",
]
[[package]]
name = "ipconfig"
version = "0.3.2"
@@ -5471,6 +5351,7 @@ dependencies = [
"color-thief",
"const_format",
"dashmap",
"deadpool-redis",
"derive_more 2.1.1",
"dotenv-build",
"dotenvy",
@@ -5495,10 +5376,8 @@ dependencies = [
"modrinth-util",
"muralpay",
"murmur2",
"neverbounce",
"paste",
"path-util",
"postcard",
"prometheus",
"quick-xml 0.38.3",
"rand 0.8.5",
@@ -5511,12 +5390,11 @@ dependencies = [
"rust_iso3166",
"rustls 0.23.32",
"rusty-money",
"scalar_api_reference",
"sentry",
"serde",
"serde_json",
"serde_with",
"sha1 0.10.6",
"sha1",
"sha2",
"spdx",
"sqlx",
@@ -5533,13 +5411,14 @@ dependencies = [
"url",
"urlencoding",
"utoipa",
"utoipa-actix-web",
"utoipa-scalar",
"uuid 1.23.3",
"validator",
"webauthn-rs",
"webauthn-rs-proto",
"webp",
"woothee",
"xredis",
"yaserde",
"zip 6.0.0",
"zxcvbn",
@@ -5605,7 +5484,7 @@ dependencies = [
"quoted_printable",
"rustls 0.23.32",
"rustls-native-certs 0.8.1",
"socket2 0.6.5",
"socket2 0.6.1",
"tokio",
"tokio-rustls 0.26.4",
"url",
@@ -5643,9 +5522,9 @@ checksum = "2c4a545a15244c7d945065b5d392b2d2d7f21526fba56ce51467b06ed445e8f7"
[[package]]
name = "libc"
version = "0.2.186"
version = "0.2.177"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66"
checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976"
[[package]]
name = "libfuzzer-sys"
@@ -5943,7 +5822,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf"
dependencies = [
"cfg-if",
"digest 0.10.7",
"digest",
]
[[package]]
@@ -6059,14 +5938,14 @@ dependencies = [
[[package]]
name = "mio"
version = "1.2.2"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "30d65c71f1ce40ab09135ce117d742b9f8a19ff91a41a8b57ed50bc2de59c427"
checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c"
dependencies = [
"libc",
"log",
"wasi 0.11.1+wasi-snapshot-preview1",
"windows-sys 0.61.2",
"windows-sys 0.59.0",
]
[[package]]
@@ -6277,14 +6156,6 @@ dependencies = [
"jni-sys",
]
[[package]]
name = "neverbounce"
version = "0.1.0"
dependencies = [
"reqwest 0.12.24",
"serde",
]
[[package]]
name = "new_debug_unreachable"
version = "1.0.6"
@@ -6329,12 +6200,6 @@ dependencies = [
"memchr",
]
[[package]]
name = "nonzero_ext"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38bf9645c8b145698bb0b18a4637dcacbc421ea49bef2317e4fd8065a387cf21"
[[package]]
name = "noop_proc_macro"
version = "0.3.0"
@@ -7559,18 +7424,6 @@ version = "1.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483"
[[package]]
name = "postcard"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6764c3b5dd454e283a30e6dfe78e9b31096d9e32036b5d1eaac7a6119ccb9a24"
dependencies = [
"cobs",
"embedded-io 0.4.0",
"embedded-io 0.6.1",
"serde",
]
[[package]]
name = "potential_utf"
version = "0.1.3"
@@ -7935,21 +7788,6 @@ dependencies = [
"bytemuck",
]
[[package]]
name = "quanta"
version = "0.12.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3ab5a9d756f0d97bdc89019bd2e4ea098cf9cde50ee7564dde6b81ccc8f06c7"
dependencies = [
"crossbeam-utils",
"libc",
"once_cell",
"raw-cpuid",
"wasi 0.11.1+wasi-snapshot-preview1",
"web-sys",
"winapi",
]
[[package]]
name = "quartz_nbt"
version = "0.2.9"
@@ -8014,7 +7852,7 @@ dependencies = [
"quinn-udp",
"rustc-hash",
"rustls 0.23.32",
"socket2 0.5.10",
"socket2 0.6.1",
"thiserror 2.0.17",
"tokio",
"tracing",
@@ -8051,7 +7889,7 @@ dependencies = [
"cfg_aliases",
"libc",
"once_cell",
"socket2 0.5.10",
"socket2 0.6.1",
"tracing",
"windows-sys 0.60.2",
]
@@ -8277,15 +8115,6 @@ dependencies = [
"rgb",
]
[[package]]
name = "raw-cpuid"
version = "11.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "498cd0dc59d73224351ee52a95fee0f1a617a2eae0e7d9d720cc622c73a54186"
dependencies = [
"bitflags 2.9.4",
]
[[package]]
name = "raw-window-handle"
version = "0.6.2"
@@ -8345,32 +8174,26 @@ dependencies = [
[[package]]
name = "redis"
version = "1.4.1"
version = "0.32.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0b9503711b03773e43b31668c7b5bd279ee7cd9b7d18cff7c23a42cc1d08e5a"
checksum = "014cc767fefab6a3e798ca45112bccad9c6e0e218fbd49720042716c73cfef44"
dependencies = [
"ahash 0.8.12",
"arcstr",
"async-lock",
"bytes",
"cfg-if",
"combine",
"crc16",
"futures-util",
"itoa",
"log",
"num-bigint",
"percent-encoding",
"pin-project-lite",
"r2d2",
"rand 0.10.1",
"ryu",
"sha1_smol",
"socket2 0.6.5",
"socket2 0.6.1",
"tokio",
"tokio-util",
"url",
"xxhash-rust",
]
[[package]]
@@ -8671,8 +8494,8 @@ version = "0.9.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78928ac1ed176a5ca1d17e578a1825f3d81ca54cf41053a592584b020cfd691b"
dependencies = [
"const-oid 0.9.6",
"digest 0.10.7",
"const-oid",
"digest",
"num-bigint-dig",
"num-integer",
"num-traits",
@@ -8685,40 +8508,6 @@ dependencies = [
"zeroize",
]
[[package]]
name = "rust-embed"
version = "8.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04113cb9355a377d83f06ef1f0a45b8ab8cd7d8b1288160717d66df5c7988d27"
dependencies = [
"rust-embed-impl",
"rust-embed-utils",
"walkdir",
]
[[package]]
name = "rust-embed-impl"
version = "8.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da0902e4c7c8e997159ab384e6d0fc91c221375f6894346ae107f47dd0f3ccaa"
dependencies = [
"proc-macro2",
"quote",
"rust-embed-utils",
"syn 2.0.106",
"walkdir",
]
[[package]]
name = "rust-embed-utils"
version = "8.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5bcdef0be6fe7f6fa333b1073c949729274b05f123a0ad7efcb8efd878e5c3b1"
dependencies = [
"sha2",
"walkdir",
]
[[package]]
name = "rust-ini"
version = "0.21.3"
@@ -9038,18 +8827,6 @@ dependencies = [
"winapi-util",
]
[[package]]
name = "scalar_api_reference"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d67c85edd56c41f364603eff688a330f01dee99bdfc84c005d3bc3d0721d6b2"
dependencies = [
"actix-web",
"rust-embed",
"serde",
"serde_json",
]
[[package]]
name = "scc"
version = "2.4.0"
@@ -9475,15 +9252,15 @@ dependencies = [
[[package]]
name = "serde_json"
version = "1.0.150"
version = "1.0.145"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9"
checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c"
dependencies = [
"itoa",
"memchr",
"ryu",
"serde",
"serde_core",
"zmij",
]
[[package]]
@@ -9678,18 +9455,7 @@ checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba"
dependencies = [
"cfg-if",
"cpufeatures 0.2.17",
"digest 0.10.7",
]
[[package]]
name = "sha1"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aacc4cc499359472b4abe1bf11d0b12e688af9a805fa5e3016f9a386dc2d0214"
dependencies = [
"cfg-if",
"cpufeatures 0.3.0",
"digest 0.11.3",
"digest",
]
[[package]]
@@ -9706,7 +9472,7 @@ checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283"
dependencies = [
"cfg-if",
"cpufeatures 0.2.17",
"digest 0.10.7",
"digest",
]
[[package]]
@@ -9739,7 +9505,7 @@ version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de"
dependencies = [
"digest 0.10.7",
"digest",
"rand_core 0.6.4",
]
@@ -9823,12 +9589,12 @@ dependencies = [
[[package]]
name = "socket2"
version = "0.6.5"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3d1e2c7f27f8d4cb10542a02c49005dbd6e93095799d6f3be745fae9f8fedd4"
checksum = "17129e116933cf371d018bb80ae557e889637989d8638274fb25622827b03881"
dependencies = [
"libc",
"windows-sys 0.61.2",
"windows-sys 0.60.2",
]
[[package]]
@@ -9903,15 +9669,6 @@ version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d5fe4ccb98d9c292d56fec89a5e07da7fc4cf0dc11e156b41793132775d3e591"
[[package]]
name = "spinning_top"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d96d2d1d716fb500937168cc09353ffdc7a012be8475ac7308e1bdf0e3923300"
dependencies = [
"lock_api",
]
[[package]]
name = "spki"
version = "0.7.3"
@@ -10025,7 +9782,7 @@ dependencies = [
"bytes",
"chrono",
"crc",
"digest 0.10.7",
"digest",
"dotenvy",
"either",
"futures-channel",
@@ -10046,7 +9803,7 @@ dependencies = [
"rsa",
"rust_decimal",
"serde",
"sha1 0.10.6",
"sha1",
"sha2",
"smallvec",
"sqlx-core",
@@ -10992,7 +10749,6 @@ dependencies = [
"async-walkdir",
"async_zip",
"base64 0.22.1",
"bon",
"bytemuck",
"bytes",
"chardetng",
@@ -11014,10 +10770,8 @@ dependencies = [
"fs4",
"futures",
"futures-lite 2.6.1",
"governor",
"heck 0.5.0",
"hickory-resolver 0.25.2",
"httpdate",
"indicatif",
"itertools 0.14.0",
"modrinth-content-management",
@@ -11278,27 +11032,30 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]]
name = "tokio"
version = "1.53.0"
version = "1.47.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d988bcd52dbe076d3d46903332f58c912b87a2c49b1428419a5845154762ffee"
checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038"
dependencies = [
"backtrace",
"bytes",
"io-uring",
"libc",
"mio",
"parking_lot",
"pin-project-lite",
"signal-hook-registry",
"socket2 0.6.5",
"slab",
"socket2 0.6.1",
"tokio-macros",
"tracing",
"windows-sys 0.61.2",
"windows-sys 0.59.0",
]
[[package]]
name = "tokio-macros"
version = "2.7.1"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6328af13490e73a9b4694030fafd93f8c8c6a9dede33e821c3fc63eddf8042ba"
checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8"
dependencies = [
"proc-macro2",
"quote",
@@ -11502,7 +11259,7 @@ dependencies = [
"hyper-util",
"percent-encoding",
"pin-project",
"socket2 0.6.5",
"socket2 0.6.1",
"sync_wrapper",
"tokio",
"tokio-stream",
@@ -11533,7 +11290,7 @@ dependencies = [
"constant_time_eq",
"hmac",
"rand 0.9.2",
"sha1 0.10.6",
"sha1",
"sha2",
]
@@ -11748,7 +11505,7 @@ dependencies = [
"rand 0.9.2",
"rustls 0.23.32",
"rustls-pki-types",
"sha1 0.10.6",
"sha1",
"thiserror 2.0.17",
"utf-8",
]
@@ -11767,9 +11524,9 @@ checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c"
[[package]]
name = "typenum"
version = "1.20.1"
version = "1.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20"
checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb"
[[package]]
name = "uds_windows"
@@ -12000,6 +11757,17 @@ dependencies = [
"utoipa-gen",
]
[[package]]
name = "utoipa-actix-web"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7eda9c23c05af0fb812f6a177514047331dac4851a2c8e9c4b895d6d826967f"
dependencies = [
"actix-service",
"actix-web",
"utoipa",
]
[[package]]
name = "utoipa-gen"
version = "5.4.0"
@@ -12014,6 +11782,18 @@ dependencies = [
"uuid 1.23.3",
]
[[package]]
name = "utoipa-scalar"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59559e1509172f6b26c1cdbc7247c4ddd1ac6560fe94b584f81ee489b141f719"
dependencies = [
"actix-web",
"serde",
"serde_json",
"utoipa",
]
[[package]]
name = "uuid"
version = "0.8.2"
@@ -13432,31 +13212,6 @@ version = "0.13.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "66fee0b777b0f5ac1c69bb06d361268faafa61cd4682ae064a171c16c433e9e4"
[[package]]
name = "xredis"
version = "0.1.0"
dependencies = [
"ariadne",
"chrono",
"dashmap",
"deadpool-redis",
"futures",
"lz4_flex",
"prometheus",
"redis",
"serde",
"serde_json",
"thiserror 2.0.17",
"tokio",
"tracing",
]
[[package]]
name = "xxhash-rust"
version = "0.8.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "985eec839aaf2a1270af8f4ebcf63cf9401cfd90f0902f97c28d9f104ffbde72"
[[package]]
name = "yaserde"
version = "0.12.0"
@@ -13695,12 +13450,6 @@ version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2f06ae92f42f5e5c42443fd094f245eb656abf56dd7cce9b8b263236565e00f2"
[[package]]
name = "zmij"
version = "1.0.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"
[[package]]
name = "zopfli"
version = "0.8.2"
+4 -17
View File
@@ -13,9 +13,7 @@ members = [
"packages/modrinth-log",
"packages/modrinth-maxmind",
"packages/modrinth-util",
"packages/neverbounce",
"packages/path-util",
"packages/xredis",
]
[workspace.package]
@@ -53,7 +51,6 @@ aws-sdk-s3 = { version = "=1.122.0", default-features = false, features = [
] }
base64 = "0.22.1"
bitflags = "2.9.4"
bon = "3.9.3"
bytemuck = "1.24.0"
bytes = "1.10.1"
censor = "0.3.0"
@@ -73,7 +70,7 @@ daedalus = { path = "packages/daedalus" }
darling = { version = "0.23" }
dashmap = "6.1.0"
data-url = "0.3.2"
deadpool-redis = { git = "https://github.com/modrinth/deadpool", rev = "c7209b086829572f368f1194aba54bdea8dc540b", version = "0.23.0" }
deadpool-redis = { git = "https://github.com/modrinth/deadpool", rev = "db5fb00b036ecc8fe5f18853c559b745ffe47bde", version = "0.22.1" }
derive_more = "2.1.1"
directories = "6.0.0"
dirs = "6.0.0"
@@ -90,12 +87,10 @@ fs4 = { version = "0.13.1", default-features = false }
futures = "0.3.31"
futures-lite = "2.6.1"
futures-util = "0.3.31"
governor = "0.10.4"
heck = "0.5.0"
hex = "0.4.3"
hickory-resolver = "0.25.2"
hmac = "0.12.1"
httpdate = "1.0.3"
hyper = "1.7.0"
hyper-rustls = { version = "0.27.7", default-features = false, features = [
"aws-lc-rs",
@@ -122,12 +117,6 @@ lettre = { version = "0.11.19", default-features = false, features = [
"tokio1",
"tokio1-rustls",
] }
lz4_flex = { version = "0.11.5", default-features = false, features = [
"checked-decode",
"safe-decode",
"safe-encode",
"std",
] }
maxminddb = "0.26.0"
meilisearch-sdk = { version = "0.30.0", default-features = false }
modrinth-content-management = { path = "packages/modrinth-content-management" }
@@ -136,7 +125,6 @@ modrinth-util = { path = "packages/modrinth-util" }
muralpay = { path = "packages/muralpay" }
murmur2 = "0.1.0"
native-dialog = "0.9.2"
neverbounce = { path = "packages/neverbounce" }
notify = { version = "8.2.0", default-features = false }
notify-debouncer-mini = { version = "0.7.0", default-features = false }
objc2-app-kit = { version = "0.3.2", default-features = false }
@@ -146,7 +134,6 @@ paste = "1.0.15"
path-util = { path = "packages/path-util" }
phf = { version = "0.13.1", features = ["macros"] }
png = "0.18.0"
postcard = { version = "1.1.3", default-features = false, features = ["alloc"] }
proc-macro2 = { version = "1.0" }
prometheus = "0.14.0"
quartz_nbt = "0.2.9"
@@ -155,7 +142,7 @@ 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 = "1.4.1"
redis = "0.32.7"
regex = "1.12.2"
reqwest = { version = "0.12.24", default-features = false }
rgb = "0.8.52"
@@ -171,7 +158,6 @@ rust-s3 = { version = "0.37.0", default-features = false, features = [
] }
rustls = "0.23.32"
rusty-money = "0.4.1"
scalar_api_reference = { version = "0.2.2", default-features = false }
secrecy = "0.10.3"
sentry = { version = "0.45.0", default-features = false, features = [
"backtrace",
@@ -231,6 +217,8 @@ typed-path = "0.12.0"
url = "2.5.7"
urlencoding = "2.1.3"
utoipa = { version = "5.4.0", features = ["actix_extras", "chrono", "decimal"] }
utoipa-actix-web = { version = "0.1.2" }
utoipa-scalar = { version = "0.3.0", default-features = false }
uuid = "1.18.1"
validator = "0.20.0"
webauthn-rs = "0.5.5"
@@ -242,7 +230,6 @@ windows = "=0.61.3" # Locked on 0.61 until we can update windows-core to 0.62
windows-core = "=0.61.2" # Locked on 0.61 until webview2-com updates to 0.62
winreg = "0.55.0"
woothee = "0.13.0"
xredis = { path = "packages/xredis" }
yaserde = "0.12.0"
zbus = "5.11.0"
zip = { version = "6.0.0", default-features = false, features = [
+2 -2
View File
@@ -3,13 +3,13 @@
![Issues](https://img.shields.io/github/issues-raw/Modrinth/code?color=c78aff&label=issues&style=for-the-badge)
![Pull Requests](https://img.shields.io/github/issues-pr-raw/Modrinth/code?color=c78aff&label=PRs&style=for-the-badge)
![Contributors](https://img.shields.io/github/contributors/Modrinth/code?color=c78aff&label=contributors&style=for-the-badge)
![Lines of Code](https://img.shields.io/endpoint?url=https://loctopus.creeperkatze.dev/github/modrinth/code/badge?style=flat&logoColor=white&color=c78aff&style=for-the-badge)
![Lines](https://img.shields.io/endpoint?url=https://ghloc.vercel.app/api/modrinth/code/badge?style=flat&logoColor=white&color=c78aff&style=for-the-badge)
![Commit Activity](https://img.shields.io/github/commit-activity/m/Modrinth/code?color=c78aff&label=commits&style=for-the-badge)
![Last Commit](https://img.shields.io/github/last-commit/Modrinth/code?color=c78aff&label=last%20commit&style=for-the-badge)
## Modrinth Monorepo
Welcome to the Modrinth Monorepo, the primary codebase for the Modrinth web interface and app. It contains ![Lines of Code](https://img.shields.io/endpoint?url=https://loctopus.creeperkatze.dev/github/modrinth/code/badge%3Fformat%3Dhuman&logoColor=white&color=black&label=) lines of code and has ![Contributors](https://img.shields.io/github/contributors/Modrinth/code?color=black&label=) contributors!
Welcome to the Modrinth Monorepo, the primary codebase for the Modrinth web interface and app. It contains ![Lines](https://img.shields.io/endpoint?url=https://ghloc.vercel.app/api/modrinth/olympus/badge?logoColor=white&color=black&label=) lines of code and has ![Contributors](https://img.shields.io/github/contributors/Modrinth/code?color=black&label=) contributors!
If you're not a developer and you've stumbled upon this repository, you can access the web interface on the [Modrinth website](https://modrinth.com) and download the latest release of the app [here](https://modrinth.com/app).
-3
View File
@@ -8,11 +8,8 @@ extend-exclude = [
"packages/blog/",
# contains licenses like `CC-BY-ND-4.0`
"packages/moderation/src/data/stages/license.ts",
"packages/moderation/src/utils.ts",
# contains payment card IDs like `IY1VMST1MOXS` which are flagged
"apps/labrinth/src/queue/payouts/mod.rs",
# contains domain names with deliberate typos
"apps/labrinth/assets/disposable_email_blocklist.txt"
]
[default.extend-words]
+24 -122
View File
@@ -76,7 +76,6 @@ import FriendsList from '@/components/ui/friends/FriendsList.vue'
import AddServerToInstanceModal from '@/components/ui/install_flow/AddServerToInstanceModal.vue'
import UnknownPackWarningModal from '@/components/ui/install_flow/UnknownPackWarningModal.vue'
import MinecraftAuthErrorModal from '@/components/ui/minecraft-auth-error-modal/MinecraftAuthErrorModal.vue'
import MinecraftRequiredModal from '@/components/ui/minecraft-required-modal/MinecraftRequiredModal.vue'
import AppSettingsModal from '@/components/ui/modal/AppSettingsModal.vue'
import AuthGrantFlowWaitModal from '@/components/ui/modal/AuthGrantFlowWaitModal.vue'
import InstallToPlayModal from '@/components/ui/modal/InstallToPlayModal.vue'
@@ -90,14 +89,7 @@ import SplashScreen from '@/components/ui/SplashScreen.vue'
import WindowControls from '@/components/ui/WindowControls.vue'
import { useCheckDisableMouseover } from '@/composables/macCssFix.js'
import { config } from '@/config'
import {
ads_consent_listener,
get_ads_consent_required,
hide_ads_window,
init_ads_window,
perform_ads_consent_action,
show_ads_window,
} from '@/helpers/ads.js'
import { hide_ads_window, init_ads_window, show_ads_window } from '@/helpers/ads.js'
import { debugAnalytics, initAnalytics, trackEvent } from '@/helpers/analytics'
import { check_reachable } from '@/helpers/auth.js'
import { get_user, get_version } from '@/helpers/cache.js'
@@ -192,8 +184,6 @@ const { handleError, addNotification } = notificationManager
const popupNotificationManager = new AppPopupNotificationManager()
providePopupNotificationManager(popupNotificationManager)
const { addPopupNotification } = popupNotificationManager
let adsConsentPopupId = null
let unlistenAdsConsent
const appVersion = getVersion()
const tauriApiClient = new TauriModrinthClient({
@@ -223,20 +213,9 @@ const { data: authenticatedModrinthUser } = useQuery({
enabled: () => !!credentials.value?.session,
retry: false,
})
const hasPlus = computed(
() =>
!!credentials.value?.user &&
(hasMidasBadge(credentials.value.user) ||
hasActivePride26Midas(authenticatedModrinthUser.value?.campaigns?.pride_26)),
)
const showAd = computed(
() => sidebarVisible.value && !hasPlus.value && credentials.value !== undefined,
)
const adConsentAvailable = computed(() => credentials.value !== undefined && !hasPlus.value)
providePageContext({
hierarchicalSidebarAvailable: ref(true),
showAds: showAd,
adConsentAvailable,
showAds: ref(false),
floatingActionBarOffsets: {
left: ref(APP_LEFT_NAV_WIDTH),
right: computed(() => (sidebarVisible.value ? `${APP_SIDEBAR_WIDTH}px` : '0px')),
@@ -316,12 +295,6 @@ const authUnreachable = computed(() => {
onMounted(async () => {
await useCheckDisableMouseover()
try {
unlistenAdsConsent = await ads_consent_listener(handleAdsConsentRequired)
handleAdsConsentRequired(await get_ads_consent_required())
} catch (error) {
handleError(error)
}
document.querySelector('body').addEventListener('click', handleClick)
document.querySelector('body').addEventListener('auxclick', handleAuxClick)
@@ -335,7 +308,6 @@ onUnmounted(async () => {
unsubscribeSidebarToggle()
clearDelayedUpdatePopup()
await unlistenAdsConsent?.()
await unlistenUpdateDownload?.()
})
@@ -360,77 +332,8 @@ const messages = defineMessages({
defaultMessage:
'Minecraft authentication servers may be down right now. Check your internet connection and try again later.',
},
adsConsentTitle: {
id: 'app.ads-consent.title',
defaultMessage: 'Your privacy and how ads support Modrinth',
},
adsConsentBody: {
id: 'app.ads-consent.body',
defaultMessage:
'Ads make Modrinth possible and fund creator payouts. Our partners may store or access cookies in the app to personalize ads and measure performance.',
},
adsConsentManage: {
id: 'app.ads-consent.manage',
defaultMessage: 'Manage preferences',
},
adsConsentReject: {
id: 'app.ads-consent.reject',
defaultMessage: 'Reject all',
},
adsConsentAccept: {
id: 'app.ads-consent.accept',
defaultMessage: 'Accept all',
},
})
function handleAdsConsentRequired(required) {
if (!required) {
if (adsConsentPopupId !== null) {
popupNotificationManager.removeNotification(adsConsentPopupId)
adsConsentPopupId = null
}
return
}
if (
adsConsentPopupId !== null &&
popupNotificationManager.getNotifications().some((item) => item.id === adsConsentPopupId)
) {
return
}
const notification = addPopupNotification({
title: formatMessage(messages.adsConsentTitle),
text: formatMessage(messages.adsConsentBody),
type: 'info',
hideIcon: true,
autoCloseMs: null,
dismissible: false,
buttons: [
{
label: formatMessage(messages.adsConsentManage),
action: () => perform_ads_consent_action('manage').catch(handleError),
color: 'standard',
keepOpen: true,
},
{
label: formatMessage(messages.adsConsentReject),
action: () => perform_ads_consent_action('reject').catch(handleError),
color: 'brand',
keepOpen: true,
},
{
label: formatMessage(messages.adsConsentAccept),
action: () => perform_ads_consent_action('accept').catch(handleError),
color: 'brand',
keepOpen: true,
},
],
})
adsConsentPopupId = notification.id
}
async function setupApp() {
const {
native_decorations,
@@ -500,7 +403,7 @@ async function setupApp() {
addNotification({
title: 'Warning',
text: e.message,
type: 'warning',
type: 'warn',
}),
)
@@ -685,7 +588,6 @@ watch(stateInitialized, (ready) => {
const error = useError()
const errorModal = ref()
const minecraftAuthErrorModal = ref()
const minecraftRequiredModal = ref()
const contentInstall = createContentInstall({ router, handleError })
provideContentInstall(contentInstall)
@@ -801,6 +703,17 @@ async function logOut() {
await fetchCredentials()
}
const hasPlus = computed(
() =>
!!credentials.value?.user &&
(hasMidasBadge(credentials.value.user) ||
hasActivePride26Midas(authenticatedModrinthUser.value?.campaigns?.pride_26)),
)
const showAd = computed(
() => sidebarVisible.value && !hasPlus.value && credentials.value !== undefined,
)
async function fetchIntercomToken() {
const creds = await getCreds()
if (!creds?.session) {
@@ -827,28 +740,19 @@ async function fetchIntercomToken() {
return await response.json()
}
watch(
[showAd, adConsentAvailable],
async ([showAds, canManageConsent]) => {
if (showAds) {
await init_ads_window(true)
return
}
await hide_ads_window(true)
if (canManageConsent) {
await init_ads_window()
}
},
{ immediate: true },
)
watch(showAd, () => {
if (!showAd.value) {
hide_ads_window(true)
} else {
init_ads_window(true)
}
})
onMounted(() => {
invoke('show_window')
error.setErrorModal(errorModal.value)
error.setMinecraftAuthErrorModal(minecraftAuthErrorModal.value)
error.setMinecraftRequiredModal(minecraftRequiredModal.value)
setContentIncompatibilityWarningModal(incompatibilityWarningModal.value)
setContentInstallModal(modInstallModal.value)
@@ -951,13 +855,12 @@ async function handleCommand(e) {
if (e.path.endsWith('.mrpack')) {
const location = { type: 'fromFile', path: e.path }
const preview = await install_get_modpack_preview(location).catch(handleError)
if (preview?.unknownFile || preview?.externalFilesInModpack.length > 0) {
if (preview?.unknownFile) {
const splitPath = e.path.split(/[\\/]/)
const fileName = splitPath ? splitPath[splitPath.length - 1] : e.path
unknownPackWarningModal.value?.show(
() => install_create_modpack_instance(location).then(() => undefined),
fileName,
preview.externalFilesInModpack,
)
} else {
await install_create_modpack_instance(location).catch(handleError)
@@ -1790,7 +1693,6 @@ provideAppUpdateDownloadProgress(appUpdateDownload)
<PopupNotificationPanel :has-sidebar="sidebarVisible" />
<ErrorModal ref="errorModal" />
<MinecraftAuthErrorModal ref="minecraftAuthErrorModal" />
<MinecraftRequiredModal ref="minecraftRequiredModal" />
<ContentInstallModal
ref="modInstallModal"
:instances="contentInstallInstances"
@@ -1834,8 +1736,8 @@ provideAppUpdateDownloadProgress(appUpdateDownload)
@create-anyway="handleContentInstallModpackDuplicateCreateAnyway"
@go-to-instance="handleContentInstallModpackDuplicateGoToInstance"
/>
<InstallToPlayModal ref="installToPlayModal" :show-external-warnings="false" />
<UpdateToPlayModal ref="updateToPlayModal" :show-external-warnings="false" />
<InstallToPlayModal ref="installToPlayModal" />
<UpdateToPlayModal ref="updateToPlayModal" />
</template>
<style lang="scss" scoped>
Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

@@ -14,7 +14,31 @@
<UnplugIcon class="text-secondary" />
<span class="text-sm text-contrast"> {{ formatMessage(messages.offline) }} </span>
</div>
<AppUpdateButton />
<ButtonStyled color="brand" type="outlined" hover-color-fill="background">
<button
v-if="showUpdatePill"
type="button"
class="!h-[34px] overflow-hidden text-sm !transition-[width,opacity,transform,background-color,color,filter] !duration-200 ease-out"
:class="[
updatePillWidthClass,
{
'update-pill-ready-hidden': finishedDownloading && !animateReadyPill,
'update-pill-ready-visible': finishedDownloading && animateReadyPill,
},
]"
:disabled="isUpdateDownloading"
:aria-busy="isUpdateDownloading"
@click="handleUpdateClick"
>
<RefreshCwIcon v-if="finishedDownloading" :class="{ 'animate-spin': restarting }" />
<DownloadIcon v-else />
<span v-if="isUpdateDownloading">
{{ formatMessage(messages.downloadingUpdate) }}
<span class="inline-block w-[3ch] text-right tabular-nums">{{ downloadPercent }}%</span>
</span>
<span v-else>{{ updateLabel }}</span>
</button>
</ButtonStyled>
<div
class="flex border-solid border-surface-5 text-sm items-center gap-2 py-1.5 px-3 rounded-xl border"
>
@@ -120,6 +144,7 @@ import {
DownloadIcon,
DropdownIcon,
OnlineIndicatorIcon,
RefreshCwIcon,
StarIcon,
StopCircleIcon,
TerminalSquareIcon,
@@ -136,10 +161,9 @@ import {
} from '@modrinth/ui'
import { convertFileSrc } from '@tauri-apps/api/core'
import { Dropdown } from 'floating-vue'
import { computed, onBeforeUnmount, onMounted, ref } from 'vue'
import { computed, nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue'
import { useRouter } from 'vue-router'
import AppUpdateButton from '@/components/ui/app-update-button/index.vue'
import { useInstallJobNotifications } from '@/composables/browse/install-job-notifications'
import { trackEvent } from '@/helpers/analytics'
import { loading_listener, process_listener } from '@/helpers/events'
@@ -148,6 +172,11 @@ import { get_all as getRunningProcesses, kill as killProcess } from '@/helpers/p
import type { LoadingBar } from '@/helpers/state'
import { progress_bars_list } from '@/helpers/state'
import type { GameInstance } from '@/helpers/types'
import {
appUpdateState,
downloadAvailableAppUpdate,
installAvailableAppUpdate,
} from '@/providers/app-update'
const { handleError } = injectNotificationManager()
const popupNotificationManager = injectPopupNotificationManager()
@@ -212,8 +241,96 @@ const messages = defineMessages({
id: 'app.action-bar.view-active-downloads',
defaultMessage: 'View active downloads',
},
update: {
id: 'app.action-bar.update',
defaultMessage: 'Update',
},
downloadingUpdate: {
id: 'app.action-bar.downloading-update',
defaultMessage: 'Downloading update',
},
reloadToUpdate: {
id: 'app.action-bar.reload-to-update',
defaultMessage: 'Reload to update',
},
})
const {
downloading,
downloadPercent,
downloadProgress,
finishedDownloading,
isVisible: isUpdateVisible,
metered,
restarting,
} = appUpdateState
const isUpdateDownloading = computed(
() =>
downloading.value ||
(downloadProgress.value > 0 && downloadProgress.value < 1 && !finishedDownloading.value),
)
const showUpdatePill = computed(
() => isUpdateVisible.value && (finishedDownloading.value || metered.value),
)
const animateReadyPill = ref(false)
const updateLabel = computed(() => {
if (isUpdateDownloading.value) {
return formatMessage(messages.downloadingUpdate)
}
if (finishedDownloading.value) {
return formatMessage(messages.reloadToUpdate)
}
return formatMessage(messages.update)
})
const updatePillWidthClass = computed(() => {
if (isUpdateDownloading.value) {
return 'w-[219px]'
}
if (finishedDownloading.value) {
return 'w-[166px]'
}
return '!w-[96px]'
})
let readyPillAnimationFrame: number | null = null
watch([showUpdatePill, finishedDownloading], async ([show, ready], [wasShown, wasReady]) => {
if (readyPillAnimationFrame !== null) {
cancelAnimationFrame(readyPillAnimationFrame)
readyPillAnimationFrame = null
}
if (!show || !ready) {
animateReadyPill.value = false
return
}
if (wasShown && wasReady) {
return
}
animateReadyPill.value = false
await nextTick()
readyPillAnimationFrame = requestAnimationFrame(() => {
animateReadyPill.value = true
readyPillAnimationFrame = null
})
})
async function handleUpdateClick() {
if (isUpdateDownloading.value) {
return
}
if (finishedDownloading.value) {
await installAvailableAppUpdate()
} else {
await downloadAvailableAppUpdate()
}
}
const currentProcesses = ref<RunningProcess[]>([])
const selectedProcess = ref<RunningProcess | undefined>()
@@ -502,5 +619,20 @@ onBeforeUnmount(() => {
unlistenProcess()
unlistenLoading()
installJobNotifications.dispose()
if (readyPillAnimationFrame !== null) {
cancelAnimationFrame(readyPillAnimationFrame)
}
})
</script>
<style scoped>
.update-pill-ready-hidden {
opacity: 0;
transform: scale(0.96);
}
.update-pill-ready-visible {
opacity: 1;
transform: scale(1);
}
</style>
@@ -52,6 +52,10 @@ defineExpose({
if (errorVal.message.includes('because the target machine actively refused it')) {
metadata.value.hostsFile = true
}
} else if (errorVal.message && errorVal.message.includes('User is not logged in')) {
title.value = 'Sign in to Minecraft'
errorType.value = 'minecraft_sign_in'
supportLink.value = 'https://support.modrinth.com'
} else if (errorVal.message && errorVal.message.includes('Move directory error:')) {
title.value = 'Could not change app directory'
errorType.value = 'directory_move'
@@ -239,6 +243,20 @@ async function copyToClipboard(text) {
</button>
</div>
</template>
<div v-else-if="errorType === 'minecraft_sign_in'">
<p>
To play this instance, you must sign in through Microsoft below. If you don't have a
Minecraft account, you can purchase the game on the
<a href="https://www.minecraft.net/en-us/store/minecraft-java-bedrock-edition-pc"
>Minecraft website</a
>.
</p>
<div class="cta-button">
<button class="btn btn-primary" :disabled="loadingMinecraft" @click="loginMinecraft">
<LogInIcon /> Sign in to Minecraft
</button>
</div>
</div>
<template v-else-if="errorType === 'state_init'">
<p>
Modrinth App failed to load correctly. This may be because of a corrupted file, or
@@ -1,32 +1,28 @@
<script setup>
import { XIcon } from '@modrinth/assets'
import { WrenchIcon, XIcon } from '@modrinth/assets'
import {
Accordion,
ButtonStyled,
Checkbox,
commonMessages,
defineMessages,
FileTreeSelect,
injectNotificationManager,
NewModal,
StyledInput,
useVIntl,
} from '@modrinth/ui'
import { save } from '@tauri-apps/plugin-dialog'
import { readDir, stat } from '@tauri-apps/plugin-fs'
import { ref } from 'vue'
import { PackageIcon } from '@/assets/icons'
import {
export_instance_mrpack,
get_full_path,
get_pack_export_candidates,
} from '@/helpers/instance'
import { PackageIcon, VersionIcon } from '@/assets/icons'
import ModalWrapper from '@/components/ui/modal/ModalWrapper.vue'
import { export_instance_mrpack, get_pack_export_candidates } from '@/helpers/instance'
const { handleError } = injectNotificationManager()
const { formatMessage } = useVIntl()
const messages = defineMessages({
header: { id: 'app.export-modal.header', defaultMessage: 'Export modpack' },
modpackNameLabel: { id: 'app.export-modal.modpack-name-label', defaultMessage: 'Modpack name' },
modpackNameLabel: { id: 'app.export-modal.modpack-name-label', defaultMessage: 'Modpack Name' },
modpackNamePlaceholder: {
id: 'app.export-modal.modpack-name-placeholder',
defaultMessage: 'Modpack name',
@@ -43,7 +39,15 @@ const messages = defineMessages({
id: 'app.export-modal.description-placeholder',
defaultMessage: 'Enter modpack description...',
},
selectFilesLabel: {
id: 'app.export-modal.select-files-label',
defaultMessage: 'Configure which files are included in this export',
},
exportButton: { id: 'app.export-modal.export-button', defaultMessage: 'Export' },
includeFile: {
id: 'app.export-modal.include-file-accessibility-label',
defaultMessage: 'Include "{file}"?',
},
})
const props = defineProps({
@@ -55,9 +59,8 @@ const props = defineProps({
defineExpose({
show: () => {
resetExportState()
exportModal.value.show()
void initFiles().catch(handleError)
initFiles()
},
})
@@ -66,33 +69,62 @@ const nameInput = ref(props.instance.name)
const exportDescription = ref('')
const versionInput = ref('1.0.0')
const files = ref([])
const selectedFilePaths = ref([])
const fileTreeKey = ref(0)
const filesLoadId = ref(0)
const instanceRoot = ref('')
const loadedDirectories = ref(new Set())
const folders = ref([])
async function initFiles() {
const loadId = ++filesLoadId.value
const [filePaths, root] = await Promise.all([
get_pack_export_candidates(props.instance.id),
get_full_path(props.instance.id),
])
if (loadId !== filesLoadId.value) return
instanceRoot.value = root
const exportCandidates = await Promise.all(
filePaths.map((path) => buildExportCandidateItem(root, path)),
const initFiles = async () => {
const newFolders = new Map()
const sep = '/'
files.value = []
await get_pack_export_candidates(props.instance.id).then((filePaths) =>
filePaths
.map((folder) => ({
path: folder,
name: folder.split(sep).pop(),
selected:
folder.startsWith('mods') ||
folder.startsWith('datapacks') ||
folder.startsWith('resourcepacks') ||
folder.startsWith('shaderpacks') ||
folder.startsWith('config'),
disabled:
folder === 'profile.json' ||
folder.startsWith('modrinth_logs') ||
folder.startsWith('.fabric') ||
folder.startsWith('__MACOSX'),
}))
.forEach((pathData) => {
const parent = pathData.path.split(sep).slice(0, -1).join(sep)
if (parent !== '') {
if (newFolders.has(parent)) {
newFolders.get(parent).push(pathData)
} else {
newFolders.set(parent, [pathData])
}
} else {
files.value.push(pathData)
}
}),
)
if (loadId !== filesLoadId.value) return
files.value = exportCandidates
selectedFilePaths.value = files.value
.filter((file) => !file.disabled && isDefaultSelectedExportCandidate(file.path))
.map((file) => file.path)
folders.value = [...newFolders.entries()].map(([name, value]) => [
{
name,
showingMore: false,
},
value,
])
}
await initFiles()
const exportPack = async () => {
const filesToExport = files.value.filter((file) => file.selected).map((file) => file.path)
folders.value.forEach((args) => {
args[1].forEach((child) => {
if (child.selected) {
filesToExport.push(child.path)
}
})
})
const outputPath = await save({
defaultPath: `${nameInput.value} ${versionInput.value}.mrpack`,
filters: [
@@ -107,7 +139,7 @@ const exportPack = async () => {
export_instance_mrpack(
props.instance.id,
outputPath,
selectedFilePaths.value,
filesToExport,
versionInput.value,
exportDescription.value,
nameInput.value,
@@ -115,176 +147,94 @@ const exportPack = async () => {
exportModal.value.hide()
}
}
function resetExportState() {
nameInput.value = props.instance.name
exportDescription.value = ''
versionInput.value = '1.0.0'
files.value = []
selectedFilePaths.value = []
fileTreeKey.value += 1
instanceRoot.value = ''
loadedDirectories.value = new Set()
}
async function loadExportDirectory(path) {
if (!path || !instanceRoot.value || loadedDirectories.value.has(path)) return
const loadId = filesLoadId.value
loadedDirectories.value.add(path)
try {
const entries = await readDir(`${instanceRoot.value}/${path}`)
const childItems = await Promise.all(
entries.map((entry) => buildExportDirectoryChildItem(instanceRoot.value, path, entry)),
)
if (loadId !== filesLoadId.value) return
appendExportItems(childItems)
} catch {
loadedDirectories.value.delete(path)
}
}
async function buildExportCandidateItem(instanceRoot, path) {
try {
const entries = await readDir(`${instanceRoot}/${path}`)
const metadata = await getExportCandidateMetadata(instanceRoot, path)
return {
path,
type: 'directory',
disabled: isExportCandidateDisabled(path),
modified: metadata.modified,
count: entries.length,
}
} catch {
return buildExportFileItem(instanceRoot, path)
}
}
async function buildExportDirectoryChildItem(instanceRoot, parentPath, entry) {
const path = `${parentPath}/${entry.name}`
if (entry.isDirectory) {
const metadata = await getExportCandidateMetadata(instanceRoot, path)
return {
path,
type: 'directory',
disabled: isExportCandidateDisabled(path),
modified: metadata.modified,
}
}
return buildExportFileItem(instanceRoot, path)
}
async function buildExportFileItem(instanceRoot, path) {
const metadata = await getExportCandidateMetadata(instanceRoot, path)
return {
path,
type: 'file',
disabled: isExportCandidateDisabled(path),
size: metadata.size,
modified: metadata.modified,
}
}
function appendExportItems(items) {
const nextFiles = new Map(files.value.map((file) => [normalizeExportPath(file.path), file]))
for (const item of items) {
nextFiles.set(normalizeExportPath(item.path), item)
}
files.value = [...nextFiles.values()]
}
async function getExportCandidateMetadata(instanceRoot, path) {
try {
const metadata = await stat(`${instanceRoot}/${path}`)
return {
size: metadata.size,
modified: metadata.mtime ? Math.floor(metadata.mtime.getTime() / 1000) : undefined,
}
} catch {
return {}
}
}
function normalizeExportPath(path) {
return path.replaceAll('\\', '/').split('/').filter(Boolean).join('/')
}
function isDefaultSelectedExportCandidate(path) {
return (
path.startsWith('mods') ||
path.startsWith('datapacks') ||
path.startsWith('resourcepacks') ||
path.startsWith('shaderpacks') ||
path.startsWith('config')
)
}
function isExportCandidateDisabled(path) {
return (
path === 'profile.json' ||
path.startsWith('modrinth_logs') ||
path.startsWith('.fabric') ||
path.startsWith('__MACOSX')
)
}
</script>
<template>
<NewModal
ref="exportModal"
:header="formatMessage(messages.header)"
scrollable
width="46rem"
max-width="calc(100vw - 2rem)"
>
<div class="flex flex-col gap-4">
<ModalWrapper ref="exportModal" :header="formatMessage(messages.header)">
<div class="flex flex-col gap-4 w-[40rem]">
<div class="grid grid-cols-2 gap-4">
<div class="labeled_input w-full">
<p class="text-contrast font-semibold">{{ formatMessage(messages.modpackNameLabel) }}</p>
<div class="labeled_input">
<p>{{ formatMessage(messages.modpackNameLabel) }}</p>
<StyledInput
v-model="nameInput"
:icon="PackageIcon"
type="text"
:placeholder="formatMessage(messages.modpackNamePlaceholder)"
clearable
wrapper-class="w-full"
/>
</div>
<div class="labeled_input w-full">
<p class="text-contrast font-semibold">
{{ formatMessage(messages.versionNumberLabel) }}
</p>
<div class="labeled_input">
<p>{{ formatMessage(messages.versionNumberLabel) }}</p>
<StyledInput
v-model="versionInput"
:icon="VersionIcon"
type="text"
:placeholder="formatMessage(messages.versionNumberPlaceholder)"
clearable
wrapper-class="w-full"
/>
</div>
</div>
<div class="flex flex-col gap-2 min-w-0">
<p class="m-0 text-contrast font-semibold">
{{ formatMessage(commonMessages.descriptionLabel) }}
</p>
<div class="flex flex-col gap-2">
<p class="m-0">{{ formatMessage(commonMessages.descriptionLabel) }}</p>
<StyledInput
v-model="exportDescription"
multiline
:placeholder="formatMessage(messages.descriptionPlaceholder)"
wrapper-class="w-full"
/>
</div>
<FileTreeSelect
:key="fileTreeKey"
v-model="selectedFilePaths"
class="min-w-0"
:items="files"
@navigate="loadExportDirectory"
/>
</div>
<template #actions>
<Accordion
class="w-full bg-surface-4 border border-solid border-surface-5 rounded-2xl overflow-clip"
button-class="p-4 w-full border-b border-solid border-b-surface-5 bg-surface-2 -mb-px hover:brightness-[--hover-brightness] group"
>
<template #title>
<span class="flex items-center gap-3 text-contrast group-active:scale-[0.98]">
<WrenchIcon aria-hidden="true" class="size-5 text-secondary" />
Configure which files are included in this export
</span>
</template>
<div class="flex flex-col [&>*:nth-child(even)]:bg-surface-3">
<div v-for="[path, children] in folders" :key="path.name" class="flex flex-col">
<Accordion
class="flex flex-col"
button-class="flex gap-3 pr-4 hover:bg-surface-5 group"
>
<template #title>
<Checkbox
:model-value="children.every((child) => child.selected)"
:indeterminate="
!children.every((child) => child.selected) &&
children.some((child) => child.selected)
"
:description="formatMessage(messages.includeFile, { file: path.name })"
class="pl-4 py-2"
:disabled="children.every((x) => x.disabled)"
@update:model-value="
(newValue) => children.forEach((child) => (child.selected = newValue))
"
@click.stop
/>
<span class="ml-2 group-active:scale-95">{{ path.name }}/</span>
</template>
<div v-for="child in children" :key="child.path">
<Checkbox
v-model="child.selected"
:label="child.name"
class="w-full px-8 py-2 hover:bg-surface-4 text-primary"
:disabled="child.disabled"
/>
</div>
</Accordion>
</div>
<Checkbox
v-for="file in files"
:key="file.path"
v-model="file.selected"
:label="file.name"
:disabled="file.disabled"
class="w-full px-4 py-2 hover:bg-surface-4 text-primary"
/>
</div>
</Accordion>
<div class="flex items-center justify-end gap-2">
<ButtonStyled type="outlined">
<button @click="exportModal.hide">
@@ -299,6 +249,6 @@ function isExportCandidateDisabled(path) {
</button>
</ButtonStyled>
</div>
</template>
</NewModal>
</div>
</ModalWrapper>
</template>
@@ -1,124 +0,0 @@
<template>
<ButtonStyled color="brand" type="outlined" hover-color-fill="background">
<button
v-if="showUpdatePill"
type="button"
class="!h-[34px] text-sm !transition-[opacity,transform,background-color,color,filter] !duration-200 ease-out"
:class="{
'opacity-0 scale-[0.96]': finishedDownloading && !animateReadyPill,
'opacity-100 scale-100': finishedDownloading && animateReadyPill,
}"
:disabled="isUpdateDownloading"
:aria-busy="isUpdateDownloading"
@click="handleUpdateClick"
>
<RefreshCwIcon v-if="finishedDownloading" :class="{ 'animate-spin': restarting }" />
<DownloadIcon v-else />
<span v-if="isUpdateDownloading">
{{ formatMessage(messages.downloadingUpdate) }}
<span class="inline-block w-[3ch] text-right tabular-nums">{{ downloadPercent }}%</span>
</span>
<span v-else>{{ updateLabel }}</span>
</button>
</ButtonStyled>
</template>
<script setup lang="ts">
import { DownloadIcon, RefreshCwIcon } from '@modrinth/assets'
import { ButtonStyled, defineMessages, useVIntl } from '@modrinth/ui'
import { computed, nextTick, onBeforeUnmount, ref, watch } from 'vue'
import {
appUpdateState,
downloadAvailableAppUpdate,
installAvailableAppUpdate,
} from '@/providers/app-update'
const { formatMessage } = useVIntl()
const messages = defineMessages({
update: {
id: 'app.action-bar.update',
defaultMessage: 'Update',
},
downloadingUpdate: {
id: 'app.action-bar.downloading-update',
defaultMessage: 'Downloading update',
},
reloadToUpdate: {
id: 'app.action-bar.reload-to-update',
defaultMessage: 'Reload to update',
},
})
const {
downloading,
downloadPercent,
downloadProgress,
finishedDownloading,
isVisible: isUpdateVisible,
metered,
restarting,
} = appUpdateState
const isUpdateDownloading = computed(
() =>
downloading.value ||
(downloadProgress.value > 0 && downloadProgress.value < 1 && !finishedDownloading.value),
)
const showUpdatePill = computed(
() => isUpdateVisible.value && (finishedDownloading.value || metered.value),
)
const animateReadyPill = ref(false)
const updateLabel = computed(() => {
if (isUpdateDownloading.value) {
return formatMessage(messages.downloadingUpdate)
}
if (finishedDownloading.value) {
return formatMessage(messages.reloadToUpdate)
}
return formatMessage(messages.update)
})
let readyPillAnimationFrame: number | null = null
watch([showUpdatePill, finishedDownloading], async ([show, ready], [wasShown, wasReady]) => {
if (readyPillAnimationFrame !== null) {
cancelAnimationFrame(readyPillAnimationFrame)
readyPillAnimationFrame = null
}
if (!show || !ready) {
animateReadyPill.value = false
return
}
if (wasShown && wasReady) {
return
}
animateReadyPill.value = false
await nextTick()
readyPillAnimationFrame = requestAnimationFrame(() => {
animateReadyPill.value = true
readyPillAnimationFrame = null
})
})
async function handleUpdateClick() {
if (isUpdateDownloading.value) {
return
}
if (finishedDownloading.value) {
await installAvailableAppUpdate()
} else {
await downloadAvailableAppUpdate()
}
}
onBeforeUnmount(() => {
if (readyPillAnimationFrame !== null) {
cancelAnimationFrame(readyPillAnimationFrame)
}
})
</script>
@@ -1,5 +1,5 @@
<script setup lang="ts">
import { MailIcon, SearchIcon, SendIcon, UserIcon, UserPlusIcon, XIcon } from '@modrinth/assets'
import { MailIcon, SendIcon, UserIcon, UserPlusIcon, XIcon } from '@modrinth/assets'
import {
Avatar,
ButtonStyled,
@@ -301,12 +301,11 @@ const messages = defineMessages({
</ButtonStyled>
<StyledInput
v-model="search"
:icon="SearchIcon"
type="text"
:placeholder="formatMessage(messages.searchFriends)"
clearable
input-class="!bg-transparent !border !border-solid !border-button-bg !text-primary !placeholder:text-primary"
wrapper-class="flex-1 [&>svg]:!text-primary [&>svg]:!opacity-100"
variant="outlined"
wrapper-class="flex-1"
@keyup.esc="search = ''"
/>
</template>
@@ -1,41 +1,108 @@
<template>
<UnknownFileWarningModal
ref="modal"
:mode="externalFilesInModpack.length > 0 ? 'modpack' : 'mod'"
:file-name="fileName"
:external-files-in-modpack="externalFilesInModpack"
@cancel="reset"
@continue="proceed"
/>
<NewModal ref="modal" :header="formatMessage(messages.header)" :on-hide="reset">
<div class="max-w-[31rem] flex flex-col gap-6">
<Admonition
type="warning"
:header="formatMessage(messages.warningTitle)"
:body="formatMessage(messages.warningBody)"
/>
<div v-if="fileName" class="overflow-x-auto whitespace-nowrap text-sm text-secondary">
{{ fileName }}
</div>
<div>
<p class="mt-0 leading-tight">
{{ formatMessage(messages.body) }}
</p>
<p class="text-orange font-semibold mb-0 leading-tight">
{{ formatMessage(messages.malwareStatement) }}
</p>
</div>
<Checkbox v-model="dontShowAgain" :label="formatMessage(messages.dontShowAgain)" />
<div class="flex gap-2 justify-end">
<ButtonStyled type="outlined">
<button @click="cancel">
<XIcon />
{{ formatMessage(commonMessages.cancelButton) }}
</button>
</ButtonStyled>
<ButtonStyled color="orange">
<button :disabled="isProceeding" @click="proceed">
<SpinnerIcon v-if="isProceeding" class="animate-spin" />
<CircleArrowRightIcon v-else />
{{ formatMessage(messages.installAnyway) }}
</button>
</ButtonStyled>
</div>
</div>
</NewModal>
</template>
<script setup lang="ts">
import { UnknownFileWarningModal } from '@modrinth/ui'
import { CircleArrowRightIcon, SpinnerIcon, XIcon } from '@modrinth/assets'
import {
Admonition,
ButtonStyled,
Checkbox,
commonMessages,
defineMessages,
NewModal,
useVIntl,
} from '@modrinth/ui'
import { ref, useTemplateRef } from 'vue'
import { get as getSettings, set as setSettings } from '@/helpers/settings'
import { useTheming } from '@/store/state'
import type { FeatureFlag } from '@/store/theme.ts'
const { formatMessage } = useVIntl()
const themeStore = useTheming()
const skipUnknownPackWarningFeatureFlag = 'skip_unknown_pack_warning' as FeatureFlag
const dontShowAgain = ref(false)
const modal = useTemplateRef('modal')
const onProceed = ref<() => Promise<void>>()
const isProceeding = ref(false)
const fileName = ref('')
const externalFilesInModpack = ref<string[]>([])
function show(
createInstance: () => Promise<void>,
selectedFileName = '',
selectedExternalFiles: string[] = [],
) {
const messages = defineMessages({
header: {
id: 'unknown-pack-warning-modal.header',
defaultMessage: 'Confirm installation',
},
warningTitle: {
id: 'unknown-pack-warning-modal.warning.title',
defaultMessage: 'Unknown file warning',
},
warningBody: {
id: 'unknown-pack-warning-modal.warning.body',
defaultMessage: `We couldn't find this file on Modrinth. We strongly recommend only installing files from sources you trust.`,
},
body: {
id: 'unknown-pack-warning-modal.body',
defaultMessage: `A file is only reviewed if its uploaded to Modrinth, regardless of its file format (including .mrpack).`,
},
malwareStatement: {
id: 'unknown-pack-warning-modal.malware-statement',
defaultMessage: `Malware is often distributed through modpack files by sharing them on platforms like Discord.`,
},
dontShowAgain: {
id: 'unknown-pack-warning-modal.dont-show-again',
defaultMessage: `Don't show this warning again`,
},
installAnyway: {
id: 'unknown-pack-warning-modal.install-anyway',
defaultMessage: `Install anyway`,
},
})
function show(createInstance: () => Promise<void>, selectedFileName = '') {
onProceed.value = createInstance
fileName.value = selectedFileName
externalFilesInModpack.value = selectedExternalFiles
dontShowAgain.value = false
if (themeStore.getFeatureFlag(skipUnknownPackWarningFeatureFlag)) {
void createInstance()
// noinspection ES6MissingAwait
createInstance()
return
}
@@ -45,11 +112,18 @@ function show(
function reset() {
onProceed.value = undefined
fileName.value = ''
externalFilesInModpack.value = []
}
async function proceed(dontShowAgain: boolean) {
if (dontShowAgain) {
function cancel() {
modal.value?.hide()
}
async function proceed() {
if (!onProceed.value) {
return
}
if (dontShowAgain.value) {
themeStore.featureFlags[skipUnknownPackWarningFeatureFlag] = true
const settings = await getSettings()
settings.feature_flags[skipUnknownPackWarningFeatureFlag] = true
@@ -57,8 +131,9 @@ async function proceed(dontShowAgain: boolean) {
}
const createInstance = onProceed.value
reset()
if (createInstance) void createInstance()
modal.value?.hide()
// noinspection ES6MissingAwait
createInstance()
}
defineExpose({ show })
@@ -1,294 +0,0 @@
<template>
<PageHeader :title="instance.name">
<template #leading>
<Avatar :src="iconSrc" :alt="instance.name" size="64px" :tint-by="instance.id" />
</template>
<template #metadata>
<InstanceHeaderServerMetadata
v-if="isServerInstance"
:loading-server-ping="loadingServerPing"
:players-online="playersOnline"
:status-online="statusOnline"
:recent-plays="recentPlays"
:ping="ping"
:minecraft-server="minecraftServer"
:linked-project-v3="linkedProjectV3"
:instance-id="instance.id"
/>
<PageHeaderMetadata v-else>
<PageHeaderMetadataItem :icon="Gamepad2Icon" tooltip="Minecraft version">
Minecraft {{ instance.game_version }}
</PageHeaderMetadataItem>
<PageHeaderMetadataItem
:icon="ServerLoaderIcon"
:icon-props="{ loader: loaderDisplayName }"
tooltip="Mod loader"
>
{{ loaderLabel }}
</PageHeaderMetadataItem>
<PageHeaderMetadataItem
v-if="showInstancePlayTime"
:icon="TimerIcon"
tooltip="Total playtime"
>
{{ playtimeLabel }}
</PageHeaderMetadataItem>
</PageHeaderMetadata>
</template>
<template #actions>
<PageHeaderActions>
<ButtonStyled v-if="isInstalling" color="brand" size="large">
<button type="button" disabled>
{{ formatMessage(commonMessages.installingLabel) }}
</button>
</ButtonStyled>
<ButtonStyled v-else-if="instance.install_stage !== 'installed'" color="brand" size="large">
<button type="button" @click="emit('repair')">
<DownloadIcon />
{{ formatMessage(messages.repair) }}
</button>
</ButtonStyled>
<ButtonStyled v-else-if="playing" color="red" size="large">
<button type="button" :disabled="stopping" @click="emit('stop')">
<StopCircleIcon />
{{
stopping ? formatMessage(messages.stopping) : formatMessage(commonMessages.stopButton)
}}
</button>
</ButtonStyled>
<JoinedButtons
v-else-if="!loading && isServerInstance"
:actions="serverPlayActions"
color="brand"
size="large"
/>
<ButtonStyled v-else-if="!loading" color="brand" size="large">
<button type="button" @click="emit('play')">
<PlayIcon />
{{ formatMessage(commonMessages.playButton) }}
</button>
</ButtonStyled>
<ButtonStyled v-else color="brand" size="large">
<button type="button" disabled>{{ formatMessage(messages.starting) }}</button>
</ButtonStyled>
<ButtonStyled circular size="large">
<button
v-tooltip="formatMessage(messages.instanceSettings)"
type="button"
:aria-label="formatMessage(messages.instanceSettings)"
@click="emit('settings')"
>
<SettingsIcon />
</button>
</ButtonStyled>
<ButtonStyled circular size="large" type="transparent">
<TeleportOverflowMenu
:options="moreActions"
:tooltip="formatMessage(messages.moreActions)"
:aria-label="formatMessage(messages.moreActions)"
>
<MoreVerticalIcon />
</TeleportOverflowMenu>
</ButtonStyled>
</PageHeaderActions>
</template>
</PageHeader>
</template>
<script setup lang="ts">
import type { Labrinth } from '@modrinth/api-client'
import {
DownloadIcon,
ExternalIcon,
FolderOpenIcon,
MoreVerticalIcon,
PackageIcon,
PlayIcon,
SettingsIcon,
StopCircleIcon,
TagCategoryGamepad2Icon as Gamepad2Icon,
TimerIcon,
} from '@modrinth/assets'
import {
Avatar,
ButtonStyled,
commonMessages,
defineMessages,
formatLoaderLabel,
type JoinedButtonAction,
JoinedButtons,
LoaderIcon as ServerLoaderIcon,
PageHeader,
PageHeaderActions,
PageHeaderMetadata,
PageHeaderMetadataItem,
type ServerLoader,
TeleportOverflowMenu,
type TeleportOverflowMenuItem,
useVIntl,
} from '@modrinth/ui'
import { computed } from 'vue'
import type { GameInstance } from '@/helpers/types'
import InstanceHeaderServerMetadata from './instance-page-header-server-metadata.vue'
const messages = defineMessages({
createShortcut: {
id: 'instance.action.create-shortcut',
defaultMessage: 'Create shortcut',
},
exportModpack: {
id: 'instance.action.export-modpack',
defaultMessage: 'Export modpack',
},
instanceSettings: {
id: 'instance.action.settings',
defaultMessage: 'Instance settings',
},
launchInstance: {
id: 'instance.action.launch-instance',
defaultMessage: 'Launch instance',
},
moreActions: {
id: 'instance.action.more-actions',
defaultMessage: 'More actions',
},
neverPlayed: {
id: 'instance.playtime.never-played',
defaultMessage: 'Never played',
},
openFolder: {
id: 'instance.action.open-folder',
defaultMessage: 'Open folder',
},
repair: {
id: 'instance.action.repair',
defaultMessage: 'Repair',
},
starting: {
id: 'instance.action.starting',
defaultMessage: 'Starting...',
},
stopping: {
id: 'instance.action.stopping',
defaultMessage: 'Stopping...',
},
})
const props = withDefaults(
defineProps<{
instance: GameInstance
iconSrc?: string | null
isServerInstance?: boolean
showInstancePlayTime?: boolean
timePlayed?: number
playing?: boolean
loading?: boolean
stopping?: boolean
loadingServerPing?: boolean
playersOnline?: number
statusOnline?: boolean
recentPlays?: number
ping?: number
minecraftServer?: Labrinth.Projects.v3.Project['minecraft_server']
linkedProjectV3?: Labrinth.Projects.v3.Project
}>(),
{
iconSrc: null,
isServerInstance: false,
showInstancePlayTime: false,
timePlayed: 0,
playing: false,
loading: false,
stopping: false,
loadingServerPing: false,
playersOnline: undefined,
statusOnline: false,
recentPlays: undefined,
ping: undefined,
minecraftServer: undefined,
linkedProjectV3: undefined,
},
)
const emit = defineEmits<{
repair: []
stop: []
play: []
playServer: []
settings: []
openFolder: []
export: []
createShortcut: []
}>()
const installingStages = [
'installing',
'pack_installing',
'pack_installed',
'not_installed',
'minecraft_installing',
]
const { formatMessage } = useVIntl()
const isInstalling = computed(() => installingStages.includes(props.instance.install_stage))
const loaderDisplayName = computed(() => formatLoaderLabel(props.instance.loader) as ServerLoader)
const loaderLabel = computed(() =>
[loaderDisplayName.value, props.instance.loader_version].filter(Boolean).join(' '),
)
const playtimeLabel = computed(() => {
if (props.timePlayed <= 0) return formatMessage(messages.neverPlayed)
const hours = Math.floor(props.timePlayed / 3600)
if (hours >= 1) {
return `${hours} hour${hours > 1 ? 's' : ''}`
}
const minutes = Math.floor(props.timePlayed / 60)
if (minutes >= 1) {
return `${minutes} minute${minutes > 1 ? 's' : ''}`
}
const seconds = Math.floor(props.timePlayed)
return `${seconds} second${seconds > 1 ? 's' : ''}`
})
const serverPlayActions = computed<JoinedButtonAction[]>(() => [
{
id: 'join_server',
label: formatMessage(commonMessages.playButton),
icon: PlayIcon,
action: () => emit('playServer'),
},
{
id: 'launch_instance',
label: formatMessage(messages.launchInstance),
icon: PlayIcon,
action: () => emit('play'),
},
])
const moreActions = computed<TeleportOverflowMenuItem[]>(() => [
{
id: 'open-folder',
label: formatMessage(messages.openFolder),
icon: FolderOpenIcon,
action: () => emit('openFolder'),
},
{
id: 'export-mrpack',
label: formatMessage(messages.exportModpack),
icon: PackageIcon,
action: () => emit('export'),
},
{
id: 'create-shortcut',
label: formatMessage(messages.createShortcut),
icon: ExternalIcon,
action: () => emit('createShortcut'),
},
])
</script>
@@ -1,63 +0,0 @@
<template>
<div class="flex items-center flex-wrap gap-2">
<template v-if="loadingServerPing">
<ServerOnlinePlayers
v-if="playersOnline !== undefined"
:online="playersOnline"
:status-online="statusOnline"
hide-label
/>
<ServerRecentPlays :recent-plays="recentPlays ?? 0" hide-label />
<div
v-if="
(playersOnline !== undefined || recentPlays !== undefined) &&
(minecraftServer?.region || ping)
"
class="w-1.5 h-1.5 rounded-full bg-surface-5"
></div>
<ServerPing v-if="ping" :ping="ping" />
</template>
<ServerRegion v-if="minecraftServer?.region" :region="minecraftServer?.region" />
<div v-if="minecraftServer?.region || ping" class="w-1.5 h-1.5 rounded-full bg-surface-5"></div>
<div v-if="linkedProjectV3" class="flex gap-1.5 items-center font-medium text-primary">
Linked to
<Avatar
:src="linkedProjectV3.icon_url"
:alt="linkedProjectV3.name"
:tint-by="instanceId"
size="24px"
/>
<router-link
:to="`/project/${linkedProjectV3.slug ?? linkedProjectV3.id}`"
class="hover:underline text-primary truncate"
>
{{ linkedProjectV3.name }}
</router-link>
</div>
</div>
</template>
<script setup lang="ts">
import type { Labrinth } from '@modrinth/api-client'
import {
Avatar,
ServerOnlinePlayers,
ServerPing,
ServerRecentPlays,
ServerRegion,
} from '@modrinth/ui'
defineProps<{
loadingServerPing?: boolean
playersOnline?: number
statusOnline?: boolean
recentPlays?: number
ping?: number
minecraftServer?: Labrinth.Projects.v3.Project['minecraft_server']
linkedProjectV3?: Labrinth.Projects.v3.Project
instanceId?: string
}>()
</script>
@@ -269,31 +269,16 @@ provideInstallationSettings({
debug('resolveLoaderVersions: no manifest', { loader, gameVersion })
return []
}
const entry = manifest.gameVersions?.find((item) => item.id === gameVersion)
if (entry?.versionGroup) {
const result =
manifest.versionGroups?.find((group) => group.id === entry.versionGroup)?.loaders ?? []
debug('resolveLoaderVersions: version group result', {
loader,
gameVersion,
versionGroup: entry.versionGroup,
count: result.length,
})
return result
}
const placeholder = manifest.gameVersions?.find((item) => item.id === '${modrinth.gameVersion}')
if (placeholder) {
const result = manifest.gameVersions?.some((item) => item.id === gameVersion)
? placeholder.loaders
: []
debug('resolveLoaderVersions: placeholder result', {
if (loader === 'fabric' || loader === 'quilt') {
const result = manifest.gameVersions[0]?.loaders ?? []
debug('resolveLoaderVersions: fabric/quilt result', {
loader,
gameVersion,
count: result.length,
})
return result
}
const result = entry?.loaders ?? []
const result = manifest.gameVersions?.find((item) => item.id === gameVersion)?.loaders ?? []
debug('resolveLoaderVersions: result', { loader, gameVersion, count: result.length })
return result
},
@@ -1,144 +0,0 @@
<template>
<NewModal ref="modal" :header="formatMessage(messages.header)" max-width="544px" no-padding>
<div class="grid grid-cols-[1fr_auto] gap-2.5 h-[154px] px-7 pt-4 pb-1 pr-9">
<div class="flex flex-col gap-2.5 items-start justify-center h-min mt-5">
<div class="font-semibold text-xl text-contrast">
{{ formatMessage(messages.descriptionHeader) }}
</div>
<div class="text-secondary leading-6">
{{ formatMessage(messages.description) }}
</div>
</div>
<div class="relative h-full w-[96px] overflow-hidden mx-3">
<div class="absolute top-0 left-0 z-0 w-full flex grow-0 flex-col items-end p-0">
<img :src="steveImage" alt="" class="self-stretch" />
</div>
<div
class="absolute left-0 bottom-0 z-10 order-1 h-6 w-[120px] shrink-0 grow-0 bg-[linear-gradient(180deg,rgba(39,41,46,0)_0%,#27292E_80%,#27292E_100%)]"
></div>
</div>
</div>
<div class="flex flex-col gap-6 px-6 pb-6">
<div class="flex justify-end gap-2">
<ButtonStyled>
<a class="w-full !shadow-none" href="https://support.modrinth.com" @click="modal?.hide()">
<MessagesSquareIcon />
{{ formatMessage(messages.getSupport) }}
</a>
</ButtonStyled>
<ButtonStyled color="brand">
<button class="w-full !shadow-none" :disabled="loadingSignIn" @click="signIn">
<SpinnerIcon v-if="loadingSignIn" class="animate-spin" />
<svg
v-else
width="20"
height="20"
viewBox="0 0 20 20"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<rect width="9.25" height="9.25" fill="black" fill-opacity="0.9" />
<rect x="10.75" width="9.25" height="9.25" fill="black" fill-opacity="0.9" />
<rect y="10.75" width="9.25" height="9.25" fill="black" fill-opacity="0.9" />
<rect
x="10.75"
y="10.75"
width="9.25"
height="9.25"
fill="black"
fill-opacity="0.9"
/>
</svg>
{{ formatMessage(messages.signIn) }}
</button>
</ButtonStyled>
</div>
<p class="m-0 text-center text-sm text-secondary">
{{ formatMessage(messages.dontHaveAccount) }}
<a
class="text-blue font-medium hover:underline"
href="https://www.minecraft.net/en-us/store/minecraft-java-bedrock-edition-pc"
>
{{ formatMessage(messages.getMinecraft) }}
</a>
</p>
</div>
</NewModal>
</template>
<script setup lang="ts">
import { MessagesSquareIcon, SpinnerIcon } from '@modrinth/assets'
import { ButtonStyled, defineMessages, NewModal, useVIntl } from '@modrinth/ui'
import { inject, type Ref, ref } from 'vue'
import steveImage from '@/assets/steve-look-up-left.webp'
import type AccountsCard from '@/components/ui/AccountsCard.vue'
import { trackEvent } from '@/helpers/analytics'
import { login as loginFlow, set_default_user } from '@/helpers/auth.js'
import { handleSevereError } from '@/store/error.js'
const { formatMessage } = useVIntl()
const accountsCard = inject('accountsCard') as Ref<InstanceType<typeof AccountsCard> | null>
const messages = defineMessages({
header: {
id: 'minecraft-required.header',
defaultMessage: 'Minecraft required',
},
descriptionHeader: {
id: 'minecraft-required.description-header',
defaultMessage: 'Sign in to a Microsoft account',
},
description: {
id: 'minecraft-required.description',
defaultMessage:
'You need a Microsoft account that owns Minecraft before you can launch and play.',
},
getSupport: {
id: 'minecraft-required.get-support',
defaultMessage: 'Get support',
},
signIn: {
id: 'minecraft-required.sign-in',
defaultMessage: 'Sign in to Microsoft',
},
dontHaveAccount: {
id: 'minecraft-required.dont-have-account',
defaultMessage: 'Dont have an account?',
},
getMinecraft: {
id: 'minecraft-required.get-minecraft',
defaultMessage: 'Get Minecraft',
},
})
const modal = ref<InstanceType<typeof NewModal>>()
const loadingSignIn = ref(false)
function show() {
modal.value?.show()
}
async function signIn() {
loadingSignIn.value = true
try {
const loggedIn = await loginFlow()
if (!loggedIn) return
await set_default_user(loggedIn.profile.id)
await accountsCard.value?.refreshValues()
await trackEvent('AccountLogIn', { source: 'MinecraftRequiredModal' })
modal.value?.hide()
} catch (error) {
handleSevereError(error)
} finally {
loadingSignIn.value = false
}
}
defineExpose({
show,
})
</script>
@@ -1,22 +1,15 @@
<template>
<NewModal
ref="modal"
:header="formatMessage(messages.installToPlay)"
:closable="true"
:on-hide="show_ads_window"
max-width="640px"
width="640px"
>
<div v-if="requiredContentProject" class="flex w-full flex-col gap-6">
<p class="m-0 text-primary">
{{ formatMessage(messages.inviteWarning) }}
</p>
<NewModal ref="modal" :header="formatMessage(messages.installToPlay)" :closable="true">
<div v-if="requiredContentProject" class="flex flex-col gap-6 max-w-[500px]">
<Admonition type="info" :header="formatMessage(messages.contentRequired)">
{{ formatMessage(messages.serverRequiresMods) }}
</Admonition>
<div class="flex flex-col gap-2.5">
<div class="flex items-center justify-between">
<span class="font-semibold text-contrast">
{{ formatMessage(messages.sharedInstance) }}
</span>
<div class="flex flex-col gap-1">
<div class="flex justify-between items-center">
<span class="font-semibold text-contrast">{{
formatMessage(messages.requiredModpack)
}}</span>
<ButtonStyled type="transparent">
<button @click="openViewContents">
@@ -26,16 +19,14 @@
</ButtonStyled>
</div>
<div class="flex items-center gap-3 rounded-2xl bg-surface-2 p-3">
<div class="flex items-center gap-3 rounded-xl bg-surface-2 p-3">
<Avatar
:src="requiredContentProject.icon_url"
:alt="requiredContentProject.title"
size="56px"
no-shadow
class="!rounded-2xl"
size="48px"
/>
<div class="flex min-w-0 flex-col gap-0.5">
<span class="truncate font-semibold text-contrast">
<div class="flex flex-col gap-0.5">
<span class="font-semibold text-contrast">
<template v-if="usingCustomModpack && modpackVersion">
{{ modpackVersion.name }}
</template>
@@ -43,7 +34,7 @@
{{ requiredContentProject.title }}
</template>
</span>
<span class="truncate text-sm font-medium text-secondary">
<span class="text-sm text-secondary">
{{ loaderDisplay }} {{ requiredContentProject.game_versions?.[0] }}
<template v-if="modCount">
· {{ formatMessage(messages.modCount, { count: modCount }) }}
@@ -52,105 +43,24 @@
</div>
</div>
</div>
</div>
<Admonition
v-if="hasExternalFiles"
type="warning"
:header="formatMessage(messages.unknownFilesWarning)"
>
{{ formatMessage(messages.unknownFilesDescription) }}
</Admonition>
<div v-if="hasExternalFiles" class="relative w-full">
<div
ref="externalFileTable"
class="max-h-[242px] overflow-y-auto rounded-2xl"
@scroll="checkTableScrollState"
>
<Table
:columns="externalFileColumns"
:data="externalFileRows"
row-key="id"
virtualized
:virtual-row-height="48"
class="shadow-sm"
>
<template #cell-name="{ value }">
<span class="block truncate" :title="String(value)">{{ value }}</span>
</template>
</Table>
</div>
<Transition
enter-active-class="transition-all duration-200 ease-out"
enter-from-class="opacity-0 max-h-0"
enter-to-class="opacity-100 max-h-2"
leave-active-class="transition-all duration-200 ease-in"
leave-from-class="opacity-100 max-h-2"
leave-to-class="opacity-0 max-h-0"
>
<div
v-if="showTableTopFade"
class="pointer-events-none absolute left-0 right-0 top-0 z-10 h-2 bg-gradient-to-b from-bg-raised to-transparent"
/>
</Transition>
<Transition
enter-active-class="transition-all duration-200 ease-out"
enter-from-class="opacity-0 max-h-0"
enter-to-class="opacity-100 max-h-2"
leave-active-class="transition-all duration-200 ease-in"
leave-from-class="opacity-100 max-h-2"
leave-to-class="opacity-0 max-h-0"
>
<div
v-if="showTableBottomFade"
class="pointer-events-none absolute bottom-0 left-0 right-0 z-10 h-2 bg-gradient-to-t from-bg-raised to-transparent"
/>
</Transition>
</div>
<p v-if="hasExternalFiles" class="m-0 text-primary">
{{ formatMessage(messages.reviewedFiles) }}
</p>
<div class="flex w-full items-center justify-between gap-2">
<ButtonStyled type="transparent" color="red">
<button @click="handleReport">
<ReportIcon />
{{ formatMessage(commonMessages.reportButton) }}
<template #actions>
<div class="flex justify-end gap-2">
<ButtonStyled>
<button @click="handleDecline">
<XIcon />
{{ formatMessage(commonMessages.cancelButton) }}
</button>
</ButtonStyled>
<ButtonStyled color="brand">
<button @click="handleAccept">
<DownloadIcon />
{{ formatMessage(messages.installButton) }}
</button>
</ButtonStyled>
<div class="flex items-center gap-2">
<template v-if="hasExternalFiles">
<ButtonStyled type="transparent" color="orange">
<button @click="handleAccept">
{{ formatMessage(messages.installAnyway) }}
</button>
</ButtonStyled>
<ButtonStyled color="brand">
<button @click="handleDecline">
<BanIcon />
{{ formatMessage(messages.dontInstall) }}
</button>
</ButtonStyled>
</template>
<template v-else>
<ButtonStyled>
<button @click="handleDecline">
<XIcon />
{{ formatMessage(commonMessages.cancelButton) }}
</button>
</ButtonStyled>
<ButtonStyled color="brand">
<button @click="handleAccept">
<DownloadIcon />
{{ formatMessage(messages.installButton) }}
</button>
</ButtonStyled>
</template>
</div>
</div>
</div>
</template>
</NewModal>
<ModpackContentModal
@@ -162,56 +72,33 @@
<script setup lang="ts">
import type { Labrinth } from '@modrinth/api-client'
import { BanIcon, DownloadIcon, EyeIcon, ReportIcon, XIcon } from '@modrinth/assets'
import { DownloadIcon, EyeIcon, XIcon } from '@modrinth/assets'
import type { ContentItem } from '@modrinth/ui'
import {
Admonition,
Avatar,
ButtonStyled,
commonMessages,
type ContentItem,
defineMessages,
formatLoader,
ModpackContentModal,
NewModal,
Table,
type TableColumn,
useScrollIndicator,
useVIntl,
} from '@modrinth/ui'
import { openUrl } from '@tauri-apps/plugin-opener'
import { computed, nextTick, ref } from 'vue'
import { computed, ref } from 'vue'
import { hide_ads_window, show_ads_window } from '@/helpers/ads'
import { get_project, get_project_many, get_version, get_version_many } from '@/helpers/cache.js'
import { injectServerInstall } from '@/providers/server-install'
type ExternalFileColumn = 'name'
type ExternalFileRow = {
id: string
name: string
}
const modal = ref<InstanceType<typeof NewModal>>()
const modpackVersionId = ref<string | null>(null)
const modpackVersion = ref<Labrinth.Versions.v2.Version | null>(null)
const project = ref<Labrinth.Projects.v3.Project | null>(null)
const requiredContentProject = ref<Labrinth.Projects.v2.Project | null>(null)
const externalFiles = ref<string[]>([])
const externalFileTable = ref<HTMLElement | null>(null)
const onInstallComplete = ref<() => void>(() => {})
const { formatMessage } = useVIntl()
const props = defineProps<{
showExternalWarnings?: boolean
}>()
const { installServerProject, startInstallingServer, stopInstallingServer } = injectServerInstall()
const {
showTopFade: showTableTopFade,
showBottomFade: showTableBottomFade,
checkScrollState: checkTableScrollState,
forceCheck: forceCheckTableScroll,
} = useScrollIndicator(externalFileTable)
const usingCustomModpack = computed(() => {
return requiredContentProject.value?.id === project.value?.id
@@ -224,36 +111,13 @@ const loaderDisplay = computed(() => {
})
const modCount = computed(() => modpackVersion.value?.dependencies?.length)
const hasExternalFiles = computed(
() => Boolean(props.showExternalWarnings) && externalFiles.value.length > 0,
)
const externalFileRows = computed<ExternalFileRow[]>(() =>
externalFiles.value.map((name, index) => ({
id: `${index}-${name}`,
name,
})),
)
async function fetchData(versionId: string) {
// cache is making version null for some reason so bypassing for now
const version = await get_version(versionId, 'bypass')
modpackVersion.value = version
modpackVersion.value = await get_version(versionId, 'bypass')
if (version?.project_id) {
requiredContentProject.value = await get_project(version.project_id, 'bypass')
externalFiles.value = [
...new Set(
(version.dependencies ?? [])
.filter(
(dependency) =>
dependency.dependency_type === 'embedded' &&
!dependency.project_id &&
!dependency.version_id &&
dependency.file_name,
)
.flatMap((dependency) => (dependency.file_name ? [dependency.file_name] : [])),
),
].sort((left, right) => left.localeCompare(right))
if (modpackVersion.value?.project_id) {
requiredContentProject.value = await get_project(modpackVersion.value.project_id, 'bypass')
}
}
@@ -275,12 +139,6 @@ function handleDecline() {
hide()
}
function handleReport() {
if (project.value?.id) {
openUrl(`https://modrinth.com/report?item=project&itemID=${project.value.id}`)
}
}
const modpackContentModal = ref<InstanceType<typeof ModpackContentModal>>()
async function openViewContents() {
@@ -359,19 +217,17 @@ async function show(
modpackVersionId.value = modpackVersionIdVal
modpackVersion.value = null
requiredContentProject.value = null
externalFiles.value = []
onInstallComplete.value = callback
if (modpackVersionIdVal) await fetchData(modpackVersionIdVal)
hide_ads_window()
modal.value?.show(e)
await nextTick()
forceCheckTableScroll()
}
function hide() {
modal.value?.hide()
show_ads_window()
}
const messages = defineMessages({
@@ -379,10 +235,22 @@ const messages = defineMessages({
id: 'app.modal.install-to-play.header',
defaultMessage: 'Install to play',
},
inviteWarning: {
id: 'app.modal.install-to-play.invite-warning',
sharedServerInstance: {
id: 'app.modal.install-to-play.shared-server-instance',
defaultMessage: 'Shared server instance',
},
contentRequired: {
id: 'app.modal.install-to-play.content-required',
defaultMessage: 'Content required',
},
serverRequiresMods: {
id: 'app.modal.install-to-play.server-requires-mods',
defaultMessage:
'This invite was created by another Modrinth user, not Modrinth. Only accept invites from people you trust.',
'This server requires mods to play. Click Install to set up the required files from Modrinth, then launch directly into the server.',
},
requiredModpack: {
id: 'app.modal.install-to-play.required-modpack',
defaultMessage: 'Required modpack',
},
sharedInstance: {
id: 'app.modal.install-to-play.shared-instance',
@@ -400,41 +268,7 @@ const messages = defineMessages({
id: 'app.modal.install-to-play.view-contents',
defaultMessage: 'View contents',
},
unknownFilesWarning: {
id: 'app.modal.install-to-play.unknown-files-warning',
defaultMessage: 'Unknown files warning',
},
unknownFilesDescription: {
id: 'app.modal.install-to-play.unknown-files-description',
defaultMessage:
'This server modpack contains files that arent published on Modrinth. We strongly recommend only installing files from sources you trust.',
},
unrecognizedFiles: {
id: 'app.modal.install-to-play.unrecognized-files',
defaultMessage: 'Unrecognized files',
},
reviewedFiles: {
id: 'app.modal.install-to-play.reviewed-files',
defaultMessage:
'A file is only reviewed if its published to Modrinth, regardless of its file format (including .mrpack).',
},
installAnyway: {
id: 'app.modal.install-to-play.install-anyway',
defaultMessage: 'Install anyway',
},
dontInstall: {
id: 'app.modal.install-to-play.dont-install',
defaultMessage: 'Dont install',
},
})
const externalFileColumns = computed<TableColumn<ExternalFileColumn>[]>(() => [
{
key: 'name',
label: formatMessage(messages.unrecognizedFiles),
cellClass: '!h-12',
},
])
defineExpose({ show, hide })
</script>
@@ -2,17 +2,17 @@
<ContentDiffModal
ref="diffModal"
:header="formatMessage(messages.updateToPlay)"
:admonition-header="formatMessage(messages.updateRequired)"
:description="
instance ? formatMessage(messages.updateRequiredDescription, { name: instance.name }) : ''
"
:diffs="normalizedDiffs"
:version-date="versionDate"
:show-external-warnings="showExternalWarnings"
:confirm-label="formatMessage(commonMessages.updateButton)"
:confirm-icon="DownloadIcon"
:removed-label="formatMessage(messages.removed)"
:show-report-button="true"
@confirm="handleUpdate"
@cancel="handleDecline"
@report="handleReport"
/>
</template>
@@ -26,8 +26,9 @@ import {
defineMessages,
useVIntl,
} from '@modrinth/ui'
import { openUrl } from '@tauri-apps/plugin-opener'
import dayjs from 'dayjs'
import { computed, ref } from 'vue'
import { computed, ref, watch } from 'vue'
import { get_project_many, get_version, get_version_many } from '@/helpers/cache.js'
import { wait_for_install_job } from '@/helpers/install'
@@ -76,10 +77,6 @@ const { formatMessage } = useVIntl()
const { startInstallingServer, stopInstallingServer } = injectServerInstall()
type UpdateCompleteCallback = () => void | Promise<void>
defineProps<{
showExternalWarnings?: boolean
}>()
const diffModal = ref<InstanceType<typeof ContentDiffModal>>()
const instance = ref<GameInstance | null>(null)
const onUpdateComplete = ref<UpdateCompleteCallback>(() => {})
@@ -90,7 +87,6 @@ const modpackVersion = ref<Version | null>(null)
const normalizedDiffs = computed<ContentDiffItem[]>(() =>
diffs.value.map((diff) => ({
type: diff.type,
external: Boolean(diff.fileName && !diff.project),
projectName: diff.project?.title,
fileName: diff.fileName,
currentVersionName: diff.currentVersion?.version_number,
@@ -98,12 +94,6 @@ const normalizedDiffs = computed<ContentDiffItem[]>(() =>
})),
)
const versionDate = computed(() =>
modpackVersion.value?.date_published
? dayjs(modpackVersion.value.date_published).format('MMMM D, YYYY')
: undefined,
)
async function computeDependencyDiffs(
currentDeps: Dependency[],
latestDeps: Dependency[],
@@ -200,10 +190,6 @@ async function computeDependencyDiffs(
}
})
.sort((a, b) => {
const aExternal = Boolean(a.fileName && !a.project)
const bExternal = Boolean(b.fileName && !b.project)
if (aExternal !== bExternal) return aExternal ? -1 : 1
const typeOrder = { added: 0, updated: 1, removed: 2 }
const typeCompare = typeOrder[a.type] - typeOrder[b.type]
if (typeCompare !== 0) return typeCompare
@@ -241,6 +227,16 @@ async function checkUpdateAvailable(inst: GameInstance): Promise<DependencyDiff[
return null
}
watch(
() => instance.value,
async (newInstance) => {
if (!newInstance) return
const result = await checkUpdateAvailable(newInstance)
diffs.value = result || []
},
{ immediate: true, deep: true },
)
async function handleUpdate() {
hide()
const serverProjectId = instance.value?.link?.project_id
@@ -258,6 +254,12 @@ async function handleUpdate() {
}
}
function handleReport() {
if (instance.value?.link?.project_id) {
openUrl(`https://modrinth.com/report?item=project&itemID=${instance.value.link.project_id}`)
}
}
function handleDecline() {
hide()
}
@@ -270,15 +272,8 @@ function show(
) {
instance.value = instanceVal
modpackVersionId.value = modpackVersionIdVal
modpackVersion.value = null
diffs.value = []
onUpdateComplete.value = callback
diffModal.value?.show(e)
void checkUpdateAvailable(instanceVal).then((result) => {
if (instance.value?.id === instanceVal.id && modpackVersionId.value === modpackVersionIdVal) {
diffs.value = result || []
}
})
}
function hide() {
@@ -290,15 +285,15 @@ const messages = defineMessages({
id: 'app.modal.update-to-play.header',
defaultMessage: 'Update to play',
},
updateRequired: {
id: 'app.modal.update-to-play.update-required',
defaultMessage: 'Update required',
},
updateRequiredDescription: {
id: 'app.modal.update-to-play.update-required-description',
defaultMessage:
'An update is required to play {name}. Please update to the latest version to launch the game.',
},
removed: {
id: 'app.modal.update-to-play.removed',
defaultMessage: 'Removed',
},
})
const hasUpdate = computed(() => {
@@ -1,44 +1,12 @@
<script setup lang="ts">
import { Settings2Icon } from '@modrinth/assets'
import {
ButtonStyled,
defineMessages,
injectNotificationManager,
injectPageContext,
Toggle,
useVIntl,
} from '@modrinth/ui'
import { Toggle } from '@modrinth/ui'
import { ref, watch } from 'vue'
import { open_ads_consent_preferences } from '@/helpers/ads.js'
import { optInAnalytics, optOutAnalytics } from '@/helpers/analytics'
import { get, set } from '@/helpers/settings.ts'
const { formatMessage } = useVIntl()
const { handleError } = injectNotificationManager()
const { adConsentAvailable } = injectPageContext()
const settings = ref(await get())
const messages = defineMessages({
adsConsentTitle: {
id: 'app.ads-consent.title',
defaultMessage: 'Your privacy and how ads support Modrinth',
},
adsConsentIntro: {
id: 'app.settings.privacy.ads-consent.intro',
defaultMessage:
'Ads make Modrinth possible and fund creator payouts. Our partners may store or access cookies in the app to personalize ads and measure performance. You can opt out or manage your preferences below.',
},
adsConsentManage: {
id: 'app.ads-consent.manage',
defaultMessage: 'Manage preferences',
},
})
async function manageAdsPreferences() {
await open_ads_consent_preferences().catch(handleError)
}
watch(
settings,
async () => {
@@ -55,26 +23,18 @@ watch(
</script>
<template>
<div v-if="adConsentAvailable">
<h2 class="m-0 text-lg font-semibold text-contrast">
{{ formatMessage(messages.adsConsentTitle) }}
</h2>
<div class="mt-1 flex flex-col gap-2.5 items-start">
<div class="flex flex-col gap-1 items-start">
<div class="text-sm">
{{ formatMessage(messages.adsConsentIntro) }}
</div>
</div>
<ButtonStyled>
<button class="!shadow-none" @click="manageAdsPreferences">
<Settings2Icon aria-hidden="true" />
{{ formatMessage(messages.adsConsentManage) }}
</button>
</ButtonStyled>
<div class="flex items-center justify-between gap-4">
<div>
<h2 class="m-0 text-lg font-semibold text-contrast">Personalized ads</h2>
<p class="m-0 mt-1 text-sm">
Modrinth's ad provider, Aditude, shows ads based on your preferences. By disabling this
option, you opt out and ads will no longer be shown based on your interests.
</p>
</div>
<Toggle id="personalized-ads" v-model="settings.personalized_ads" />
</div>
<div class="mt-8 flex items-center justify-between gap-4">
<div class="mt-4 flex items-center justify-between gap-4">
<div>
<h2 class="m-0 text-lg font-semibold text-contrast">Telemetry</h2>
<p class="m-0 mt-1 text-sm">
@@ -11,7 +11,6 @@
<SkinPreviewRenderer
:variant="variant"
:texture-src="previewSkin || ''"
:ears-texture-src="uploadedTextureUrl?.original ?? currentSkin?.texture"
:cape-src="selectedCapeTexture"
framing="modal"
:initial-rotation="Math.PI / 8"
@@ -1,4 +1,4 @@
import { CheckIcon, CopyIcon, UpdatedIcon } from '@modrinth/assets'
import { UpdatedIcon } from '@modrinth/assets'
import {
defineMessages,
type PopupNotificationButton,
@@ -15,11 +15,9 @@ import {
install_job_dismiss,
install_job_list,
install_job_retry,
install_job_support_details,
installJobInstanceId,
type InstallJobSnapshot,
type InstallJobStatus,
type InstallPhaseId,
type InstallProgress,
} from '@/helpers/install'
import { get_many as getInstances } from '@/helpers/instance'
@@ -33,14 +31,6 @@ const messages = defineMessages({
id: 'app.action-bar.install.retry',
defaultMessage: 'Retry',
},
copyDetails: {
id: 'app.action-bar.install.copy-details',
defaultMessage: 'Copy details',
},
copied: {
id: 'app.action-bar.install.copied-details',
defaultMessage: 'Copied',
},
dismiss: {
id: 'app.action-bar.install.dismiss',
defaultMessage: 'Dismiss',
@@ -49,6 +39,22 @@ const messages = defineMessages({
id: 'app.action-bar.install.open-instance',
defaultMessage: 'Open instance',
},
installFailed: {
id: 'app.action-bar.install.failed',
defaultMessage: 'Install failed',
},
installFailedAppClosed: {
id: 'app.action-bar.install.failed-app-closed',
defaultMessage: 'Installation failed due to app closing.',
},
installFailedNetwork: {
id: 'app.action-bar.install.failed-network',
defaultMessage: 'Installation failed due to a network error.',
},
installFailedUnknown: {
id: 'app.action-bar.install.failed-unknown',
defaultMessage: 'Installation failed due to an unknown error.',
},
unknownInstance: {
id: 'app.action-bar.install.unknown-instance',
defaultMessage: 'Unknown instance',
@@ -58,7 +64,7 @@ const messages = defineMessages({
const phaseMessages = defineMessages({
preparing_instance: {
id: 'app.install.phase.preparing_instance',
defaultMessage: 'Queued to install',
defaultMessage: 'Preparing instance',
},
resolving_pack: {
id: 'app.install.phase.resolving_pack',
@@ -133,97 +139,7 @@ const javaStepMessages = defineMessages({
},
})
const failureSummaryMessages = defineMessages({
canceled: {
id: 'app.action-bar.install.summary.canceled',
defaultMessage: 'Canceled',
},
appClosed: {
id: 'app.action-bar.install.summary.app-closing',
defaultMessage: 'Canceled due to app closing',
},
downloadFailed: {
id: 'app.action-bar.install.summary.download-failed',
defaultMessage: "Download couldn't finish",
},
modrinthUnreachable: {
id: 'app.action-bar.install.summary.modrinth-unreachable',
defaultMessage: "Couldn't reach Modrinth",
},
packDownloadFailed: {
id: 'app.action-bar.install.summary.pack-download-failed',
defaultMessage: "Couldn't download pack",
},
badModpackFile: {
id: 'app.action-bar.install.summary.bad-modpack-file',
defaultMessage: "Couldn't read modpack",
},
invalidModpack: {
id: 'app.action-bar.install.summary.invalid-modpack',
defaultMessage: 'Modpack data invalid',
},
contentDownloadFailed: {
id: 'app.action-bar.install.summary.content-download-failed',
defaultMessage: "Couldn't download files",
},
corruptDownload: {
id: 'app.action-bar.install.summary.corrupt-download',
defaultMessage: 'Downloaded file is corrupt',
},
invalidModpackFiles: {
id: 'app.action-bar.install.summary.invalid-modpack-files',
defaultMessage: 'Modpack files have invalid metadata',
},
noWritePermission: {
id: 'app.action-bar.install.summary.no-write-permission',
defaultMessage: 'No permission to write',
},
couldNotSaveFiles: {
id: 'app.action-bar.install.summary.could-not-save-files',
defaultMessage: "Couldn't save files",
},
invalidFilePath: {
id: 'app.action-bar.install.summary.invalid-file-path',
defaultMessage: 'File path is invalid',
},
instanceNotFound: {
id: 'app.action-bar.install.summary.instance-not-found',
defaultMessage: "Instance couldn't be found",
},
cleanupIncomplete: {
id: 'app.action-bar.install.summary.cleanup-incomplete',
defaultMessage: "Cleanup didn't finish",
},
javaSetupFailed: {
id: 'app.action-bar.install.summary.java-setup-failed',
defaultMessage: "Java setup couldn't finish",
},
minecraftSetupFailed: {
id: 'app.action-bar.install.summary.minecraft-setup-failed',
defaultMessage: 'Minecraft setup failed',
},
loaderSetupFailed: {
id: 'app.action-bar.install.summary.loader-setup-failed',
defaultMessage: 'Loader setup failed',
},
localDataError: {
id: 'app.action-bar.install.summary.local-data-error',
defaultMessage: "Couldn't update local data",
},
unexpectedError: {
id: 'app.action-bar.install.summary.unexpected-error',
defaultMessage: 'Something went wrong',
},
})
const visibleJobStatuses = new Set<InstallJobStatus>(['queued', 'running', 'failed', 'interrupted'])
const copyDetailsStallMs = 30_000
interface ProgressSnapshot {
signature: string
changedAt: number
timeout: number | null
}
function getDisplayIconUrl(icon: string | null | undefined): string | null {
if (!icon) return null
@@ -240,13 +156,10 @@ export async function useInstallJobNotifications(opts: {
const jobs = ref<InstallJobSnapshot[]>([])
const iconUrls = ref<Record<string, string | null>>({})
const instanceNames = ref<Record<string, string>>({})
const copiedJobIds = ref<Set<string>>(new Set())
const jobOrder = new Map<string, number>()
let refreshRequest = 0
let metadataRequest = 0
let nextJobOrder = 0
const copiedResetTimeouts = new Map<string, number>()
const progressSnapshots = new Map<string, ProgressSnapshot>()
function getTitle(job: InstallJobSnapshot): string {
if (job.display?.title) return job.display.title
@@ -261,7 +174,13 @@ export async function useInstallJobNotifications(opts: {
function getText(job: InstallJobSnapshot): string {
if (job.status === 'failed' || job.status === 'interrupted') {
return getFailureSummary(job)
if (job.error?.code === 'interrupted') {
return formatMessage(messages.installFailedAppClosed)
}
if (job.error?.code === 'network_error') {
return formatMessage(messages.installFailedNetwork)
}
return formatMessage(messages.installFailedUnknown)
}
if (job.phase === 'preparing_java' && job.details.type === 'java') {
return formatMessage(javaStepMessages[job.details.step], {
@@ -271,104 +190,6 @@ export async function useInstallJobNotifications(opts: {
return formatMessage(phaseMessages[job.phase])
}
function getFailureSummary(job: InstallJobSnapshot): string {
const code = job.error?.code
const phase = job.error?.phase ?? job.phase
if (code === 'app_closed' || (job.status === 'interrupted' && code === 'interrupted')) {
return formatMessage(failureSummaryMessages.appClosed)
}
if (code === 'canceled') {
return formatMessage(failureSummaryMessages.canceled)
}
if (job.rollback_error || code === 'rollback_error') {
return formatMessage(failureSummaryMessages.cleanupIncomplete)
}
if (hasPermissionError(job)) {
return formatMessage(failureSummaryMessages.noWritePermission)
}
switch (code) {
case 'network_error':
return formatMessage(
phase === 'downloading_pack_file'
? failureSummaryMessages.packDownloadFailed
: failureSummaryMessages.downloadFailed,
)
case 'api_error':
return formatMessage(failureSummaryMessages.modrinthUnreachable)
case 'pack_error':
return formatMessage(
phase === 'downloading_pack_file'
? failureSummaryMessages.packDownloadFailed
: failureSummaryMessages.invalidModpack,
)
case 'archive_error':
return formatMessage(failureSummaryMessages.badModpackFile)
case 'parse_error':
return formatMessage(failureSummaryMessages.invalidModpack)
case 'content_error':
return formatMessage(failureSummaryMessages.invalidModpackFiles)
case 'hash_error':
return formatMessage(failureSummaryMessages.corruptDownload)
case 'filesystem_error':
return formatMessage(failureSummaryMessages.couldNotSaveFiles)
case 'path_error':
return formatMessage(failureSummaryMessages.invalidFilePath)
case 'instance_error':
return formatMessage(failureSummaryMessages.instanceNotFound)
case 'java_error':
return formatMessage(failureSummaryMessages.javaSetupFailed)
case 'loader_error':
case 'processor_error':
return formatMessage(failureSummaryMessages.loaderSetupFailed)
case 'database_error':
return formatMessage(failureSummaryMessages.localDataError)
case 'launcher_error':
case 'metadata_error':
return getFailureSummaryForPhase(phase)
default:
return getFailureSummaryForPhase(phase)
}
}
function getFailureSummaryForPhase(phase: InstallPhaseId): string {
switch (phase) {
case 'downloading_pack_file':
return formatMessage(failureSummaryMessages.packDownloadFailed)
case 'resolving_pack':
case 'reading_pack_manifest':
return formatMessage(failureSummaryMessages.invalidModpack)
case 'downloading_content':
return formatMessage(failureSummaryMessages.contentDownloadFailed)
case 'extracting_overrides':
return formatMessage(failureSummaryMessages.couldNotSaveFiles)
case 'resolving_minecraft':
case 'downloading_minecraft':
return formatMessage(failureSummaryMessages.minecraftSetupFailed)
case 'resolving_loader':
case 'running_loader_processors':
return formatMessage(failureSummaryMessages.loaderSetupFailed)
case 'preparing_java':
return formatMessage(failureSummaryMessages.javaSetupFailed)
case 'preparing_instance':
return formatMessage(failureSummaryMessages.instanceNotFound)
case 'rolling_back':
return formatMessage(failureSummaryMessages.cleanupIncomplete)
default:
return formatMessage(failureSummaryMessages.unexpectedError)
}
}
function hasPermissionError(job: InstallJobSnapshot): boolean {
const message = job.error?.message.toLowerCase() ?? ''
return (
message.includes('permission denied') ||
message.includes('access is denied') ||
message.includes('operation not permitted')
)
}
function getProgressType(job: InstallJobSnapshot): PopupNotificationProgressType | undefined {
if (!getEffectiveProgress(job)) return undefined
if (
@@ -414,165 +235,11 @@ export async function useInstallJobNotifications(opts: {
return job.status === 'failed' || job.status === 'interrupted'
}
function canShowStalledProgressDetails(job: InstallJobSnapshot): boolean {
return (
job.status === 'running' &&
job.phase !== 'preparing_instance' &&
job.phase !== 'finalizing' &&
job.phase !== 'rolling_back'
)
}
function getTerminalButtons(job: InstallJobSnapshot): PopupNotificationButton[] | undefined {
if (!isTerminalJob(job)) return undefined
function getJobSortRank(job: InstallJobSnapshot): number {
if (isTerminalJob(job)) return 0
if (job.status === 'queued' || job.phase === 'preparing_instance') return 2
return 1
}
function progressSignature(job: InstallJobSnapshot): string {
const progress = job.progress
const secondary = progress?.secondary
return [
job.status,
job.phase,
JSON.stringify(job.details),
progress?.current ?? '',
progress?.total ?? '',
secondary?.current ?? '',
secondary?.total ?? '',
].join(':')
}
function clearCopied(jobId: string) {
if (!copiedJobIds.value.has(jobId)) {
return
}
const timeout = copiedResetTimeouts.get(jobId)
if (timeout != null) {
window.clearTimeout(timeout)
copiedResetTimeouts.delete(jobId)
}
const nextCopiedJobIds = new Set(copiedJobIds.value)
nextCopiedJobIds.delete(jobId)
copiedJobIds.value = nextCopiedJobIds
}
function clearProgressSnapshot(jobId: string) {
const snapshot = progressSnapshots.get(jobId)
if (snapshot?.timeout != null) {
window.clearTimeout(snapshot.timeout)
}
progressSnapshots.delete(jobId)
}
function scheduleStaleProgressRefresh(jobId: string) {
const snapshot = progressSnapshots.get(jobId)
if (!snapshot) {
return
}
snapshot.timeout = window.setTimeout(() => {
const snapshot = progressSnapshots.get(jobId)
if (!snapshot) {
return
}
snapshot.timeout = null
opts.onChange()
}, copyDetailsStallMs)
}
function syncProgressSnapshots(nextJobs: InstallJobSnapshot[]) {
const trackedJobIds = new Set<string>()
const now = Date.now()
for (const job of nextJobs) {
if (!canShowStalledProgressDetails(job)) {
continue
}
trackedJobIds.add(job.job_id)
const signature = progressSignature(job)
const snapshot = progressSnapshots.get(job.job_id)
if (snapshot?.signature === signature) {
continue
}
clearProgressSnapshot(job.job_id)
clearCopied(job.job_id)
progressSnapshots.set(job.job_id, {
signature,
changedAt: now,
timeout: null,
})
scheduleStaleProgressRefresh(job.job_id)
}
for (const jobId of progressSnapshots.keys()) {
if (!trackedJobIds.has(jobId)) {
clearProgressSnapshot(jobId)
}
}
}
function hasStalledProgress(job: InstallJobSnapshot): boolean {
const snapshot = progressSnapshots.get(job.job_id)
return !!snapshot && Date.now() - snapshot.changedAt >= copyDetailsStallMs
}
function shouldShowCopyDetails(job: InstallJobSnapshot): boolean {
return isTerminalJob(job) || (canShowStalledProgressDetails(job) && hasStalledProgress(job))
}
function isCopied(job: InstallJobSnapshot): boolean {
return copiedJobIds.value.has(job.job_id)
}
function setCopied(job: InstallJobSnapshot) {
copiedJobIds.value = new Set([...copiedJobIds.value, job.job_id])
const existingTimeout = copiedResetTimeouts.get(job.job_id)
if (existingTimeout != null) {
window.clearTimeout(existingTimeout)
}
copiedResetTimeouts.set(
job.job_id,
window.setTimeout(() => {
copiedResetTimeouts.delete(job.job_id)
if (!copiedJobIds.value.has(job.job_id)) {
return
}
const nextCopiedJobIds = new Set(copiedJobIds.value)
nextCopiedJobIds.delete(job.job_id)
copiedJobIds.value = nextCopiedJobIds
opts.onChange()
}, 1_000),
)
opts.onChange()
}
async function copyJobDetails(job: InstallJobSnapshot) {
const details = await install_job_support_details(job.job_id).catch((error) => {
opts.handleError(error)
return null
})
if (!details) {
return
}
try {
await navigator.clipboard.writeText(details)
setCopied(job)
} catch (error) {
opts.handleError(error)
}
}
function getButtons(job: InstallJobSnapshot): PopupNotificationButton[] {
const buttons: PopupNotificationButton[] = []
if (isTerminalJob(job)) {
buttons.push({
{
label: formatMessage(messages.retry),
icon: UpdatedIcon,
color: 'brand',
@@ -581,23 +248,8 @@ export async function useInstallJobNotifications(opts: {
await install_job_retry(job.job_id).catch(opts.handleError)
await refresh()
},
})
}
if (shouldShowCopyDetails(job)) {
const copied = isCopied(job)
buttons.push({
label: formatMessage(copied ? messages.copied : messages.copyDetails),
icon: copied ? CheckIcon : CopyIcon,
color: 'standard',
keepOpen: true,
action: async () => {
await copyJobDetails(job)
},
})
}
return buttons
},
]
}
function setJobs(nextJobs: InstallJobSnapshot[]) {
@@ -607,15 +259,13 @@ export async function useInstallJobNotifications(opts: {
}
}
const visibleJobs = nextJobs.filter((job) => visibleJobStatuses.has(job.status))
syncProgressSnapshots(visibleJobs)
jobs.value = visibleJobs.sort(
(a, b) =>
getJobSortRank(a) - getJobSortRank(b) ||
a.created.localeCompare(b.created) ||
(jobOrder.get(a.job_id) ?? 0) - (jobOrder.get(b.job_id) ?? 0),
)
jobs.value = nextJobs
.filter((job) => visibleJobStatuses.has(job.status))
.sort(
(a, b) =>
a.created.localeCompare(b.created) ||
(jobOrder.get(a.job_id) ?? 0) - (jobOrder.get(b.job_id) ?? 0),
)
}
const progressItems = computed<PopupNotificationProgressItem[]>(() =>
@@ -634,7 +284,7 @@ export async function useInstallJobNotifications(opts: {
progressType: isTerminalJob(job) ? undefined : getProgressType(job),
progressCurrent: isTerminalJob(job) ? undefined : progress?.current,
progressTotal: isTerminalJob(job) ? undefined : progress?.total,
buttons: getButtons(job),
buttons: getTerminalButtons(job),
onDismiss: isTerminalJob(job)
? async () => {
await install_job_dismiss(job.job_id).catch(opts.handleError)
@@ -732,14 +382,6 @@ export async function useInstallJobNotifications(opts: {
progressItems,
buttons,
refresh,
dispose: () => {
for (const timeout of copiedResetTimeouts.values()) {
window.clearTimeout(timeout)
}
for (const jobId of progressSnapshots.keys()) {
clearProgressSnapshot(jobId)
}
unlisten()
},
dispose: () => unlisten(),
}
}
@@ -8,18 +8,13 @@ import type { ComputedRef, Ref } from 'vue'
import { onUnmounted, ref, shallowRef } from 'vue'
import type { Router } from 'vue-router'
import {
fetchCachedServerStatus,
getFreshCachedServerStatus,
} from '@/composables/instances/use-server-status-query'
import { process_listener } from '@/helpers/events'
import { kill, list as listInstances } from '@/helpers/instance'
import { get_by_instance_id } from '@/helpers/process'
import type { GameInstance } from '@/helpers/types'
import { add_server_to_instance, getServerAddress } from '@/helpers/worlds'
import { add_server_to_instance, getServerAddress, getServerLatency } from '@/helpers/worlds'
interface BrowseServerInstance {
id: string
name: string
path: string
}
@@ -73,10 +68,12 @@ export function useAppServerBrowse(options: UseAppServerBrowseOptions) {
const queryClient = useQueryClient()
const debugLog = useDebugLogger('BrowseServer')
const serverPings = shallowRef<Record<string, number | undefined>>({})
const serverPingCache = new Map<string, number | undefined>()
const pendingServerPings = new Map<string, Promise<number | undefined>>()
const runningServerProjects = ref<Record<string, string>>({})
const lastServerHits = shallowRef<Labrinth.Search.v3.ResultSearchProject[]>([])
const contextMenuRef = ref<ContextMenuHandle | null>(null)
let serverPingsActive = true
let serverPingCacheActive = true
let unlistenProcesses: (() => void) | null = null
async function checkServerRunningStates(hits: Labrinth.Search.v3.ResultSearchProject[]) {
@@ -149,26 +146,37 @@ export function useAppServerBrowse(options: UseAppServerBrowseOptions) {
})
const nextPings = { ...serverPings.value }
for (const { hit, address } of pingsToFetch) {
const cachedStatus = getFreshCachedServerStatus(queryClient, address)
if (cachedStatus) {
nextPings[hit.project_id] = cachedStatus.ping
if (serverPingCache.has(address)) {
nextPings[hit.project_id] = serverPingCache.get(address)
}
}
serverPings.value = nextPings
await Promise.all(
pingsToFetch.map(async ({ hit, address }) => {
if (getFreshCachedServerStatus(queryClient, address)) return
if (serverPingCache.has(address)) return
try {
const status = await fetchCachedServerStatus(queryClient, address)
if (!serverPingsActive) return
serverPings.value = { ...serverPings.value, [hit.project_id]: status.ping }
} catch (error) {
console.error(`Failed to ping server ${address}:`, error)
if (!serverPingsActive) return
serverPings.value = { ...serverPings.value, [hit.project_id]: undefined }
let pending = pendingServerPings.get(address)
if (!pending) {
pending = getServerLatency(address)
.then((latency) => {
if (serverPingCacheActive) serverPingCache.set(address, latency)
return latency
})
.catch((error) => {
console.error(`Failed to ping server ${address}:`, error)
if (serverPingCacheActive) serverPingCache.set(address, undefined)
return undefined
})
.finally(() => {
pendingServerPings.delete(address)
})
pendingServerPings.set(address, pending)
}
const latency = await pending
if (!serverPingCacheActive) return
serverPings.value = { ...serverPings.value, [hit.project_id]: latency }
}),
)
}
@@ -300,8 +308,10 @@ export function useAppServerBrowse(options: UseAppServerBrowseOptions) {
.catch(options.handleError)
onUnmounted(() => {
serverPingsActive = false
serverPingCacheActive = false
unlistenProcesses?.()
serverPingCache.clear()
pendingServerPings.clear()
})
return {
@@ -1,50 +0,0 @@
import type { QueryClient } from '@tanstack/vue-query'
import {
get_server_status,
normalizeServerAddress,
type ProtocolVersion,
type ServerStatus,
} from '@/helpers/worlds'
export const SERVER_STATUS_CACHE_MS = 10 * 60 * 1000
function getProtocolVersionKey(protocolVersion: ProtocolVersion | null) {
if (!protocolVersion) return 'default'
return `${protocolVersion.version}:${protocolVersion.legacy ? 'legacy' : 'modern'}`
}
export function getServerStatusQueryKey(
address: string,
protocolVersion: ProtocolVersion | null = null,
) {
return [
'minecraft-server-status',
normalizeServerAddress(address) || address.trim().toLowerCase(),
getProtocolVersionKey(protocolVersion),
] as const
}
export function getFreshCachedServerStatus(
queryClient: QueryClient,
address: string,
protocolVersion: ProtocolVersion | null = null,
) {
const queryKey = getServerStatusQueryKey(address, protocolVersion)
const updatedAt = queryClient.getQueryState(queryKey)?.dataUpdatedAt ?? 0
if (!updatedAt || Date.now() - updatedAt >= SERVER_STATUS_CACHE_MS) return undefined
return queryClient.getQueryData<ServerStatus>(queryKey)
}
export async function fetchCachedServerStatus(
queryClient: QueryClient,
address: string,
protocolVersion: ProtocolVersion | null = null,
) {
return await queryClient.fetchQuery({
queryKey: getServerStatusQueryKey(address, protocolVersion),
queryFn: () => get_server_status(address, protocolVersion),
staleTime: SERVER_STATUS_CACHE_MS,
gcTime: SERVER_STATUS_CACHE_MS,
})
}
-17
View File
@@ -1,5 +1,4 @@
import { invoke } from '@tauri-apps/api/core'
import { listen } from '@tauri-apps/api/event'
export async function init_ads_window(overrideShown = false) {
return await invoke('plugin:ads|init_ads_window', {
@@ -16,22 +15,6 @@ export async function hide_ads_window(reset) {
return await invoke('plugin:ads|hide_ads_window', { reset })
}
export async function get_ads_consent_required() {
return await invoke('plugin:ads|get_ads_consent_required')
}
export async function perform_ads_consent_action(action) {
return await invoke('plugin:ads|perform_ads_consent_action', { action })
}
export async function open_ads_consent_preferences() {
return await invoke('plugin:ads|open_ads_consent_preferences')
}
export async function ads_consent_listener(callback) {
return await listen('ads-consent-required', (event) => callback(event.payload))
}
export async function record_ads_click() {
return await invoke('plugin:ads|record_ads_click')
}
+1 -37
View File
@@ -27,7 +27,6 @@ export interface InstallModpackPreview {
iconUrl?: string | null
link?: InstanceLink | null
unknownFile: boolean
externalFilesInModpack: string[]
}
export interface InstallCreateInstanceRequest {
@@ -86,36 +85,6 @@ export type InstallJavaStep =
| 'extracting'
| 'validating'
export interface InstallErrorView {
code: string
phase?: InstallPhaseId | null
message: string
api?: {
error: string
status?: number | null
method?: string | null
url?: string | null
route?: string | null
} | null
context?: {
operation: string
source_path?: string | null
target_path?: string | null
file_path?: string | null
entry_path?: string | null
urls?: string[]
expected_hash?: string | null
expected_size?: number | null
project_id?: string | null
version_id?: string | null
minecraft_version?: string | null
loader?: string | null
java_version?: number | null
os?: string | null
arch?: string | null
} | null
}
export interface InstallJobSnapshot {
job_id: string
instance_id?: string | null
@@ -145,8 +114,7 @@ export interface InstallJobSnapshot {
}
| { type: 'import'; launcher_type: string; instance_folder: string }
display?: { title: string; icon?: string | null } | null
error?: InstallErrorView | null
rollback_error?: InstallErrorView | null
error?: { code: string; message: string } | null
created: string
modified: string
finished?: string | null
@@ -229,10 +197,6 @@ export async function install_job_dismiss(jobId: string) {
return await invoke<void>('plugin:install|install_job_dismiss', { jobId })
}
export async function install_job_support_details(jobId: string) {
return await invoke<string>('plugin:install|install_job_support_details', { jobId })
}
export function installJobInstanceId(job: InstallJobSnapshot): string | null {
return job.instance_id ?? job.target.instance_id ?? null
}
@@ -242,10 +242,6 @@ export async function add_project_from_path(
})
}
export async function is_file_on_modrinth(projectPath: string): Promise<boolean> {
return await invoke('plugin:instance|instance_is_file_on_modrinth', { projectPath })
}
// Toggle disabling a project
export async function toggle_disable_project(
instanceId: string,
@@ -1,11 +1,8 @@
import { ClassicPlayerModel, SlimPlayerModel } from '@modrinth/assets'
import {
applyCapeTexture,
applyEarsMod,
createTransparentTexture,
disposeCaches,
loadTexture,
removeEarsMod,
setupSkinModel,
} from '@modrinth/ui'
import * as THREE from 'three'
@@ -74,13 +71,12 @@ class BatchSkinRenderer {
textureUrl: string,
modelUrl: string,
capeUrl?: string,
earsTextureUrl?: string,
): Promise<RawRenderResult> {
this.initializeRenderer()
this.clearScene()
await this.setupModel(modelUrl, textureUrl, capeUrl, earsTextureUrl)
await this.setupModel(modelUrl, textureUrl, capeUrl)
const headPart = this.currentModel!.getObjectByName('Head')
let lookAtTarget: [number, number, number]
@@ -127,29 +123,17 @@ class BatchSkinRenderer {
})
}
private async setupModel(
modelUrl: string,
textureUrl: string,
capeUrl?: string,
earsTextureUrl?: string,
): Promise<void> {
private async setupModel(modelUrl: string, textureUrl: string, capeUrl?: string): Promise<void> {
if (!this.scene) {
throw new Error('Renderer not initialized')
}
const [{ model }, earsTexture] = await Promise.all([
setupSkinModel(modelUrl, textureUrl, capeUrl),
earsTextureUrl ? loadTexture(earsTextureUrl) : Promise.resolve(null),
])
const { model } = await setupSkinModel(modelUrl, textureUrl, capeUrl)
if (!capeUrl) {
applyCapeTexture(model, null, this.getTransparentTexture())
}
if (earsTexture) {
applyEarsMod(model, earsTexture)
}
const group = new THREE.Group()
group.add(model)
group.position.set(0, 0.3, 1.95)
@@ -170,7 +154,6 @@ class BatchSkinRenderer {
private clearScene(): void {
if (!this.scene || !this.currentModel) return
removeEarsMod(this.currentModel)
this.scene.remove(this.currentModel)
this.currentModel.clear()
this.currentModel = null
@@ -210,7 +193,6 @@ function getModelUrlForVariant(variant: string): string {
export const skinBlobUrlMap = reactive(new Map<string, RenderResult>())
export const headBlobUrlMap = reactive(new Map<string, string>())
const DEBUG_MODE = false
const SKIN_PREVIEW_RENDER_VERSION = 'ears-2-fixed-uvs'
let sharedRenderer: BatchSkinRenderer | null = null
let latestPreviewGeneration = 0
@@ -223,10 +205,6 @@ function getSharedRenderer(): BatchSkinRenderer {
return sharedRenderer
}
export function getSkinPreviewKey(skin: Skin): string {
return `${SKIN_PREVIEW_RENDER_VERSION}+${skin.texture_key}+${skin.variant}+${skin.cape_id ?? 'no-cape'}`
}
export function disposeSharedRenderer(): void {
if (sharedRenderer) {
sharedRenderer.dispose()
@@ -239,7 +217,7 @@ export async function cleanupUnusedPreviews(skins: Skin[]): Promise<void> {
const validHeadKeys = new Set<string>()
for (const skin of skins) {
const key = getSkinPreviewKey(skin)
const key = `${skin.texture_key}+${skin.variant}+${skin.cape_id ?? 'no-cape'}`
const headKey = `${skin.texture_key}-head`
validKeys.add(key)
validHeadKeys.add(headKey)
@@ -404,7 +382,9 @@ async function generateSkinPreviewsForGeneration(
const isCurrentGeneration = () => generation === latestPreviewGeneration
try {
const skinKeys = skins.map(getSkinPreviewKey)
const skinKeys = skins.map(
(skin) => `${skin.texture_key}+${skin.variant}+${skin.cape_id ?? 'no-cape'}`,
)
const headKeys = skins.map((skin) => `${skin.texture_key}-head`)
const [cachedSkinPreviews, cachedHeadPreviews] = await Promise.all([
@@ -435,7 +415,7 @@ async function generateSkinPreviewsForGeneration(
for (const skin of skins) {
if (!isCurrentGeneration()) return
const key = getSkinPreviewKey(skin)
const key = `${skin.texture_key}+${skin.variant}+${skin.cape_id ?? 'no-cape'}`
if (skinBlobUrlMap.has(key)) {
if (DEBUG_MODE) {
@@ -463,7 +443,6 @@ async function generateSkinPreviewsForGeneration(
await get_normalized_skin_texture(skin),
modelUrl,
cape?.texture,
skin.texture,
)
if (!isCurrentGeneration()) return
-7
View File
@@ -133,18 +133,11 @@ type Hooks = {
type Manifest = {
gameVersions: ManifestGameVersion[]
versionGroups?: ManifestVersionGroup[]
}
type ManifestGameVersion = {
id: string
stable: boolean
versionGroup?: string
loaders: ManifestLoaderVersion[]
}
type ManifestVersionGroup = {
id: string
loaders: ManifestLoaderVersion[]
}
+23 -30
View File
@@ -404,35 +404,28 @@ export async function refreshServerData(
): Promise<void> {
const refreshTime = Date.now()
serverData.refreshing = true
try {
const status = await get_server_status(address, protocolVersion)
if (serverData.lastSuccessfulRefresh && serverData.lastSuccessfulRefresh > refreshTime) {
// Don't update if there was a more recent successful refresh
return
}
serverData.lastSuccessfulRefresh = Date.now()
serverData.status = status
if (status.description) {
serverData.rawMotd = status.description
serverData.renderedMotd = autoToHTML(status.description)
} else {
delete serverData.rawMotd
delete serverData.renderedMotd
}
} catch (err) {
console.error(`Refreshing addr ${address}`, protocolVersion, err)
if (!protocolVersion?.legacy) {
await refreshServerData(serverData, { version: 74, legacy: true }, address)
return
}
if (!serverData.lastSuccessfulRefresh || serverData.lastSuccessfulRefresh <= refreshTime) {
delete serverData.status
delete serverData.rawMotd
delete serverData.renderedMotd
}
} finally {
serverData.refreshing = false
}
await get_server_status(address, protocolVersion)
.then((status) => {
if (serverData.lastSuccessfulRefresh && serverData.lastSuccessfulRefresh > refreshTime) {
// Don't update if there was a more recent successful refresh
return
}
serverData.lastSuccessfulRefresh = Date.now()
serverData.status = status
if (status.description) {
serverData.rawMotd = status.description
serverData.renderedMotd = autoToHTML(status.description)
}
})
.finally(() => {
serverData.refreshing = false
})
.catch((err) => {
console.error(`Refreshing addr ${address}`, protocolVersion, err)
if (!protocolVersion?.legacy) {
refreshServerData(serverData, { version: 74, legacy: true }, address)
}
})
}
export function refreshServers(
@@ -451,7 +444,7 @@ export function refreshServers(
}
})
// noinspection ES6MissingAwait - handled by refreshServerData
// noinspection ES6MissingAwait - handled with .then by refreshServerData already
Object.keys(serverData).forEach((address) =>
refreshServerData(serverData[address], protocolVersion, address),
)
+24 -381
View File
@@ -11,87 +11,6 @@
"app.action-bar.hide-more-running-instances": {
"message": "إخفاء المزيد من الحالات قيد التشغيل"
},
"app.action-bar.install.copied-details": {
"message": "تم النسخ"
},
"app.action-bar.install.copy-details": {
"message": "نسخ التفاصيل"
},
"app.action-bar.install.dismiss": {
"message": "التجاهل"
},
"app.action-bar.install.open-instance": {
"message": "فتح المثيل"
},
"app.action-bar.install.retry": {
"message": "إعادة المحاولة"
},
"app.action-bar.install.summary.app-closing": {
"message": "تم الإلغاء بسبب إغلاق التطبيق"
},
"app.action-bar.install.summary.bad-modpack-file": {
"message": "تعذّر قراءة حزمة التعديلات"
},
"app.action-bar.install.summary.canceled": {
"message": "تم الإلغاء"
},
"app.action-bar.install.summary.cleanup-incomplete": {
"message": "لم تكتمل عملية التنظيف"
},
"app.action-bar.install.summary.content-download-failed": {
"message": "تعذّر تنزيل الملفات"
},
"app.action-bar.install.summary.corrupt-download": {
"message": "الملف الذي تم تنزيله تالف"
},
"app.action-bar.install.summary.could-not-save-files": {
"message": "تعذّر حفظ الملفات"
},
"app.action-bar.install.summary.download-failed": {
"message": "لم يكتمل التنزيل"
},
"app.action-bar.install.summary.instance-not-found": {
"message": "تعذّر العثور على المثيل"
},
"app.action-bar.install.summary.invalid-file-path": {
"message": "مسار الملف غير صالح"
},
"app.action-bar.install.summary.invalid-modpack": {
"message": "بيانات حزمة التعديلات غير صالحة"
},
"app.action-bar.install.summary.invalid-modpack-files": {
"message": "تحتوي ملفات حزمة التعديلات على بيانات وصفية غير صالحة"
},
"app.action-bar.install.summary.java-setup-failed": {
"message": "لم يكتمل إعداد Java"
},
"app.action-bar.install.summary.loader-setup-failed": {
"message": "فشل إعداد أداة التحميل"
},
"app.action-bar.install.summary.local-data-error": {
"message": "تعذر تحديث البيانات المحلية"
},
"app.action-bar.install.summary.minecraft-setup-failed": {
"message": "فشل إعداد Minecraft"
},
"app.action-bar.install.summary.modrinth-unreachable": {
"message": "لم أتمكن من الوصول إلى Modrinth"
},
"app.action-bar.install.summary.no-write-permission": {
"message": "لا يوجد إذن للكتابة"
},
"app.action-bar.install.summary.pack-download-failed": {
"message": "تعذر تنزيل الحزمة"
},
"app.action-bar.install.summary.unexpected-error": {
"message": "حدث خطأ ما"
},
"app.action-bar.install.unknown-instance": {
"message": "مثيل غير معروف"
},
"app.action-bar.installs": {
"message": "عمليات التثبيت"
},
"app.action-bar.make-primary-instance": {
"message": "إنشاء نسخة أساسية"
},
@@ -155,63 +74,12 @@
"app.appearance-settings.hide-nametag.title": {
"message": "إخفاء عرض الاسم"
},
"app.appearance-settings.jump-back-into-worlds.description": {
"message": "يتضمن العوالم الحديثة في قسم \"العودة إلى اللعب\" على الصفحة الرئيسية."
},
"app.appearance-settings.jump-back-into-worlds.title": {
"message": "العودة إلى العوالم"
},
"app.appearance-settings.minimize-launcher.description": {
"message": "قم بتصغير نافذة المشغل عند بدء تشغيل عملية Minecraft."
},
"app.appearance-settings.minimize-launcher.title": {
"message": "تصغير نافذة المشغل"
},
"app.appearance-settings.native-decorations.description": {
"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": "إظهار مدة التشغيل"
},
"app.appearance-settings.skip-non-essential-warnings.description": {
"message": "يتخطى تلقائيًا عمليات التأكيد ذات المخاطر المنخفضة، مثل تثبيت حزم التعديلات المكررة، وحذف المحتوى العادي، والتحديثات الجماعية، وإلغاء ربط حزم التعديلات، ومطالبات الإصلاح. وسيستمر عرض التحذيرات الخطيرة."
},
"app.appearance-settings.skip-non-essential-warnings.title": {
"message": "تخطي التحذيرات غير الضرورية"
},
"app.appearance-settings.toggle-sidebar.description": {
"message": "يتيح إمكانية إظهار الشريط الجانبي أو إخفائه."
},
"app.appearance-settings.toggle-sidebar.title": {
"message": "تبديل الشريط الجانبي"
},
"app.appearance-settings.unknown-pack-warning.description": {
"message": "إذا حاولت تثبيت ملف Modrinth Pack (.mrpack) غير مستضاف على Modrinth، فسنتأكد من أنك على دراية بالمخاطر قبل تثبيته."
},
"app.appearance-settings.unknown-pack-warning.title": {
"message": "أخبرني قبل تثبيت حزم التعديلات غير المعروفة"
},
"app.auth-servers.unreachable.body": {
"message": "قد تكون خوادم مصادقة ماينكرافت معطلة حاليًا. تحقق من اتصالك بالإنترنت وحاول مرة أخرى لاحقًا."
},
"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": "أضف للنموذج"
},
@@ -234,20 +102,11 @@
"message": "استكشف خوادم"
},
"app.browse.hide-added-servers": {
"message": "إخفاء الخوادم التي تمت إضافتها بالفعل"
},
"app.browse.project-type.modpacks": {
"message": "حزم التعديل"
},
"app.browse.server-instance-content-warning": {
"message": "قد تؤدي إضافة المحتوى إلى حدوث مشاكل في التوافق عند الاتصال بالخادم. كما سيُفقد أي محتوى تمت إضافته عند تحديث محتوى مثيل الخادم."
"message": "إخفاء الخوادم المضافة مسبقًا"
},
"app.browse.server.installing": {
"message": "جاري التثبيت"
},
"app.content-install.no-compatible-versions": {
"message": "لا توجد إصدارات متاحة تتوافق مع {compatibilityLabel}. اختر إصدارًا لتثبيته على أي حال. لن يتم تثبيت التبعيات تلقائيًا."
},
"app.export-modal.description-placeholder": {
"message": "أدخل وصف التعديل..."
},
@@ -263,66 +122,15 @@
"app.export-modal.modpack-name-placeholder": {
"message": "إسم حزمة التعديل"
},
"app.export-modal.select-files-label": {
"message": "أختيار الملفات التي سيتم تصديرها"
},
"app.export-modal.version-number-label": {
"message": "رقم الإصدار"
},
"app.export-modal.version-number-placeholder": {
"message": "1.0.0"
},
"app.install.phase.downloading_content": {
"message": "تنزيل المحتوى"
},
"app.install.phase.downloading_minecraft": {
"message": "تنزيل لعبة Minecraft"
},
"app.install.phase.downloading_pack_file": {
"message": "تنزيل ملف الحزمة"
},
"app.install.phase.extracting_overrides": {
"message": "استخراج التجاوزات"
},
"app.install.phase.finalizing": {
"message": "وضع اللمسات الأخيرة"
},
"app.install.phase.preparing_instance": {
"message": "في قائمة الانتظار للتثبيت"
},
"app.install.phase.preparing_java": {
"message": "التحضير لـ Java"
},
"app.install.phase.preparing_java.downloading": {
"message": "تنزيل Java {version}"
},
"app.install.phase.preparing_java.extracting": {
"message": "استخراج Java {version}"
},
"app.install.phase.preparing_java.fetching-metadata": {
"message": "جلب Java {version}"
},
"app.install.phase.preparing_java.resolving": {
"message": "التحضير لـ Java {version}"
},
"app.install.phase.preparing_java.validating": {
"message": "التحقق من صحة إصدار Java {version}"
},
"app.install.phase.reading_pack_manifest": {
"message": "قراءة قائمة المحتويات"
},
"app.install.phase.resolving_loader": {
"message": "معالج التحميل"
},
"app.install.phase.resolving_minecraft": {
"message": "حل مشكلة Minecraft"
},
"app.install.phase.resolving_pack": {
"message": "معالجة المحتوى"
},
"app.install.phase.rolling_back": {
"message": "التراجع"
},
"app.install.phase.running_loader_processors": {
"message": "تشغيل معالجات التحميل"
},
"app.instance.confirm-delete.admonition-body": {
"message": "سيتم حذف جميع البيانات الخاصة لنموذجك نهائيًا، بما في ذلك عوالمك والتكوينات وكل المحتوى المثبت."
},
@@ -347,15 +155,6 @@
"app.instance.modpack-already-installed.instance": {
"message": "النموذج"
},
"app.instance.mods.bulk-update.downloading-projects": {
"message": "جاري تنزيل {current, number}/{total, number} مشروعًا..."
},
"app.instance.mods.bulk-update.finishing": {
"message": "آخر المستجدات حول عملية التشطيب..."
},
"app.instance.mods.bulk-update.resolving-versions": {
"message": "تحديد الإصدارات..."
},
"app.instance.mods.content-type-project": {
"message": "مشروع"
},
@@ -380,18 +179,6 @@
"app.instance.worlds.browse-servers": {
"message": "تصفح الخوادم"
},
"app.instance.worlds.delete-world-modal.delete-button": {
"message": "حذف العالم"
},
"app.instance.worlds.delete-world-modal.title": {
"message": "حذف العالم"
},
"app.instance.worlds.delete-world-modal.warning-body": {
"message": "سيتم حذف هذا العالم نهائيًا من هذه النسخة. لا يمكن التراجع عن هذا الإجراء."
},
"app.instance.worlds.delete-world-modal.warning-header": {
"message": "حذف {name}"
},
"app.instance.worlds.filter-modded": {
"message": "معدّل"
},
@@ -410,21 +197,12 @@
"app.instance.worlds.no-worlds-heading": {
"message": "لم تتم إضافة أي من الخوادم أو العوالم"
},
"app.instance.worlds.remove-server-modal.remove-button": {
"message": "إزالة الخادم"
},
"app.instance.worlds.remove-server-modal.title": {
"message": "إزالة الخادم"
},
"app.instance.worlds.remove-server-modal.warning-body": {
"message": "سيتم حذف هذا الخادم من قائمة الخوادم الخاصة بك ومن قائمة الخوادم داخل اللعبة. يمكنك إضافته مرة أخرى لاحقًا إذا كنت تعرف عنوانه."
},
"app.instance.worlds.remove-server-modal.warning-header": {
"message": "إزالة {name}"
},
"app.instance.worlds.search-worlds-placeholder": {
"message": "ابحث بين ال{count} عوالم..."
},
"app.modal.install-to-play.content-required": {
"message": "المحتوى مطلوب"
},
"app.modal.install-to-play.header": {
"message": "نزل للعب"
},
@@ -434,38 +212,35 @@
"app.modal.install-to-play.mod-count": {
"message": "{count, plural, one {# تعديل} other {# تعديلات}}"
},
"app.modal.install-to-play.required-modpack": {
"message": "حُزْمَة التعديل مطلوبة"
},
"app.modal.install-to-play.server-requires-mods": {
"message": "يتطلب هذا الخادم تعديلات للعب. انقر فوق \"تثبيت\" لإعداد الملفات المطلوبة من Modrinth، ثم قم بتشغيله مباشرة إلى الخادم."
},
"app.modal.install-to-play.shared-instance": {
"message": "النماذج المشتركة"
},
"app.modal.install-to-play.shared-server-instance": {
"message": "نماذج الخادم مشتركة"
},
"app.modal.install-to-play.view-contents": {
"message": "عرض المحتويات"
},
"app.modal.update-to-play.header": {
"message": "حدث للعب"
},
"app.modal.update-to-play.update-required": {
"message": "يلزم التحديث"
},
"app.modal.update-to-play.update-required-description": {
"message": "هناك تحديث مطلوب للعب بـ {name}. الرجاء التحديث إلى أحدث اصدار لتشغيل اللعبة."
},
"app.project.install-button.already-installed": {
"message": "هذا المشروع مُثبَّت بالفعل"
},
"app.project.install-button.switch-version": {
"message": "تغيير الإصدار"
},
"app.project.install-context.back-to-browse": {
"message": "العودة إلى الاستكشاف"
},
"app.project.version.all-versions": {
"message": "جميع الإصدارات"
},
"app.project.version.download-in-browser": {
"message": "التنزيل من المتصفح"
},
"app.project.version.installing": {
"message": "التثبيت"
},
"app.project.versions.already-installed": {
"message": "مثبت بالفعل"
"app.project.install-context.install-content-to-instance": {
"message": "تثبيت المحتويات الى النموذج"
},
"app.settings.developer-mode-enabled": {
"message": "تم تفعيل وضع المطوّر."
@@ -509,12 +284,6 @@
"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": "تعديل المظهر"
},
@@ -524,15 +293,6 @@
"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": "رداء"
},
@@ -551,18 +311,12 @@
"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": "أرفع المظهر أولا !"
},
@@ -572,69 +326,9 @@
"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": "البناة والبيئات الحيوية"
},
"app.skins.section.chaos-cubed": {
"message": "الفوضى المربعة"
},
"app.skins.section.chase-the-skies": {
"message": "انطلق نحو السماء"
},
"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"
},
"app.skins.section.modrinth-pride.tooltip": {
"message": "لقد حصلت على هذه أشكال الشخصيات مقابل تبرعك لحملة جمع التبرعات التي نظمتها Modrinth Pride خلال شهر الفخر."
},
"app.skins.section.mounts-of-mayhem": {
"message": "جبال الفوضى"
},
"app.skins.section.saved-skins": {
"message": "المظاهر المحفوظة"
},
"app.skins.section.striding-hero": {
"message": "البطل السائر بخطوات واسعة"
},
"app.skins.section.the-copper-age": {
"message": "العصر النحاسي"
},
"app.skins.section.the-garden-awakens": {
"message": "الحديقة تستيقظ"
},
"app.skins.section.tiny-takeover": {
"message": "الاستحواذ الصغير"
},
"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": "الرجاء تسجيل الدخول"
},
@@ -659,9 +353,6 @@
"app.update-popup.download-complete": {
"message": "اكتمل التنزيل"
},
"app.update-popup.reload": {
"message": "أعد التحميل للتحديث"
},
"app.update-popup.title": {
"message": "تحديث متاح"
},
@@ -860,30 +551,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": "إجراءات تشغيل إضافية"
},
@@ -932,24 +599,6 @@
"instance.settings.tabs.java": {
"message": "جافا والذاكرة"
},
"instance.settings.tabs.java.custom-environment-variables": {
"message": "متغيرات البيئة المخصصة"
},
"instance.settings.tabs.java.custom-java-arguments": {
"message": "معلمات Java المخصصة"
},
"instance.settings.tabs.java.custom-java-installation": {
"message": "تثبيت Java المخصص"
},
"instance.settings.tabs.java.custom-memory-allocation": {
"message": "تخصيص الذاكرة حسب الطلب"
},
"instance.settings.tabs.java.enter-environment-variables": {
"message": "أدخل المتغيرات البيئية..."
},
"instance.settings.tabs.java.enter-java-arguments": {
"message": "أدخل معلمات Java..."
},
"instance.settings.tabs.java.environment-variables": {
"message": "المتغيرات البيئية"
},
@@ -1007,9 +656,6 @@
"instance.worlds.copy_address": {
"message": "نسخ العنوان"
},
"instance.worlds.create_shortcut": {
"message": "إنشاء اختصار"
},
"instance.worlds.dont_show_on_home": {
"message": "عدم العرض في الصفحة الرئيسية"
},
@@ -1049,12 +695,6 @@
"minecraft-account.label": {
"message": "حساب ماينكرافت"
},
"minecraft-account.not-signed-in": {
"message": "لم يتم تسجيل الدخول"
},
"minecraft-account.remove-account": {
"message": "حذف الحساب"
},
"minecraft-account.select-account": {
"message": "اختيار الحساب"
},
@@ -1084,5 +724,8 @@
},
"search.filter.locked.server-loader.title": {
"message": "يتم توفير المحمّل من قبل الخادم"
},
"unknown-pack-warning-modal.dont-show-again": {
"message": "لا تعرض هذا التحذير مجددا"
}
}
+63 -147
View File
@@ -11,27 +11,6 @@
"app.action-bar.hide-more-running-instances": {
"message": "Skrýt více spuštěných instancí"
},
"app.action-bar.install.copied-details": {
"message": "Zkopírováno"
},
"app.action-bar.install.dismiss": {
"message": "Zavřít"
},
"app.action-bar.install.open-instance": {
"message": "Otevřít instanci"
},
"app.action-bar.install.retry": {
"message": "Zkusit znovu"
},
"app.action-bar.install.summary.canceled": {
"message": "Zrušeno"
},
"app.action-bar.install.unknown-instance": {
"message": "Neznámá instance"
},
"app.action-bar.installs": {
"message": "Instalace"
},
"app.action-bar.make-primary-instance": {
"message": "Nastavit jako primární instanci"
},
@@ -54,7 +33,7 @@
"message": "Zastavit instanci"
},
"app.action-bar.update": {
"message": "Aktualizovat"
"message": "Aktualizace"
},
"app.action-bar.view-active-downloads": {
"message": "Zobrazit probíhající stahování"
@@ -122,14 +101,8 @@
"app.appearance-settings.show-play-time.title": {
"message": "Zobrazit odehraný čas"
},
"app.appearance-settings.skip-non-essential-warnings.description": {
"message": "Automaticky přeskočí nízko riziková potrvzení jako duplicitní instalace modpacků, běžné mazání obsahu, hromadné aktualizace, odpojování modpacků a výzvy k opravě. Nebezpečná upozornění se budou nadále zobrazovat."
},
"app.appearance-settings.skip-non-essential-warnings.title": {
"message": "Přeskočit nedůležitá upozornění"
},
"app.appearance-settings.toggle-sidebar.description": {
"message": "Umožňí přepínat zobrazení postranního panelu."
"message": "Umožní přepínat zobrazení postranního panelu."
},
"app.appearance-settings.toggle-sidebar.title": {
"message": "Přepnout postranní panel"
@@ -168,10 +141,13 @@
"message": "Zpět k instanci"
},
"app.browse.discover-content": {
"message": "Objevit obsah"
"message": "Objevovat obsah"
},
"app.browse.discover-servers": {
"message": "Objevit servery"
"message": "Objevovat servery"
},
"app.browse.hide-added-servers": {
"message": "Skrýt již přidané servery"
},
"app.browse.project-type.modpacks": {
"message": "Modpacky"
@@ -183,7 +159,7 @@
"message": "Instalování"
},
"app.content-install.no-compatible-versions": {
"message": "Žádné dostupné verze se neshodují s {compatibilityLabel}. Vyberte verzi, kterou chcete přesto nainstalovat. Závislosti nebudou nainstalovány automaticky."
"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.export-modal.description-placeholder": {
"message": "Zadej popis modpacku..."
@@ -194,72 +170,24 @@
"app.export-modal.header": {
"message": "Exportovat modpack"
},
"app.export-modal.include-file-accessibility-label": {
"message": "Zahrnout \"{file}\"?"
},
"app.export-modal.modpack-name-label": {
"message": "Název modpacku"
},
"app.export-modal.modpack-name-placeholder": {
"message": "Název modpacku"
},
"app.export-modal.select-files-label": {
"message": "Nastavte, které soubory mají být do tohoto exportu zahrnuty"
},
"app.export-modal.version-number-label": {
"message": "Číslo verze"
},
"app.export-modal.version-number-placeholder": {
"message": "1.0.0"
},
"app.install.phase.downloading_content": {
"message": "Stahování obsahu"
},
"app.install.phase.downloading_minecraft": {
"message": "Stahování Minecraftu"
},
"app.install.phase.downloading_pack_file": {
"message": "Stahování pack souboru"
},
"app.install.phase.extracting_overrides": {
"message": "Rozbalování souborů overrides"
},
"app.install.phase.finalizing": {
"message": "Dokončení instalace"
},
"app.install.phase.preparing_instance": {
"message": "Ve frontě na instalaci"
},
"app.install.phase.preparing_java": {
"message": "Příprava Javy"
},
"app.install.phase.preparing_java.downloading": {
"message": "Stahování Javy {version}"
},
"app.install.phase.preparing_java.extracting": {
"message": "Rozbalování Javy {version}"
},
"app.install.phase.preparing_java.fetching-metadata": {
"message": "Získávání Javy {version}"
},
"app.install.phase.preparing_java.resolving": {
"message": "Připravení Javy {version}"
},
"app.install.phase.preparing_java.validating": {
"message": "Validace Javy {version}"
},
"app.install.phase.reading_pack_manifest": {
"message": "Čtení pack manifestu"
},
"app.install.phase.resolving_loader": {
"message": "Vyhodnocování loaderu"
},
"app.install.phase.resolving_minecraft": {
"message": "Vyhodnocování Minecraftu"
},
"app.install.phase.resolving_pack": {
"message": "Vyhodnocování obsahu"
},
"app.install.phase.rolling_back": {
"message": "Obnovování do předchozího stavu"
},
"app.install.phase.running_loader_processors": {
"message": "Spuštění loader procesů"
},
"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."
},
@@ -267,7 +195,7 @@
"message": "Tato akce nemůže být vrácena"
},
"app.instance.confirm-delete.delete-button": {
"message": "Smazat instanci"
"message": "Odstranit instanci"
},
"app.instance.confirm-delete.header": {
"message": "Odstranění instance"
@@ -284,15 +212,6 @@
"app.instance.modpack-already-installed.instance": {
"message": "Instance"
},
"app.instance.mods.bulk-update.downloading-projects": {
"message": "Stahování {current, number}/{total, number} projektů..."
},
"app.instance.mods.bulk-update.finishing": {
"message": "Dokončení aktualizace..."
},
"app.instance.mods.bulk-update.resolving-versions": {
"message": "Vyhodnocování verzí..."
},
"app.instance.mods.content-type-project": {
"message": "projekt"
},
@@ -317,20 +236,8 @@
"app.instance.worlds.browse-servers": {
"message": "Procházet servery"
},
"app.instance.worlds.delete-world-modal.delete-button": {
"message": "Smazat svět"
},
"app.instance.worlds.delete-world-modal.title": {
"message": "Smazat svět"
},
"app.instance.worlds.delete-world-modal.warning-body": {
"message": "Tento svět bude trvale smazán z této instance. Tuto akci nelze vrátit zpět."
},
"app.instance.worlds.delete-world-modal.warning-header": {
"message": "Mazání {name}"
},
"app.instance.worlds.filter-modded": {
"message": "Módované"
"message": "Módovaný"
},
"app.instance.worlds.filter-offline": {
"message": "Offline"
@@ -342,26 +249,17 @@
"message": "Vanilla"
},
"app.instance.worlds.no-worlds-description": {
"message": "Začněte přidáním serveru nebo procházením obsahu"
"message": "Přidej server nebo začni procházet"
},
"app.instance.worlds.no-worlds-heading": {
"message": "Nejsou přidané žádné servery nebo světy"
},
"app.instance.worlds.remove-server-modal.remove-button": {
"message": "Odebrat server"
},
"app.instance.worlds.remove-server-modal.title": {
"message": "Odebrat server"
},
"app.instance.worlds.remove-server-modal.warning-body": {
"message": "Tento server bude odebrán z vašeho seznamu serverů i ze seznamu serverů ve hře. Pokud znáte jeho adresu, můžete ho později znovu přidat."
},
"app.instance.worlds.remove-server-modal.warning-header": {
"message": "Odebírání {name}"
"message": "Žádné servery nebo světy nebyly přidány"
},
"app.instance.worlds.search-worlds-placeholder": {
"message": "Hledat v {count} světech..."
},
"app.modal.install-to-play.content-required": {
"message": "Požadovaný obsah"
},
"app.modal.install-to-play.header": {
"message": "Nainstaluj pro hraní"
},
@@ -371,35 +269,38 @@
"app.modal.install-to-play.mod-count": {
"message": "{count, plural, one {# mód} few {# módy} other {# módů}}"
},
"app.modal.install-to-play.required-modpack": {
"message": "Požadovaný 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."
},
"app.modal.install-to-play.shared-instance": {
"message": "Sdílená instance"
},
"app.modal.install-to-play.shared-server-instance": {
"message": "Sdílená serverová instance"
},
"app.modal.install-to-play.view-contents": {
"message": "Zobrazit obsah"
},
"app.modal.update-to-play.header": {
"message": "Aktualizuj pro 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."
},
"app.project.install-button.already-installed": {
"message": "Tento projekt je již nainstalován"
},
"app.project.install-button.switch-version": {
"message": "Změnit verzi"
},
"app.project.install-context.back-to-browse": {
"message": "Zpět k Objevování"
},
"app.project.version.all-versions": {
"message": "Všechny verze"
},
"app.project.version.download-in-browser": {
"message": "Stáhnout v prohlížeči"
},
"app.project.versions.already-installed": {
"message": "Již staženo"
"app.project.install-context.install-content-to-instance": {
"message": "Nainstalovat obsah do instance"
},
"app.settings.developer-mode-enabled": {
"message": "Vývojářský režim povolen."
@@ -438,7 +339,7 @@
"message": "Smazat skin"
},
"app.skins.delete-modal.description": {
"message": "Tímto dojde k trvalému smazání vybreného skinu. Tuto akci nelze vrátit zpět."
"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?"
@@ -521,9 +422,6 @@
"app.skins.section.builders-and-biomes": {
"message": "Builders & Biomes"
},
"app.skins.section.chaos-cubed": {
"message": "Chaos Cubed"
},
"app.skins.section.chase-the-skies": {
"message": "Chase the Skies"
},
@@ -570,19 +468,19 @@
"message": "Nadšený Modrinth Bot"
},
"app.skins.sign-in.title": {
"message": "Přihlaš se prosím"
"message": "Přihlas se prosím"
},
"app.skins.title": {
"message": "Výběr skinu"
},
"app.update-popup.body.download-complete": {
"message": "Stahování Modrinth App v{version} bylo dokončeno. Aktualizujte ji znovunačtením nebo se automaticky aktualizuje po zavření."
"message": "Stahování aplikace Modrinth v{version} bylo dokončeno. Aktualizujte ji znovunačtením nebo se automaticky aktualizuje po zavření."
},
"app.update-popup.body.linux": {
"message": "Modrinth App 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 ji pomocí svého správce balíčků, abyste získali nejnovější funkce a opravy!"
},
"app.update-popup.body.metered": {
"message": "Modrinth App v{version} je nyní k dispozici! Protože jste v měřené síti, nebyla stažena automaticky."
"message": "Aplikace Modrinth v{version} je nyní k dispozici! Protože jste v měřené síti, nebyla stažena automaticky."
},
"app.update-popup.changelog": {
"message": "Seznam změn"
@@ -795,13 +693,13 @@
"message": "Jméno"
},
"instance.settings.tabs.general.update-channel": {
"message": "Kanál aktualiza"
"message": "Aktualizační kanál"
},
"instance.settings.tabs.general.update-channel.alpha": {
"message": "Alfa"
},
"instance.settings.tabs.general.update-channel.alpha.description": {
"message": "Verze typu release, beta a alpha se budou zobrazovat jako dostupné aktualizace."
"message": "Vydané, beta a alfa verze se zobrazí jako dostupné aktualizace."
},
"instance.settings.tabs.general.update-channel.beta": {
"message": "Beta"
@@ -941,9 +839,6 @@
"instance.worlds.copy_address": {
"message": "Kopírovat adresu"
},
"instance.worlds.create_shortcut": {
"message": "Vytvořit záložku"
},
"instance.worlds.dont_show_on_home": {
"message": "Nezobrazovat na domovské stránce"
},
@@ -1018,5 +913,26 @@
},
"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)."
},
"unknown-pack-warning-modal.dont-show-again": {
"message": "Toto varování znovu nezobrazovat"
},
"unknown-pack-warning-modal.header": {
"message": "Potvrdit instalaci"
},
"unknown-pack-warning-modal.install-anyway": {
"message": "Přesto nainstalovat"
},
"unknown-pack-warning-modal.malware-statement": {
"message": "Malware se často šíří prostřednictvím souborů s modpacky, které se sdílejí na platformách jako Discord."
},
"unknown-pack-warning-modal.warning.body": {
"message": "Tento soubor jsme na Modrinthu nenašli. Důrazně doporučujeme instalovat pouze soubory z důvěryhodných zdrojů."
},
"unknown-pack-warning-modal.warning.title": {
"message": "Upozornění na neznámý soubor"
}
}
+40 -40
View File
@@ -11,18 +11,6 @@
"app.action-bar.hide-more-running-instances": {
"message": "Gem flere kørende instances"
},
"app.action-bar.install.dismiss": {
"message": "Tilsidesæt"
},
"app.action-bar.install.open-instance": {
"message": "Åben instance"
},
"app.action-bar.install.retry": {
"message": "Prøv igen"
},
"app.action-bar.install.unknown-instance": {
"message": "Ukendt instance"
},
"app.action-bar.make-primary-instance": {
"message": "Gør til primære instance"
},
@@ -153,7 +141,7 @@
"message": "Opdag servere"
},
"app.browse.hide-added-servers": {
"message": "Gem servere som allerede er tilføjet"
"message": "Gem allerede tilføjet til servere"
},
"app.browse.project-type.modpacks": {
"message": "Modpacks"
@@ -176,27 +164,24 @@
"app.export-modal.header": {
"message": "Eksporter modpack"
},
"app.export-modal.include-file-accessibility-label": {
"message": "Inkludere \"{file}\"?"
},
"app.export-modal.modpack-name-label": {
"message": "Modpack navn"
"message": "Modpack Navn"
},
"app.export-modal.modpack-name-placeholder": {
"message": "Modpack navn"
},
"app.export-modal.select-files-label": {
"message": "Konfigurer hvilke filer inkludere i denne eksport"
},
"app.export-modal.version-number-label": {
"message": "Versionsnummer"
},
"app.export-modal.version-number-placeholder": {
"message": "1.0.0"
},
"app.install.phase.downloading_content": {
"message": "Downloader indhold"
},
"app.install.phase.downloading_minecraft": {
"message": "Downloader Minecraft"
},
"app.install.phase.preparing_java.downloading": {
"message": "Downloader Java {version}"
},
"app.instance.confirm-delete.admonition-body": {
"message": "Al' data for din instance vil blive permanent slettet, dette inkludere dine verdener, konfigurationer, og alt installeret indhold."
},
@@ -221,9 +206,6 @@
"app.instance.modpack-already-installed.instance": {
"message": "Instance"
},
"app.instance.mods.bulk-update.downloading-projects": {
"message": "Downloader {current, number}/{total, number} projekter..."
},
"app.instance.mods.content-type-project": {
"message": "projekt"
},
@@ -269,6 +251,9 @@
"app.instance.worlds.search-worlds-placeholder": {
"message": "Søg {count} verdener..."
},
"app.modal.install-to-play.content-required": {
"message": "Indhold krævet"
},
"app.modal.install-to-play.header": {
"message": "Installer for at spille"
},
@@ -278,15 +263,27 @@
"app.modal.install-to-play.mod-count": {
"message": "{count, plural, one {# mod} other {# mods}}"
},
"app.modal.install-to-play.required-modpack": {
"message": "Modpack krævet"
},
"app.modal.install-to-play.server-requires-mods": {
"message": "Denne server kræver mods for at spille. Tryk på installer for at sætte de krævet filler fra modrinth op, så lancer direkte til serveren."
},
"app.modal.install-to-play.shared-instance": {
"message": "Delt instance"
},
"app.modal.install-to-play.shared-server-instance": {
"message": "Delt server instance"
},
"app.modal.install-to-play.view-contents": {
"message": "Vis indhold"
},
"app.modal.update-to-play.header": {
"message": "Opdater for at spille"
},
"app.modal.update-to-play.update-required": {
"message": "Opdatering krævet"
},
"app.modal.update-to-play.update-required-description": {
"message": "En opdatering er krævet for at spille {name}. Venligst opdater til den seneste version for at køre spillet."
},
@@ -296,8 +293,8 @@
"app.project.install-context.back-to-browse": {
"message": "Tilbage til udforsk"
},
"app.project.versions.already-installed": {
"message": "Allerede installeret"
"app.project.install-context.install-content-to-instance": {
"message": "Installer indhold til instance"
},
"app.settings.developer-mode-enabled": {
"message": "Udvikler-tilstand aktiveret."
@@ -404,9 +401,6 @@
"app.skins.section.builders-and-biomes": {
"message": "Builders & Biomes"
},
"app.skins.section.chaos-cubed": {
"message": "Chaos Cubed"
},
"app.skins.section.chase-the-skies": {
"message": "Chase the Skies"
},
@@ -476,9 +470,6 @@
"app.update-popup.download-complete": {
"message": "Download færdiggjort"
},
"app.update-popup.reload": {
"message": "Genindlæs for at opdater"
},
"app.update-popup.title": {
"message": "Opdatering tilgængelig"
},
@@ -677,12 +668,6 @@
"instance.settings.tabs.general.name": {
"message": "Navn"
},
"instance.settings.tabs.general.update-channel.alpha": {
"message": "Alpha"
},
"instance.settings.tabs.general.update-channel.beta": {
"message": "Beta"
},
"instance.settings.tabs.hooks": {
"message": "Startkommandoer"
},
@@ -865,5 +850,20 @@
},
"search.filter.locked.server-loader.title": {
"message": "Loader er givet af serveren"
},
"unknown-pack-warning-modal.body": {
"message": "En fil behandles kun, hvis den uploades til Modrinth, uanset filformat (herunder .mrpack)."
},
"unknown-pack-warning-modal.dont-show-again": {
"message": "Vis ikke denne advarsel igen"
},
"unknown-pack-warning-modal.header": {
"message": "Bekræft installation"
},
"unknown-pack-warning-modal.install-anyway": {
"message": "Installer alligevel"
},
"unknown-pack-warning-modal.warning.title": {
"message": "Ukendt fil advarsel"
}
}
+41 -215
View File
@@ -11,87 +11,6 @@
"app.action-bar.hide-more-running-instances": {
"message": "Weitere laufende Instanzen ausblenden"
},
"app.action-bar.install.copied-details": {
"message": "Kopiert"
},
"app.action-bar.install.copy-details": {
"message": "Details kopieren"
},
"app.action-bar.install.dismiss": {
"message": "Ignorieren"
},
"app.action-bar.install.open-instance": {
"message": "Instanz öffnen"
},
"app.action-bar.install.retry": {
"message": "Wiederholen"
},
"app.action-bar.install.summary.app-closing": {
"message": "Aufgrund von Schließung der App abgebrochen"
},
"app.action-bar.install.summary.bad-modpack-file": {
"message": "Modpack konnte nicht gelesen werden"
},
"app.action-bar.install.summary.canceled": {
"message": "Abgebrochen"
},
"app.action-bar.install.summary.cleanup-incomplete": {
"message": "Bereinigung konnte nicht abgeschlossen werden"
},
"app.action-bar.install.summary.content-download-failed": {
"message": "Dateien konnte nicht heruntergeladen werden"
},
"app.action-bar.install.summary.corrupt-download": {
"message": "Heruntergeladene Datei ist beschädigt"
},
"app.action-bar.install.summary.could-not-save-files": {
"message": "Dateien konnten nicht gespeichert werden"
},
"app.action-bar.install.summary.download-failed": {
"message": "Download konnte nicht abgeschlossen werden"
},
"app.action-bar.install.summary.instance-not-found": {
"message": "Instanz konnte nicht gefunden werden"
},
"app.action-bar.install.summary.invalid-file-path": {
"message": "Dateipfad ist ungültig"
},
"app.action-bar.install.summary.invalid-modpack": {
"message": "Daten des Modpacks sind ungültig"
},
"app.action-bar.install.summary.invalid-modpack-files": {
"message": "Dateien des Modpacks enthalten ungültige Metadaten"
},
"app.action-bar.install.summary.java-setup-failed": {
"message": "Die Einrichtung von Java konnte nicht abgeschlossen werden"
},
"app.action-bar.install.summary.loader-setup-failed": {
"message": "Einrichtung des Loaders ist fehlgeschlagen"
},
"app.action-bar.install.summary.local-data-error": {
"message": "Lokale Daten konnten nicht aktualisiert werden"
},
"app.action-bar.install.summary.minecraft-setup-failed": {
"message": "Einrichtung von Minecraft ist fehlgeschlagen"
},
"app.action-bar.install.summary.modrinth-unreachable": {
"message": "Modrinth konnte nicht erreicht werden"
},
"app.action-bar.install.summary.no-write-permission": {
"message": "Keine Berechtigung zum Schreiben"
},
"app.action-bar.install.summary.pack-download-failed": {
"message": "Das Pack konnte nicht heruntergeladen werden"
},
"app.action-bar.install.summary.unexpected-error": {
"message": "Irgendwas ist schiefgelaufen"
},
"app.action-bar.install.unknown-instance": {
"message": "Unbekannte Instanz"
},
"app.action-bar.installs": {
"message": "Installationen"
},
"app.action-bar.make-primary-instance": {
"message": "Zur primären Instanz machen"
},
@@ -182,12 +101,6 @@
"app.appearance-settings.show-play-time.title": {
"message": "Spielzeit anzeigen"
},
"app.appearance-settings.skip-non-essential-warnings.description": {
"message": "Bestätigungen mit geringem Risiko, wie z. B. die Installation doppelter Modpacks, das Löschen normaler Inhalte, Massenaktualisierungen, das Aufheben von Verknüpfungen zu Modpacks und Reparaturaufrufe, werden automatisch übersprungen. Gefährliche Warnungen werden weiterhin angezeigt."
},
"app.appearance-settings.skip-non-essential-warnings.title": {
"message": "Unwichtige Warnungen überspringen"
},
"app.appearance-settings.toggle-sidebar.description": {
"message": "Ermöglicht das Umschalten der Seitenleiste."
},
@@ -257,72 +170,24 @@
"app.export-modal.header": {
"message": "Modpack exportieren"
},
"app.export-modal.include-file-accessibility-label": {
"message": "\"{file}\" einschliessen?"
},
"app.export-modal.modpack-name-label": {
"message": "Name des Modpacks"
"message": "Modpaketname"
},
"app.export-modal.modpack-name-placeholder": {
"message": "Modpaketname"
},
"app.export-modal.select-files-label": {
"message": "Konfiguriere, welche Dateien in diesem Export miteinbezogen werden"
},
"app.export-modal.version-number-label": {
"message": "Versionsnummer"
},
"app.export-modal.version-number-placeholder": {
"message": "1.0.0"
},
"app.install.phase.downloading_content": {
"message": "Inhalte werden heruntergeladen"
},
"app.install.phase.downloading_minecraft": {
"message": "Minecraft wird heruntergeladen"
},
"app.install.phase.downloading_pack_file": {
"message": "Paketdatei wird herunterladen"
},
"app.install.phase.extracting_overrides": {
"message": "Überschreibungen werden extrahiert"
},
"app.install.phase.finalizing": {
"message": "Wird finalisiert"
},
"app.install.phase.preparing_instance": {
"message": "Installation in Warteschlange"
},
"app.install.phase.preparing_java": {
"message": "Java wird vorbereitet"
},
"app.install.phase.preparing_java.downloading": {
"message": "Java {version} wird heruntergeladen"
},
"app.install.phase.preparing_java.extracting": {
"message": "Java {version} wird extrahiert"
},
"app.install.phase.preparing_java.fetching-metadata": {
"message": "Java {version} wird abgerufen"
},
"app.install.phase.preparing_java.resolving": {
"message": "Java {version} wird vorbereitet"
},
"app.install.phase.preparing_java.validating": {
"message": "Java {version} wird validiert"
},
"app.install.phase.reading_pack_manifest": {
"message": "Paketmanifest wird gelesen"
},
"app.install.phase.resolving_loader": {
"message": "Loader wird ermittelt"
},
"app.install.phase.resolving_minecraft": {
"message": "Minecraft wird ermittelt"
},
"app.install.phase.resolving_pack": {
"message": "Inhalte werden ermittelt"
},
"app.install.phase.rolling_back": {
"message": "Wird rückgängig gemacht"
},
"app.install.phase.running_loader_processors": {
"message": "Loader-Prozesse werden ausgeführt"
},
"app.instance.confirm-delete.admonition-body": {
"message": "Alle Daten deiner Instanz werden permanent gelöscht, inlusive deiner Welten, Konfigurationen und allen installierten Inhalten."
},
@@ -347,15 +212,6 @@
"app.instance.modpack-already-installed.instance": {
"message": "Instanz"
},
"app.instance.mods.bulk-update.downloading-projects": {
"message": "{current, number}/{total, number} Projekte werden heruntergeladen..."
},
"app.instance.mods.bulk-update.finishing": {
"message": "Update wird fertiggestellt..."
},
"app.instance.mods.bulk-update.resolving-versions": {
"message": "Versionen werden ermittelt..."
},
"app.instance.mods.content-type-project": {
"message": "Projekt"
},
@@ -380,18 +236,6 @@
"app.instance.worlds.browse-servers": {
"message": "Server durchsuchen"
},
"app.instance.worlds.delete-world-modal.delete-button": {
"message": "Welt löschen"
},
"app.instance.worlds.delete-world-modal.title": {
"message": "Welt löschen"
},
"app.instance.worlds.delete-world-modal.warning-body": {
"message": "Diese Welt wird permanent von dieser Instanz gelöscht. Diese Aktion kann nicht rückgängig gemacht werden."
},
"app.instance.worlds.delete-world-modal.warning-header": {
"message": "Lösche {name}"
},
"app.instance.worlds.filter-modded": {
"message": "Modifiziert"
},
@@ -410,53 +254,32 @@
"app.instance.worlds.no-worlds-heading": {
"message": "Keine Server oder Welten hinzugefügt"
},
"app.instance.worlds.remove-server-modal.remove-button": {
"message": "Server entfernen"
},
"app.instance.worlds.remove-server-modal.title": {
"message": "Server entfernen"
},
"app.instance.worlds.remove-server-modal.warning-body": {
"message": "Dieser Server wird aus deiner Serverliste und aus der Serverliste im Spiel entfernt. Du kannst ihn später wieder hinzufügen, wenn du die Adresse kennst."
},
"app.instance.worlds.remove-server-modal.warning-header": {
"message": "Entferne {name}"
},
"app.instance.worlds.search-worlds-placeholder": {
"message": "Durchsuche {count} Welten..."
},
"app.modal.install-to-play.dont-install": {
"message": "Nicht Installieren"
"app.modal.install-to-play.content-required": {
"message": "Inhalte benötigt"
},
"app.modal.install-to-play.header": {
"message": "Installieren zum Spielen"
},
"app.modal.install-to-play.install-anyway": {
"message": "Trotzdem Installieren"
},
"app.modal.install-to-play.install-button": {
"message": "Installieren"
},
"app.modal.install-to-play.invite-warning": {
"message": "Diese Einladung wurde von einem anderen Modrinth-Nutzer erstellt, nicht von Modrinth. Nimm nur Einladungen von Personen an, denen du vertraust."
},
"app.modal.install-to-play.mod-count": {
"message": "{count, plural, one {# Mod} other {# Mods}}"
},
"app.modal.install-to-play.reviewed-files": {
"message": "Eine Datei wird nur geprüft, wenn sie auf Modrinth veröffentlicht wird, unabhängig von ihrem Dateiformat (auch .mrpack)."
"app.modal.install-to-play.required-modpack": {
"message": "Benötigtes Modpack"
},
"app.modal.install-to-play.server-requires-mods": {
"message": "Dieser Server benötigt Mods zum spielen. Klicke auf Installieren um die nötigen Dateien von Modrinth herunterzuladen und dannach direkt dem Server beizutreten."
},
"app.modal.install-to-play.shared-instance": {
"message": "Geteilte Instanz"
},
"app.modal.install-to-play.unknown-files-description": {
"message": "Dieses Server-Modpack enthält Dateien, die nicht auf Modrinth veröffentlicht sind. Wir empfehlen dringend, nur Dateien aus vertrauenswürdigen Quellen zu installieren."
},
"app.modal.install-to-play.unknown-files-warning": {
"message": "Warnung vor unbekannten Dateien"
},
"app.modal.install-to-play.unrecognized-files": {
"message": "Nicht erkannte Dateien"
"app.modal.install-to-play.shared-server-instance": {
"message": "Geteilte Server Instanz"
},
"app.modal.install-to-play.view-contents": {
"message": "Inhalte ansehen"
@@ -464,8 +287,8 @@
"app.modal.update-to-play.header": {
"message": "Aktualisieren zum Spielen"
},
"app.modal.update-to-play.removed": {
"message": "Entfernt"
"app.modal.update-to-play.update-required": {
"message": "Aktualisierung benötigt"
},
"app.modal.update-to-play.update-required-description": {
"message": "Eine aktualisierung zum spielen von {name} ist benötigt. Bitte aktualisiere auf die neuste Version um das Spiel zu starten."
@@ -473,23 +296,11 @@
"app.project.install-button.already-installed": {
"message": "Dieses Projekt ist bereits installiert"
},
"app.project.install-button.switch-version": {
"message": "Version wechseln"
},
"app.project.install-context.back-to-browse": {
"message": "Zurück zu Entdecken"
},
"app.project.version.all-versions": {
"message": "Alle Versionen"
},
"app.project.version.download-in-browser": {
"message": "Im Browser herunterladen"
},
"app.project.version.installing": {
"message": "Wird installiert"
},
"app.project.versions.already-installed": {
"message": "Bereits installiert"
"app.project.install-context.install-content-to-instance": {
"message": "Inhalt in Instanz installieren"
},
"app.settings.developer-mode-enabled": {
"message": "Entwicklermodus aktiviert."
@@ -611,9 +422,6 @@
"app.skins.section.builders-and-biomes": {
"message": "Builders & Biomes"
},
"app.skins.section.chaos-cubed": {
"message": "Chaos Cubed"
},
"app.skins.section.chase-the-skies": {
"message": "Chase the Skies"
},
@@ -1031,9 +839,6 @@
"instance.worlds.copy_address": {
"message": "Adresse kopieren"
},
"instance.worlds.create_shortcut": {
"message": "Shortcut erstellen"
},
"instance.worlds.dont_show_on_home": {
"message": "Nicht auf Startseite anzeigen"
},
@@ -1108,5 +913,26 @@
},
"search.filter.locked.server-loader.title": {
"message": "Loader wird vom Server bereitgestellt"
},
"unknown-pack-warning-modal.body": {
"message": "Eine Datei wird nur geprüft, wenn sie auf Modrinth hochgeladen wird, unabhängig von ihrem Dateiformat (auch .mrpack)."
},
"unknown-pack-warning-modal.dont-show-again": {
"message": "Diese Warnung nicht mehr anzeigen"
},
"unknown-pack-warning-modal.header": {
"message": "Installation bestätigen"
},
"unknown-pack-warning-modal.install-anyway": {
"message": "Trotzdem installieren"
},
"unknown-pack-warning-modal.malware-statement": {
"message": "Schadsoftware wird häufig über Modpack-Dateien verbreitet, indem diese auf Plattformen wie Discord geteilt werden."
},
"unknown-pack-warning-modal.warning.body": {
"message": "Wir konnten diese Datei auf Modrinth nicht finden. Wir empfehlen dringend, nur Dateien aus vertrauenswürdigen Quellen zu installieren."
},
"unknown-pack-warning-modal.warning.title": {
"message": "Warnung vor unbekannter Datei"
}
}
+45 -219
View File
@@ -11,87 +11,6 @@
"app.action-bar.hide-more-running-instances": {
"message": "Weitere laufende Instanzen ausblenden"
},
"app.action-bar.install.copied-details": {
"message": "Kopiert"
},
"app.action-bar.install.copy-details": {
"message": "Details kopieren"
},
"app.action-bar.install.dismiss": {
"message": "Ausblenden"
},
"app.action-bar.install.open-instance": {
"message": "Instanz öffnen"
},
"app.action-bar.install.retry": {
"message": "Erneut versuchen"
},
"app.action-bar.install.summary.app-closing": {
"message": "Aufgrund von Schließung der App abgebrochen"
},
"app.action-bar.install.summary.bad-modpack-file": {
"message": "Modpack konnte nicht gelesen werden"
},
"app.action-bar.install.summary.canceled": {
"message": "Abgebrochen"
},
"app.action-bar.install.summary.cleanup-incomplete": {
"message": "Bereinigung konnte nicht abgeschlossen werden"
},
"app.action-bar.install.summary.content-download-failed": {
"message": "Dateien konnte nicht heruntergeladen werden"
},
"app.action-bar.install.summary.corrupt-download": {
"message": "Heruntergeladene Datei ist beschädigt"
},
"app.action-bar.install.summary.could-not-save-files": {
"message": "Dateien konnten nicht gespeichert werden"
},
"app.action-bar.install.summary.download-failed": {
"message": "Download konnte nicht abgeschlossen werden"
},
"app.action-bar.install.summary.instance-not-found": {
"message": "Instanz konnte nicht gefunden werden"
},
"app.action-bar.install.summary.invalid-file-path": {
"message": "Dateipfad ist ungültig"
},
"app.action-bar.install.summary.invalid-modpack": {
"message": "Daten des Modpacks sind ungültig"
},
"app.action-bar.install.summary.invalid-modpack-files": {
"message": "Dateien des Modpacks enthalten ungültige Metadaten"
},
"app.action-bar.install.summary.java-setup-failed": {
"message": "Die Einrichtung von Java konnte nicht abgeschlossen werden"
},
"app.action-bar.install.summary.loader-setup-failed": {
"message": "Einrichtung des Loaders ist fehlgeschlagen"
},
"app.action-bar.install.summary.local-data-error": {
"message": "Lokale Daten konnten nicht aktualisiert werden"
},
"app.action-bar.install.summary.minecraft-setup-failed": {
"message": "Einrichtung von Minecraft ist fehlgeschlagen"
},
"app.action-bar.install.summary.modrinth-unreachable": {
"message": "Modrinth konnte nicht erreicht werden"
},
"app.action-bar.install.summary.no-write-permission": {
"message": "Keine Berechtigung zum Schreiben"
},
"app.action-bar.install.summary.pack-download-failed": {
"message": "Das Pack konnte nicht heruntergeladen werden"
},
"app.action-bar.install.summary.unexpected-error": {
"message": "Irgendwas ist schiefgelaufen"
},
"app.action-bar.install.unknown-instance": {
"message": "Unbekannte Instanz"
},
"app.action-bar.installs": {
"message": "Installationen"
},
"app.action-bar.make-primary-instance": {
"message": "Zur primären Instanz machen"
},
@@ -168,7 +87,7 @@
"message": "Launcher minimieren"
},
"app.appearance-settings.native-decorations.description": {
"message": "Fensterrahmen des Systems verwenden (Neustart der App erforderlich)."
"message": "Fensterdekorationen des Systems verwenden (Neustart der App erforderlich)."
},
"app.appearance-settings.native-decorations.title": {
"message": "Native Dekorationen"
@@ -182,12 +101,6 @@
"app.appearance-settings.show-play-time.title": {
"message": "Spielzeit anzeigen"
},
"app.appearance-settings.skip-non-essential-warnings.description": {
"message": "Bestätigungen mit geringem Risiko, wie z. B. die Installation doppelter Modpacks, das Löschen normaler Inhalte, Massenaktualisierungen, das Aufheben von Verknüpfungen zu Modpacks und Reparaturaufrufe, werden automatisch übersprungen. Gefährliche Warnungen werden weiterhin angezeigt."
},
"app.appearance-settings.skip-non-essential-warnings.title": {
"message": "Unwichtige Warnungen überspringen"
},
"app.appearance-settings.toggle-sidebar.description": {
"message": "Ermöglicht das Umschalten der Seitenleiste."
},
@@ -257,11 +170,17 @@
"app.export-modal.header": {
"message": "Modpack exportieren"
},
"app.export-modal.include-file-accessibility-label": {
"message": "\"{file}\" einschließen?"
},
"app.export-modal.modpack-name-label": {
"message": "Name des Modpacks"
"message": "Modpackname"
},
"app.export-modal.modpack-name-placeholder": {
"message": "Name des Modpacks"
"message": "Modpackname"
},
"app.export-modal.select-files-label": {
"message": "Konfiguriere, welche Dateien in diesen Export enthalten sind"
},
"app.export-modal.version-number-label": {
"message": "Versionsnummer"
@@ -269,60 +188,6 @@
"app.export-modal.version-number-placeholder": {
"message": "1.0.0"
},
"app.install.phase.downloading_content": {
"message": "Inhalte werden heruntergeladen"
},
"app.install.phase.downloading_minecraft": {
"message": "Minecraft wird heruntergeladen"
},
"app.install.phase.downloading_pack_file": {
"message": "Paketdatei wird herunterladen"
},
"app.install.phase.extracting_overrides": {
"message": "Überschreibungen werden extrahiert"
},
"app.install.phase.finalizing": {
"message": "Wird finalisiert"
},
"app.install.phase.preparing_instance": {
"message": "Installation in Warteschlange"
},
"app.install.phase.preparing_java": {
"message": "Java wird vorbereitet"
},
"app.install.phase.preparing_java.downloading": {
"message": "Java {version} wird heruntergeladen"
},
"app.install.phase.preparing_java.extracting": {
"message": "Java {version} wird extrahiert"
},
"app.install.phase.preparing_java.fetching-metadata": {
"message": "Java {version} wird abgerufen"
},
"app.install.phase.preparing_java.resolving": {
"message": "Java {version} wird vorbereitet"
},
"app.install.phase.preparing_java.validating": {
"message": "Java {version} wird validiert"
},
"app.install.phase.reading_pack_manifest": {
"message": "Paketmanifest wird gelesen"
},
"app.install.phase.resolving_loader": {
"message": "Loader wird ermittelt"
},
"app.install.phase.resolving_minecraft": {
"message": "Minecraft wird ermittelt"
},
"app.install.phase.resolving_pack": {
"message": "Inhalte werden ermittelt"
},
"app.install.phase.rolling_back": {
"message": "Wird rückgängig gemacht"
},
"app.install.phase.running_loader_processors": {
"message": "Loader-Prozesse werden ausgeführt"
},
"app.instance.confirm-delete.admonition-body": {
"message": "Alle Daten deiner Instanz werden permanent gelöscht, einschließlich deiner Welten, Konfigurationen und allen installierten Inhalten."
},
@@ -347,15 +212,6 @@
"app.instance.modpack-already-installed.instance": {
"message": "Instanz"
},
"app.instance.mods.bulk-update.downloading-projects": {
"message": "{current, number}/{total, number} Projekte werden heruntergelanden..."
},
"app.instance.mods.bulk-update.finishing": {
"message": "Update wird fertiggestellt..."
},
"app.instance.mods.bulk-update.resolving-versions": {
"message": "Versionen werden ermittelt..."
},
"app.instance.mods.content-type-project": {
"message": "Projekt"
},
@@ -380,18 +236,6 @@
"app.instance.worlds.browse-servers": {
"message": "Server durchsuchen"
},
"app.instance.worlds.delete-world-modal.delete-button": {
"message": "Welt löschen"
},
"app.instance.worlds.delete-world-modal.title": {
"message": "Welt löschen"
},
"app.instance.worlds.delete-world-modal.warning-body": {
"message": "Die Welt wird permanent von der Instanz gelöscht. Diese Aktion kann nicht rückgängig gemacht werden."
},
"app.instance.worlds.delete-world-modal.warning-header": {
"message": "{name} wird gelöscht"
},
"app.instance.worlds.filter-modded": {
"message": "Modifiziert"
},
@@ -410,53 +254,32 @@
"app.instance.worlds.no-worlds-heading": {
"message": "Keine Server oder Welten hinzugefügt"
},
"app.instance.worlds.remove-server-modal.remove-button": {
"message": "Server entfernen"
},
"app.instance.worlds.remove-server-modal.title": {
"message": "Server entfernen"
},
"app.instance.worlds.remove-server-modal.warning-body": {
"message": "Dieser Server wird aus deiner Serverliste und aus der Serverliste im Spiel entfernt. Du kannst ihn später wieder hinzufügen, wenn du die Adresse kennst."
},
"app.instance.worlds.remove-server-modal.warning-header": {
"message": "{name} wird entfernt"
},
"app.instance.worlds.search-worlds-placeholder": {
"message": "Durchsuche {count} Welten..."
},
"app.modal.install-to-play.dont-install": {
"message": "Nicht Installieren"
"app.modal.install-to-play.content-required": {
"message": "Inhalte benötigt"
},
"app.modal.install-to-play.header": {
"message": "Installieren zum Spielen"
},
"app.modal.install-to-play.install-anyway": {
"message": "Trotzdem Installieren"
},
"app.modal.install-to-play.install-button": {
"message": "Installieren"
},
"app.modal.install-to-play.invite-warning": {
"message": "Diese Einladung wurde von einem anderen Modrinth-Nutzer erstellt, nicht von Modrinth. Nimm nur Einladungen von Personen an, denen du vertraust."
},
"app.modal.install-to-play.mod-count": {
"message": "{count, plural, one {# Mod} other {# Mods}}"
},
"app.modal.install-to-play.reviewed-files": {
"message": "Eine Datei wird nur geprüft, wenn sie auf Modrinth veröffentlicht wird, unabhängig von ihrem Dateiformat (auch .mrpack)."
"app.modal.install-to-play.required-modpack": {
"message": "Benötigtes Modpack"
},
"app.modal.install-to-play.server-requires-mods": {
"message": "Dieser Server benötigt Mods zum Spielen. Klicke auf Installieren um die nötigen Dateien von Modrinth herunterzuladen und danach direkt dem Server beizutreten."
},
"app.modal.install-to-play.shared-instance": {
"message": "Geteilte Instanz"
},
"app.modal.install-to-play.unknown-files-description": {
"message": "Dieses Server-Modpack enthält Dateien, die nicht auf Modrinth veröffentlicht sind. Wir empfehlen dringend, nur Dateien aus vertrauenswürdigen Quellen zu installieren."
},
"app.modal.install-to-play.unknown-files-warning": {
"message": "Warnung vor unbekannten Dateien"
},
"app.modal.install-to-play.unrecognized-files": {
"message": "Nicht erkannte Dateien"
"app.modal.install-to-play.shared-server-instance": {
"message": "Geteilte Serverinstanz"
},
"app.modal.install-to-play.view-contents": {
"message": "Inhalte ansehen"
@@ -464,8 +287,8 @@
"app.modal.update-to-play.header": {
"message": "Aktualisieren zum Spielen"
},
"app.modal.update-to-play.removed": {
"message": "Entfernt"
"app.modal.update-to-play.update-required": {
"message": "Aktualisierung erforderlich"
},
"app.modal.update-to-play.update-required-description": {
"message": "Zum Spielen von {name} ist eine Aktualisierung erforderlich. Bitte aktualisiere auf die neueste Version, um das Spiel zu starten."
@@ -473,23 +296,11 @@
"app.project.install-button.already-installed": {
"message": "Dieses Projekt ist bereits installiert"
},
"app.project.install-button.switch-version": {
"message": "Version wechseln"
},
"app.project.install-context.back-to-browse": {
"message": "Zurück zum Entdecken"
},
"app.project.version.all-versions": {
"message": "Alle Versionen"
},
"app.project.version.download-in-browser": {
"message": "Im Browser herunterladen"
},
"app.project.version.installing": {
"message": "Wird installiert"
},
"app.project.versions.already-installed": {
"message": "Bereits installiert"
"app.project.install-context.install-content-to-instance": {
"message": "Inhalt in Instanz installieren"
},
"app.settings.developer-mode-enabled": {
"message": "Entwicklermodus aktiviert."
@@ -611,9 +422,6 @@
"app.skins.section.builders-and-biomes": {
"message": "Builders & Biomes"
},
"app.skins.section.chaos-cubed": {
"message": "Chaos Cubed"
},
"app.skins.section.chase-the-skies": {
"message": "Chase the Skies"
},
@@ -666,7 +474,7 @@
"message": "Skin-Auswahl"
},
"app.update-popup.body.download-complete": {
"message": "Modrinth App v{version} wurde heruntergeladen. Jetzt neu laden, um sofort zu aktualisieren, oder beim Schließen der Modrinth-App automatisch aktualisieren."
"message": "Modrinth App v{version} wurde heruntergeladen. Neu laden, um jetzt zu aktualisieren, oder automatisch aktualisieren, wenn du die Modrinth App schließt."
},
"app.update-popup.body.linux": {
"message": "Modrinth App v{version} ist verfügbar. Verwende deinen Paketmanager, um die neuesten Funktionen und Fehlerbehebungen zu installieren!"
@@ -699,7 +507,7 @@
"message": "example.modrinth.gg"
},
"app.world.server-modal.select-an-option": {
"message": "Wähle eine Option"
"message": "Wählen eine Option"
},
"app.world.world-item.incompatible-version": {
"message": "Inkompatible Version {version}"
@@ -1031,9 +839,6 @@
"instance.worlds.copy_address": {
"message": "Adresse kopieren"
},
"instance.worlds.create_shortcut": {
"message": "Verknüpfung erstellen"
},
"instance.worlds.dont_show_on_home": {
"message": "Nicht auf der Startseite anzeigen"
},
@@ -1108,5 +913,26 @@
},
"search.filter.locked.server-loader.title": {
"message": "Loader vom Server vorgegeben"
},
"unknown-pack-warning-modal.body": {
"message": "Eine Datei wird nur geprüft, wenn sie auf Modrinth hochgeladen wird, unabhängig von ihrem Dateiformat (auch .mrpack)."
},
"unknown-pack-warning-modal.dont-show-again": {
"message": "Diese Warnung nicht mehr anzeigen"
},
"unknown-pack-warning-modal.header": {
"message": "Installation bestätigen"
},
"unknown-pack-warning-modal.install-anyway": {
"message": "Trotzdem installieren"
},
"unknown-pack-warning-modal.malware-statement": {
"message": "Schadsoftware wird häufig über Modpack-Dateien verbreitet, indem diese auf Plattformen wie Discord geteilt werden."
},
"unknown-pack-warning-modal.warning.body": {
"message": "Wir konnten diese Datei auf Modrinth nicht finden. Wir empfehlen dringend, nur Dateien aus vertrauenswürdigen Quellen zu installieren."
},
"unknown-pack-warning-modal.warning.title": {
"message": "Warnung vor unbekannter Datei"
}
}
+55 -184
View File
@@ -11,81 +11,27 @@
"app.action-bar.hide-more-running-instances": {
"message": "Hide more running instances"
},
"app.action-bar.install.copied-details": {
"message": "Copied"
},
"app.action-bar.install.copy-details": {
"message": "Copy details"
},
"app.action-bar.install.dismiss": {
"message": "Dismiss"
},
"app.action-bar.install.failed": {
"message": "Install failed"
},
"app.action-bar.install.failed-app-closed": {
"message": "Installation failed due to app closing."
},
"app.action-bar.install.failed-network": {
"message": "Installation failed due to a network error."
},
"app.action-bar.install.failed-unknown": {
"message": "Installation failed due to an unknown error."
},
"app.action-bar.install.open-instance": {
"message": "Open instance"
},
"app.action-bar.install.retry": {
"message": "Retry"
},
"app.action-bar.install.summary.app-closing": {
"message": "Canceled due to app closing"
},
"app.action-bar.install.summary.bad-modpack-file": {
"message": "Couldn't read modpack"
},
"app.action-bar.install.summary.canceled": {
"message": "Canceled"
},
"app.action-bar.install.summary.cleanup-incomplete": {
"message": "Cleanup didn't finish"
},
"app.action-bar.install.summary.content-download-failed": {
"message": "Couldn't download files"
},
"app.action-bar.install.summary.corrupt-download": {
"message": "Downloaded file is corrupt"
},
"app.action-bar.install.summary.could-not-save-files": {
"message": "Couldn't save files"
},
"app.action-bar.install.summary.download-failed": {
"message": "Download couldn't finish"
},
"app.action-bar.install.summary.instance-not-found": {
"message": "Instance couldn't be found"
},
"app.action-bar.install.summary.invalid-file-path": {
"message": "File path is invalid"
},
"app.action-bar.install.summary.invalid-modpack": {
"message": "Modpack data invalid"
},
"app.action-bar.install.summary.invalid-modpack-files": {
"message": "Modpack files have invalid metadata"
},
"app.action-bar.install.summary.java-setup-failed": {
"message": "Java setup couldn't finish"
},
"app.action-bar.install.summary.loader-setup-failed": {
"message": "Loader setup failed"
},
"app.action-bar.install.summary.local-data-error": {
"message": "Couldn't update local data"
},
"app.action-bar.install.summary.minecraft-setup-failed": {
"message": "Minecraft setup failed"
},
"app.action-bar.install.summary.modrinth-unreachable": {
"message": "Couldn't reach Modrinth"
},
"app.action-bar.install.summary.no-write-permission": {
"message": "No permission to write"
},
"app.action-bar.install.summary.pack-download-failed": {
"message": "Couldn't download pack"
},
"app.action-bar.install.summary.unexpected-error": {
"message": "Something went wrong"
},
"app.action-bar.install.unknown-instance": {
"message": "Unknown instance"
},
@@ -125,21 +71,6 @@
"app.action-bar.view-logs": {
"message": "View logs"
},
"app.ads-consent.accept": {
"message": "Accept all"
},
"app.ads-consent.body": {
"message": "Ads make Modrinth possible and fund creator payouts. Our partners may store or access cookies in the app to personalize ads and measure performance."
},
"app.ads-consent.manage": {
"message": "Manage preferences"
},
"app.ads-consent.reject": {
"message": "Reject all"
},
"app.ads-consent.title": {
"message": "Your privacy and how ads support Modrinth"
},
"app.appearance-settings.advanced-rendering.description": {
"message": "Enables advanced rendering such as blur effects that may cause performance issues without hardware-accelerated rendering."
},
@@ -249,7 +180,7 @@
"message": "Discover servers"
},
"app.browse.hide-added-servers": {
"message": "Hide servers already added"
"message": "Hide already added servers"
},
"app.browse.project-type.modpacks": {
"message": "Modpacks"
@@ -272,12 +203,18 @@
"app.export-modal.header": {
"message": "Export modpack"
},
"app.export-modal.include-file-accessibility-label": {
"message": "Include \"{file}\"?"
},
"app.export-modal.modpack-name-label": {
"message": "Modpack name"
"message": "Modpack Name"
},
"app.export-modal.modpack-name-placeholder": {
"message": "Modpack name"
},
"app.export-modal.select-files-label": {
"message": "Configure which files are included in this export"
},
"app.export-modal.version-number-label": {
"message": "Version number"
},
@@ -300,7 +237,7 @@
"message": "Finalizing"
},
"app.install.phase.preparing_instance": {
"message": "Queued to install"
"message": "Preparing instance"
},
"app.install.phase.preparing_java": {
"message": "Preparing Java"
@@ -440,38 +377,29 @@
"app.instance.worlds.search-worlds-placeholder": {
"message": "Search {count} worlds..."
},
"app.modal.install-to-play.dont-install": {
"message": "Dont install"
"app.modal.install-to-play.content-required": {
"message": "Content required"
},
"app.modal.install-to-play.header": {
"message": "Install to play"
},
"app.modal.install-to-play.install-anyway": {
"message": "Install anyway"
},
"app.modal.install-to-play.install-button": {
"message": "Install"
},
"app.modal.install-to-play.invite-warning": {
"message": "This invite was created by another Modrinth user, not Modrinth. Only accept invites from people you trust."
},
"app.modal.install-to-play.mod-count": {
"message": "{count, plural, one {# mod} other {# mods}}"
},
"app.modal.install-to-play.reviewed-files": {
"message": "A file is only reviewed if its published to Modrinth, regardless of its file format (including .mrpack)."
"app.modal.install-to-play.required-modpack": {
"message": "Required modpack"
},
"app.modal.install-to-play.server-requires-mods": {
"message": "This server requires mods to play. Click Install to set up the required files from Modrinth, then launch directly into the server."
},
"app.modal.install-to-play.shared-instance": {
"message": "Shared instance"
},
"app.modal.install-to-play.unknown-files-description": {
"message": "This server modpack contains files that arent published on Modrinth. We strongly recommend only installing files from sources you trust."
},
"app.modal.install-to-play.unknown-files-warning": {
"message": "Unknown files warning"
},
"app.modal.install-to-play.unrecognized-files": {
"message": "Unrecognized files"
"app.modal.install-to-play.shared-server-instance": {
"message": "Shared server instance"
},
"app.modal.install-to-play.view-contents": {
"message": "View contents"
@@ -479,8 +407,8 @@
"app.modal.update-to-play.header": {
"message": "Update to play"
},
"app.modal.update-to-play.removed": {
"message": "Removed"
"app.modal.update-to-play.update-required": {
"message": "Update required"
},
"app.modal.update-to-play.update-required-description": {
"message": "An update is required to play {name}. Please update to the latest version to launch the game."
@@ -488,23 +416,11 @@
"app.project.install-button.already-installed": {
"message": "This project is already installed"
},
"app.project.install-button.switch-version": {
"message": "Switch version"
},
"app.project.install-context.back-to-browse": {
"message": "Back to discover"
},
"app.project.version.all-versions": {
"message": "All versions"
},
"app.project.version.download-in-browser": {
"message": "Download in browser"
},
"app.project.version.installing": {
"message": "Installing"
},
"app.project.versions.already-installed": {
"message": "Already installed"
"app.project.install-context.install-content-to-instance": {
"message": "Install content to instance"
},
"app.settings.developer-mode-enabled": {
"message": "Developer mode enabled."
@@ -512,9 +428,6 @@
"app.settings.downloading": {
"message": "Downloading v{version}"
},
"app.settings.privacy.ads-consent.intro": {
"message": "Ads make Modrinth possible and fund creator payouts. Our partners may store or access cookies in the app to personalize ads and measure performance. You can opt out or manage your preferences below."
},
"app.settings.tabs.appearance": {
"message": "Appearance"
},
@@ -557,9 +470,6 @@
"app.skins.dropped-file-error.title": {
"message": "Error processing file"
},
"app.skins.ears-feature-notice": {
"message": "This skin uses features from the {ears} mod"
},
"app.skins.edit-button": {
"message": "Edit skin"
},
@@ -632,9 +542,6 @@
"app.skins.section.builders-and-biomes": {
"message": "Builders & Biomes"
},
"app.skins.section.chaos-cubed": {
"message": "Chaos Cubed"
},
"app.skins.section.chase-the-skies": {
"message": "Chase the Skies"
},
@@ -686,12 +593,6 @@
"app.skins.title": {
"message": "Skin selector"
},
"app.skins.toggle-ears-features-off": {
"message": "Toggle off"
},
"app.skins.toggle-ears-features-on": {
"message": "Toggle on"
},
"app.update-popup.body.download-complete": {
"message": "Modrinth App v{version} has finished downloading. Reload to update now, or automatically when you close Modrinth App."
},
@@ -803,33 +704,6 @@
"friends.sign-in-to-add-friends": {
"message": "<link>Sign in to a Modrinth account</link> to add friends and see what they're playing!"
},
"instance.action.create-shortcut": {
"message": "Create shortcut"
},
"instance.action.export-modpack": {
"message": "Export modpack"
},
"instance.action.launch-instance": {
"message": "Launch instance"
},
"instance.action.more-actions": {
"message": "More actions"
},
"instance.action.open-folder": {
"message": "Open folder"
},
"instance.action.repair": {
"message": "Repair"
},
"instance.action.settings": {
"message": "Instance settings"
},
"instance.action.starting": {
"message": "Starting..."
},
"instance.action.stopping": {
"message": "Stopping..."
},
"instance.add-server.add-and-play": {
"message": "Add and play"
},
@@ -872,9 +746,6 @@
"instance.files.save-as": {
"message": "Save as..."
},
"instance.playtime.never-played": {
"message": "Never played"
},
"instance.server-modal.address": {
"message": "Address"
},
@@ -1142,27 +1013,6 @@
"minecraft-account.sign-in": {
"message": "Sign in to Minecraft"
},
"minecraft-required.description": {
"message": "You need a Microsoft account that owns Minecraft before you can launch and play."
},
"minecraft-required.description-header": {
"message": "Sign in to a Microsoft account"
},
"minecraft-required.dont-have-account": {
"message": "Dont have an account?"
},
"minecraft-required.get-minecraft": {
"message": "Get Minecraft"
},
"minecraft-required.get-support": {
"message": "Get support"
},
"minecraft-required.header": {
"message": "Minecraft required"
},
"minecraft-required.sign-in": {
"message": "Sign in to Microsoft"
},
"search.filter.locked.instance": {
"message": "Provided by the instance"
},
@@ -1186,5 +1036,26 @@
},
"search.filter.locked.server-loader.title": {
"message": "Loader is provided by the server"
},
"unknown-pack-warning-modal.body": {
"message": "A file is only reviewed if its uploaded to Modrinth, regardless of its file format (including .mrpack)."
},
"unknown-pack-warning-modal.dont-show-again": {
"message": "Don't show this warning again"
},
"unknown-pack-warning-modal.header": {
"message": "Confirm installation"
},
"unknown-pack-warning-modal.install-anyway": {
"message": "Install anyway"
},
"unknown-pack-warning-modal.malware-statement": {
"message": "Malware is often distributed through modpack files by sharing them on platforms like Discord."
},
"unknown-pack-warning-modal.warning.body": {
"message": "We couldn't find this file on Modrinth. We strongly recommend only installing files from sources you trust."
},
"unknown-pack-warning-modal.warning.title": {
"message": "Unknown file warning"
}
}
+41 -215
View File
@@ -11,87 +11,6 @@
"app.action-bar.hide-more-running-instances": {
"message": "Ocultar más instancias en ejecución"
},
"app.action-bar.install.copied-details": {
"message": "Copiado"
},
"app.action-bar.install.copy-details": {
"message": "Copiar detalles"
},
"app.action-bar.install.dismiss": {
"message": "Descartar"
},
"app.action-bar.install.open-instance": {
"message": "Abrir instancia"
},
"app.action-bar.install.retry": {
"message": "Reintentar"
},
"app.action-bar.install.summary.app-closing": {
"message": "Cancelado debido al cierre de la aplicación"
},
"app.action-bar.install.summary.bad-modpack-file": {
"message": "No se pudo leer el modpack"
},
"app.action-bar.install.summary.canceled": {
"message": "Cancelado"
},
"app.action-bar.install.summary.cleanup-incomplete": {
"message": "La limpieza no se completó"
},
"app.action-bar.install.summary.content-download-failed": {
"message": "No se pudieron descargar los archivos"
},
"app.action-bar.install.summary.corrupt-download": {
"message": "El archivo descargado está dañado"
},
"app.action-bar.install.summary.could-not-save-files": {
"message": "No se pudieron guardar los archivos"
},
"app.action-bar.install.summary.download-failed": {
"message": "No se pudo completar la descarga"
},
"app.action-bar.install.summary.instance-not-found": {
"message": "No se pudo encontrar la instancia"
},
"app.action-bar.install.summary.invalid-file-path": {
"message": "La ruta del archivo no es válida"
},
"app.action-bar.install.summary.invalid-modpack": {
"message": "Los datos del modpack no son válidos"
},
"app.action-bar.install.summary.invalid-modpack-files": {
"message": "Los archivos del modpack tienen metadatos no válidos"
},
"app.action-bar.install.summary.java-setup-failed": {
"message": "No se pudo completar la configuración de Java"
},
"app.action-bar.install.summary.loader-setup-failed": {
"message": "No se pudo configurar el cargador"
},
"app.action-bar.install.summary.local-data-error": {
"message": "No se pudieron actualizar los datos locales"
},
"app.action-bar.install.summary.minecraft-setup-failed": {
"message": "La instalación de Minecraft falló"
},
"app.action-bar.install.summary.modrinth-unreachable": {
"message": "No pude comunicarme con Modrinth"
},
"app.action-bar.install.summary.no-write-permission": {
"message": "No tiene permiso para escribir"
},
"app.action-bar.install.summary.pack-download-failed": {
"message": "No se pudo descargar el modpack"
},
"app.action-bar.install.summary.unexpected-error": {
"message": "Algo salió mal"
},
"app.action-bar.install.unknown-instance": {
"message": "Instancia desconocida"
},
"app.action-bar.installs": {
"message": "Instalaciones"
},
"app.action-bar.make-primary-instance": {
"message": "Hacer instancia principal"
},
@@ -182,12 +101,6 @@
"app.appearance-settings.show-play-time.title": {
"message": "Mostrar tiempo de juego"
},
"app.appearance-settings.skip-non-essential-warnings.description": {
"message": "Omite automáticamente las confirmaciones de bajo riesgo, como las instalaciones duplicadas de paquetes de mods, la eliminación de contenido normal, las actualizaciones masivas, la desvinculación de paquetes de mods y las solicitudes de reparación. Las advertencias peligrosas seguirán mostrándose."
},
"app.appearance-settings.skip-non-essential-warnings.title": {
"message": "Omitir advertencias que no sean esenciales"
},
"app.appearance-settings.toggle-sidebar.description": {
"message": "Permite mostrar u ocultar la barra lateral."
},
@@ -234,7 +147,7 @@
"message": "Descubrir servidores"
},
"app.browse.hide-added-servers": {
"message": "Ocultar los servidores ya agregados"
"message": "Ocultar servidores ya añadidos"
},
"app.browse.project-type.modpacks": {
"message": "Modpacks"
@@ -257,72 +170,24 @@
"app.export-modal.header": {
"message": "Exportar modpack"
},
"app.export-modal.include-file-accessibility-label": {
"message": "¿Incluir \"{file}\"?"
},
"app.export-modal.modpack-name-label": {
"message": "Nombre del modpack"
},
"app.export-modal.modpack-name-placeholder": {
"message": "Nombre del modpack"
},
"app.export-modal.select-files-label": {
"message": "Configura que archivos incluir en esta exportación"
},
"app.export-modal.version-number-label": {
"message": "Número de la versión"
},
"app.export-modal.version-number-placeholder": {
"message": "1.0.0"
},
"app.install.phase.downloading_content": {
"message": "Descargando contenido"
},
"app.install.phase.downloading_minecraft": {
"message": "Descargando Minecraft"
},
"app.install.phase.downloading_pack_file": {
"message": "Descargando el archivo del paquete"
},
"app.install.phase.extracting_overrides": {
"message": "Extracción de anulaciones"
},
"app.install.phase.finalizing": {
"message": "Finalizando"
},
"app.install.phase.preparing_instance": {
"message": "En cola para instalarse"
},
"app.install.phase.preparing_java": {
"message": "Preparando Java"
},
"app.install.phase.preparing_java.downloading": {
"message": "Descargando Java {version}"
},
"app.install.phase.preparing_java.extracting": {
"message": "Descomprimiendo Java {version}"
},
"app.install.phase.preparing_java.fetching-metadata": {
"message": "Obteniendo Java {version}"
},
"app.install.phase.preparing_java.resolving": {
"message": "Preparando Java {version}"
},
"app.install.phase.preparing_java.validating": {
"message": "Validación de Java {version}"
},
"app.install.phase.reading_pack_manifest": {
"message": "Leer el manifiesto del paquete"
},
"app.install.phase.resolving_loader": {
"message": "Resolviendo el cargador"
},
"app.install.phase.resolving_minecraft": {
"message": "Resolviendo Minecraft"
},
"app.install.phase.resolving_pack": {
"message": "Resolviendo el contenido"
},
"app.install.phase.rolling_back": {
"message": "Revertir"
},
"app.install.phase.running_loader_processors": {
"message": "Ejecutando los procesadores del cargador"
},
"app.instance.confirm-delete.admonition-body": {
"message": "Todos los datos de tu instancia se eliminarán permanentemente, incluidos tus mundos, configuraciones y todo el contenido instalado."
},
@@ -347,15 +212,6 @@
"app.instance.modpack-already-installed.instance": {
"message": "Instancia"
},
"app.instance.mods.bulk-update.downloading-projects": {
"message": "Descargando {current, number}/{total, number} proyectos..."
},
"app.instance.mods.bulk-update.finishing": {
"message": "Finalizando la actualización..."
},
"app.instance.mods.bulk-update.resolving-versions": {
"message": "Resolviendo versiones..."
},
"app.instance.mods.content-type-project": {
"message": "proyecto"
},
@@ -380,18 +236,6 @@
"app.instance.worlds.browse-servers": {
"message": "Explorar servidores"
},
"app.instance.worlds.delete-world-modal.delete-button": {
"message": "Eliminar mundo"
},
"app.instance.worlds.delete-world-modal.title": {
"message": "Eliminar mundo"
},
"app.instance.worlds.delete-world-modal.warning-body": {
"message": "Este mundo se eliminará de forma permanente de esta instancia. Esta acción no se puede deshacer."
},
"app.instance.worlds.delete-world-modal.warning-header": {
"message": "Eliminando {name}"
},
"app.instance.worlds.filter-modded": {
"message": "Con mods"
},
@@ -410,53 +254,32 @@
"app.instance.worlds.no-worlds-heading": {
"message": "No hay servidores ni mundos añadidos"
},
"app.instance.worlds.remove-server-modal.remove-button": {
"message": "Eliminar servidor"
},
"app.instance.worlds.remove-server-modal.title": {
"message": "Eliminar servidor"
},
"app.instance.worlds.remove-server-modal.warning-body": {
"message": "Este servidor se eliminará de tu lista de servidores y de la lista de servidores del juego. Puedes volver a agregarlo más adelante si conoces la dirección."
},
"app.instance.worlds.remove-server-modal.warning-header": {
"message": "Se está eliminando {name}"
},
"app.instance.worlds.search-worlds-placeholder": {
"message": "Buscar en {count} mundos..."
},
"app.modal.install-to-play.dont-install": {
"message": "No instalar"
"app.modal.install-to-play.content-required": {
"message": "Contenido requerido"
},
"app.modal.install-to-play.header": {
"message": "Instalar para jugar"
},
"app.modal.install-to-play.install-anyway": {
"message": "Instalar de todos modos"
},
"app.modal.install-to-play.install-button": {
"message": "Instalar"
},
"app.modal.install-to-play.invite-warning": {
"message": "Esta invitación la creó otro usuario de Modrinth, no Modrinth. Solo acepta invitaciones de personas en las que confíes."
},
"app.modal.install-to-play.mod-count": {
"message": "{count, plural, one {# mod} other {# mods}}"
},
"app.modal.install-to-play.reviewed-files": {
"message": "Un archivo solo se revisa si se publica en Modrinth, sin importar su formato (incluido el .mrpack)."
"app.modal.install-to-play.required-modpack": {
"message": "Modpack requerido"
},
"app.modal.install-to-play.server-requires-mods": {
"message": "Este servidor requiere mods para poder jugar. Haz clic en Instalar para configurar los archivos requeridos desde Modrinth, después se ejecutará para entrar directamente al servidor."
},
"app.modal.install-to-play.shared-instance": {
"message": "Instancia compartida"
},
"app.modal.install-to-play.unknown-files-description": {
"message": "Este modpack para servidor contiene archivos que no están publicados en Modrinth. Recomendamos encarecidamente instalar únicamente archivos de fuentes de confianza."
},
"app.modal.install-to-play.unknown-files-warning": {
"message": "Advertencia sobre archivos desconocidos"
},
"app.modal.install-to-play.unrecognized-files": {
"message": "Archivos no reconocidos"
"app.modal.install-to-play.shared-server-instance": {
"message": "Instancia de servidor compartida"
},
"app.modal.install-to-play.view-contents": {
"message": "Ver contenidos"
@@ -464,8 +287,8 @@
"app.modal.update-to-play.header": {
"message": "Actualizar para jugar"
},
"app.modal.update-to-play.removed": {
"message": "Eliminado"
"app.modal.update-to-play.update-required": {
"message": "Actualización requerida"
},
"app.modal.update-to-play.update-required-description": {
"message": "Se requiere una actualización para jugar {name}. Por favor, actualiza a la versión más reciente para iniciar el juego."
@@ -473,23 +296,11 @@
"app.project.install-button.already-installed": {
"message": "Este proyecto ya está instalado"
},
"app.project.install-button.switch-version": {
"message": "Cambiar versión"
},
"app.project.install-context.back-to-browse": {
"message": "Volver al explorador"
},
"app.project.version.all-versions": {
"message": "Todas las versiones"
},
"app.project.version.download-in-browser": {
"message": "Descargar en el navegador"
},
"app.project.version.installing": {
"message": "Instalando"
},
"app.project.versions.already-installed": {
"message": "Ya instalado"
"app.project.install-context.install-content-to-instance": {
"message": "Instalar contenido a la instancia"
},
"app.settings.developer-mode-enabled": {
"message": "Modo desarrollador activado."
@@ -611,9 +422,6 @@
"app.skins.section.builders-and-biomes": {
"message": "Constructores y Biomas"
},
"app.skins.section.chaos-cubed": {
"message": "Caos al cubo"
},
"app.skins.section.chase-the-skies": {
"message": "Persigue los Cielos"
},
@@ -1031,9 +839,6 @@
"instance.worlds.copy_address": {
"message": "Copiar dirección"
},
"instance.worlds.create_shortcut": {
"message": "Crear un acceso directo"
},
"instance.worlds.dont_show_on_home": {
"message": "No mostrar en la página de inicio"
},
@@ -1108,5 +913,26 @@
},
"search.filter.locked.server-loader.title": {
"message": "El loader es proporcionado por 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)."
},
"unknown-pack-warning-modal.dont-show-again": {
"message": "No volver a mostrar esta advertencia"
},
"unknown-pack-warning-modal.header": {
"message": "Confirmar instalación"
},
"unknown-pack-warning-modal.install-anyway": {
"message": "Instalar de todos modos"
},
"unknown-pack-warning-modal.malware-statement": {
"message": "El malware a menudo es distribuido mediante modpacks compartidos en aplicaciones como Discord."
},
"unknown-pack-warning-modal.warning.body": {
"message": "No pudimos encontrar este archivo en Modrinth. Recomendamos solo instalar archivos de sitios de confianza."
},
"unknown-pack-warning-modal.warning.title": {
"message": "Advertencia de archivo desconocido"
}
}
+48 -21
View File
@@ -11,21 +11,6 @@
"app.action-bar.hide-more-running-instances": {
"message": "Ocultar más instancias en ejecución"
},
"app.action-bar.install.dismiss": {
"message": "Descartar"
},
"app.action-bar.install.open-instance": {
"message": "Abrir instancia"
},
"app.action-bar.install.retry": {
"message": "Reintentar"
},
"app.action-bar.install.unknown-instance": {
"message": "Instancia desconocida"
},
"app.action-bar.installs": {
"message": "Instalaciones"
},
"app.action-bar.make-primary-instance": {
"message": "Establecer como instancia principal"
},
@@ -116,12 +101,6 @@
"app.appearance-settings.show-play-time.title": {
"message": "Mostrar tiempo de juego"
},
"app.appearance-settings.skip-non-essential-warnings.description": {
"message": "Automáticamente omite confirmaciones de bajo riesgo como modpacks duplicados, eliminación de contenido normal, actualizaciones masivas, desvinculando modpacks, y mensajes de reparación. Advertencias importantes seguirán siendo mostradas."
},
"app.appearance-settings.skip-non-essential-warnings.title": {
"message": "Omitir advertencias no esenciales"
},
"app.appearance-settings.toggle-sidebar.description": {
"message": "Activa la posibilidad de alternar la barra lateral."
},
@@ -167,6 +146,9 @@
"app.browse.discover-servers": {
"message": "Descubrir servidores"
},
"app.browse.hide-added-servers": {
"message": "Ocultar servidores ya añadidos"
},
"app.browse.project-type.modpacks": {
"message": "Modpacks"
},
@@ -188,12 +170,18 @@
"app.export-modal.header": {
"message": "Exportar modpack"
},
"app.export-modal.include-file-accessibility-label": {
"message": "¿Incluir \"{file}\"?"
},
"app.export-modal.modpack-name-label": {
"message": "Nombre del modpack"
},
"app.export-modal.modpack-name-placeholder": {
"message": "Nombre del modpack"
},
"app.export-modal.select-files-label": {
"message": "Configura que archivos se incluirán en esta instancia"
},
"app.export-modal.version-number-label": {
"message": "Número de versión"
},
@@ -269,6 +257,9 @@
"app.instance.worlds.search-worlds-placeholder": {
"message": "Buscar {count} mundos..."
},
"app.modal.install-to-play.content-required": {
"message": "Contenido obligatorio"
},
"app.modal.install-to-play.header": {
"message": "Instala para jugar"
},
@@ -278,15 +269,27 @@
"app.modal.install-to-play.mod-count": {
"message": "{count, plural, one {# mod} other {# mods}}"
},
"app.modal.install-to-play.required-modpack": {
"message": "Modpack requerido"
},
"app.modal.install-to-play.server-requires-mods": {
"message": "Este servidor requiere ciertos mods. Pulsa Instalar para instalar los archivos requeridos de Modrinth y luego el launcher te enviara directo al servidor."
},
"app.modal.install-to-play.shared-instance": {
"message": "Instancia compartida"
},
"app.modal.install-to-play.shared-server-instance": {
"message": "Instancia de servidor compartida"
},
"app.modal.install-to-play.view-contents": {
"message": "Ver contenido"
},
"app.modal.update-to-play.header": {
"message": "Actualiza para jugar"
},
"app.modal.update-to-play.update-required": {
"message": "Actualización requerida"
},
"app.modal.update-to-play.update-required-description": {
"message": "Una actualización es requerida para jugar {name}. Por favor actualízala a la versión más reciente para ejecutar el juego."
},
@@ -296,6 +299,9 @@
"app.project.install-context.back-to-browse": {
"message": "Volver a descubrir"
},
"app.project.install-context.install-content-to-instance": {
"message": "Instalar contenido"
},
"app.settings.developer-mode-enabled": {
"message": "Modo desarrollador activado."
},
@@ -907,5 +913,26 @@
},
"search.filter.locked.server-loader.title": {
"message": "Loader proporcionado por 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)."
},
"unknown-pack-warning-modal.dont-show-again": {
"message": "No mostrar esta advertencia otra vez"
},
"unknown-pack-warning-modal.header": {
"message": "Confirmar instalación"
},
"unknown-pack-warning-modal.install-anyway": {
"message": "Instalar de todos modos"
},
"unknown-pack-warning-modal.malware-statement": {
"message": "El malware es normalmente distribuido por archivos de modpack que son normalmente compartidas en aplicaciones como Discord."
},
"unknown-pack-warning-modal.warning.body": {
"message": "No pudimos encontrar este archivo en Modrinth. Recomendamos solo instalar archivos de sitios de confianza."
},
"unknown-pack-warning-modal.warning.title": {
"message": "Advertencia de archivo desconocido"
}
}
+51 -270
View File
@@ -2,30 +2,12 @@
"app.action-bar.downloading-java": {
"message": "Ladataan Java-versiota {version}"
},
"app.action-bar.downloading-update": {
"message": "Ladataan päivitystä"
},
"app.action-bar.downloads": {
"message": "Lataukset"
},
"app.action-bar.hide-more-running-instances": {
"message": "Piilota enemmän käynnissä olevia instansseja"
},
"app.action-bar.install.dismiss": {
"message": "Hylkää"
},
"app.action-bar.install.open-instance": {
"message": "Avaa instanssi"
},
"app.action-bar.install.retry": {
"message": "Yritä uudelleen"
},
"app.action-bar.install.unknown-instance": {
"message": "Tuntematon instanssi"
},
"app.action-bar.installs": {
"message": "Asennukset"
},
"app.action-bar.make-primary-instance": {
"message": "Aseta ensisijaiseksi instanssiksi"
},
@@ -38,18 +20,12 @@
"app.action-bar.primary-instance": {
"message": "Ensisijainen instanssi"
},
"app.action-bar.reload-to-update": {
"message": "Lataa uudelleen päivittääksesi"
},
"app.action-bar.show-more-running-instances": {
"message": "Näytä enemmän käynnissä olevia instansseja"
},
"app.action-bar.stop-instance": {
"message": "Pysäytä instanssi"
},
"app.action-bar.update": {
"message": "Päivitä"
},
"app.action-bar.view-active-downloads": {
"message": "Näytä aktiiviset lataukset"
},
@@ -110,18 +86,6 @@
"app.appearance-settings.select-option": {
"message": "Valitse vaihtoehto"
},
"app.appearance-settings.show-play-time.description": {
"message": "Näyttää, kuinka paljon aikaa olet käyttänyt instanssin pelaamiseen."
},
"app.appearance-settings.show-play-time.title": {
"message": "Näyttää peliaika"
},
"app.appearance-settings.skip-non-essential-warnings.description": {
"message": "Ohittaa automaattisesti vähäriskiset vahvistukset, kuten modpackien kaksoisasennukset, tavallisen sisällön poistamisen, massapäivitykset, modpackien linkityksen poistamisen ja korjauskehotteet. Vaaralliset varoitukset näytetään silti."
},
"app.appearance-settings.skip-non-essential-warnings.title": {
"message": "Ohita ei olennaiset varoitukset"
},
"app.appearance-settings.toggle-sidebar.description": {
"message": "Laittaa päälle mahdollisuuden kytkeä sivupalkkia."
},
@@ -167,6 +131,9 @@
"app.browse.discover-servers": {
"message": "Löydä palvelimia"
},
"app.browse.hide-added-servers": {
"message": "Piilota lisätyt palvelimet"
},
"app.browse.project-type.modpacks": {
"message": "Modipaketit"
},
@@ -188,66 +155,24 @@
"app.export-modal.header": {
"message": "Vie modipaketti"
},
"app.export-modal.include-file-accessibility-label": {
"message": "Sisällytä \"{file}\"?"
},
"app.export-modal.modpack-name-label": {
"message": "Modipaketin nimi"
},
"app.export-modal.modpack-name-placeholder": {
"message": "Modipaketin nimi"
},
"app.export-modal.select-files-label": {
"message": "Muuta mitkä tiedostot sisältyvät tähän vientiin"
},
"app.export-modal.version-number-label": {
"message": "Versio numero"
},
"app.export-modal.version-number-placeholder": {
"message": "1.0.0"
},
"app.install.phase.downloading_content": {
"message": "Ladataan sisältöä"
},
"app.install.phase.downloading_minecraft": {
"message": "Ladataan Minecraftia"
},
"app.install.phase.downloading_pack_file": {
"message": "Ladataan paketti tiedostoja"
},
"app.install.phase.extracting_overrides": {
"message": "Puretaan ohituksia"
},
"app.install.phase.finalizing": {
"message": "Viimeistellään"
},
"app.install.phase.preparing_java": {
"message": "Valmistellaan Javaa"
},
"app.install.phase.preparing_java.downloading": {
"message": "Ladataan Java {version}"
},
"app.install.phase.preparing_java.extracting": {
"message": "Puretaan Java {version}"
},
"app.install.phase.preparing_java.fetching-metadata": {
"message": "Haetaan Java {version}"
},
"app.install.phase.preparing_java.resolving": {
"message": "Valmistellaan Java {version}"
},
"app.install.phase.preparing_java.validating": {
"message": "Tarkistetaan Java {version}"
},
"app.install.phase.reading_pack_manifest": {
"message": "Luetaan paketin manifestia"
},
"app.install.phase.resolving_loader": {
"message": "Ratkaistaan lataajaa"
},
"app.install.phase.resolving_minecraft": {
"message": "Ratkaistaan Minecraftia"
},
"app.install.phase.resolving_pack": {
"message": "Ratkaistaan sisältöä"
},
"app.install.phase.rolling_back": {
"message": "Palautetaan muutoksia"
},
"app.install.phase.running_loader_processors": {
"message": "Suoritetaan lataajan käsittelijöitä"
},
"app.instance.confirm-delete.admonition-body": {
"message": "Kaikki sinun instanssisi data poistetaan pysyvästi, sisältäen maailmasi, konfiguraatiosi ja asennetun sisällön."
},
@@ -272,15 +197,6 @@
"app.instance.modpack-already-installed.instance": {
"message": "Instanssi"
},
"app.instance.mods.bulk-update.downloading-projects": {
"message": "Ladataan {current, number}/{total, number} projekteja..."
},
"app.instance.mods.bulk-update.finishing": {
"message": "Viimeistellään päivitystä..."
},
"app.instance.mods.bulk-update.resolving-versions": {
"message": "Selvitetään versioita..."
},
"app.instance.mods.content-type-project": {
"message": "projekti"
},
@@ -305,18 +221,6 @@
"app.instance.worlds.browse-servers": {
"message": "Selaa palvelimia"
},
"app.instance.worlds.delete-world-modal.delete-button": {
"message": "Poista maailma"
},
"app.instance.worlds.delete-world-modal.title": {
"message": "Poista maailma"
},
"app.instance.worlds.delete-world-modal.warning-body": {
"message": "Tämä maailma poistetaan pysyvästi tästä instanssista. Tätä toimintoa ei voi kumota."
},
"app.instance.worlds.delete-world-modal.warning-header": {
"message": "Poistetaan {name}"
},
"app.instance.worlds.filter-modded": {
"message": "Modattu"
},
@@ -335,21 +239,12 @@
"app.instance.worlds.no-worlds-heading": {
"message": "Ei palvelimia tai maailmoja lisättynä"
},
"app.instance.worlds.remove-server-modal.remove-button": {
"message": "Poista palvelin"
},
"app.instance.worlds.remove-server-modal.title": {
"message": "Poista palvelin"
},
"app.instance.worlds.remove-server-modal.warning-body": {
"message": "Tämä palvelin poistetaan palvelinluettelostasi ja pelin sisäisestä palvelinluettelosta. Voit lisätä sen myöhemmin uudelleen, jos tiedät osoitteen."
},
"app.instance.worlds.remove-server-modal.warning-header": {
"message": "Poistetaan {name}"
},
"app.instance.worlds.search-worlds-placeholder": {
"message": "Hae {count} maailmasta..."
},
"app.modal.install-to-play.content-required": {
"message": "Sisältö vaaditaan"
},
"app.modal.install-to-play.header": {
"message": "Asenna pelataksesi"
},
@@ -359,15 +254,27 @@
"app.modal.install-to-play.mod-count": {
"message": "{count, plural, one {# mod} other {# modia}}"
},
"app.modal.install-to-play.required-modpack": {
"message": "Vaadittu modipaketti"
},
"app.modal.install-to-play.server-requires-mods": {
"message": "Tämä palvelin vaatii modeja toimiakseen. Klikkaa Asenna ladataksesi vaaditut tiedostot Modrinthista, ja käynnistä peli suoraan palvelimelle."
},
"app.modal.install-to-play.shared-instance": {
"message": "Jaettu instanssi"
},
"app.modal.install-to-play.shared-server-instance": {
"message": "Jaettu palvelininstanssi"
},
"app.modal.install-to-play.view-contents": {
"message": "Tarkastele sisältöä"
},
"app.modal.update-to-play.header": {
"message": "Päivitä pelataksesi"
},
"app.modal.update-to-play.update-required": {
"message": "Päivitys vaaditaan"
},
"app.modal.update-to-play.update-required-description": {
"message": "Pävitys vaaditaan pelataksesi {name}. Päivitä viimeisimpään versioon pelataksesi."
},
@@ -377,6 +284,9 @@
"app.project.install-context.back-to-browse": {
"message": "Takaisin sisällön löytöön"
},
"app.project.install-context.install-content-to-instance": {
"message": "Asenna sisältöä instanssiin"
},
"app.settings.developer-mode-enabled": {
"message": "Kehittäjätila käytössä."
},
@@ -401,147 +311,15 @@
"app.settings.tabs.resource-management": {
"message": "Resurssien hallinta"
},
"app.skins.add-button": {
"message": "Lisää ulkoasu"
},
"app.skins.add-button.drag-and-drop": {
"message": "Raahaa ja pudota"
},
"app.skins.apply-button": {
"message": "Käytä"
},
"app.skins.delete-button": {
"message": "Poista ulkoasu"
},
"app.skins.delete-modal.description": {
"message": "Tämä poistaa valitun skinin pysyvästi. Tätä toimintoa ei voi kumota."
},
"app.skins.delete-modal.title": {
"message": "Haluatko varmasti poistaa ulkoasun?"
},
"app.skins.dropped-file-error.text": {
"message": "Tiedoston lukeminen epäonnistui."
},
"app.skins.dropped-file-error.title": {
"message": "Virhe prosessoidessa tiedostoa"
},
"app.skins.edit-button": {
"message": "Muokkaa ulkoasua"
},
"app.skins.modal.add-skin-button": {
"message": "Lisää ulkoasu"
},
"app.skins.modal.add-title": {
"message": "Lisätään ulkoasua"
},
"app.skins.modal.arm-style-section": {
"message": "Käsien tyyli"
},
"app.skins.modal.arm-style-slim": {
"message": "Ohut"
},
"app.skins.modal.arm-style-wide": {
"message": "Paksu"
},
"app.skins.modal.cape-fallback-name": {
"message": "Viitta"
},
"app.skins.modal.cape-section": {
"message": "Viitta"
},
"app.skins.modal.edit-title": {
"message": "Muokataan ulkoasua"
},
"app.skins.modal.make-edit-first-tooltip": {
"message": "Tee muokkaus ulkoasuun ensin!"
},
"app.skins.modal.no-cape-tooltip": {
"message": "Ei viittaa"
},
"app.skins.modal.none-cape-option": {
"message": "Ei mitään"
},
"app.skins.modal.replace-texture-button": {
"message": "Korvaa tekstuuri"
},
"app.skins.modal.save-skin-button": {
"message": "Tallenna ulkoasu"
},
"app.skins.modal.saving-tooltip": {
"message": "Tallennetaan..."
},
"app.skins.modal.texture-section": {
"message": "Tekstuuri"
},
"app.skins.modal.upload-skin-first-tooltip": {
"message": "Lataa ulkoasu ensin!"
},
"app.skins.preview.edit-button": {
"message": "Muokkaa ulkoasua"
},
"app.skins.previewing-badge": {
"message": "Esikatsellaan"
},
"app.skins.rate-limit.text": {
"message": "Vaihdat ulkoasua liian usein. Mojangin palvelimet ovat estäneet uudet pyynnöt väliaikaisesti. Yritä hetken kuluttua uudelleen."
},
"app.skins.rate-limit.title": {
"message": "Hidasta vähän!"
},
"app.skins.reorder-error.text": {
"message": "Ulkoasujen järjestystä ei voitu tallentaa."
},
"app.skins.reorder-error.title": {
"message": "Skinien järjestyksen muuttaminen epäonnistui"
},
"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": "Oletus ulkoasut"
},
"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": "Sait nämä skinit lahjoittamalla Modrinthin Pride-keräykseen Pride-kuukauden aikana."
},
"app.skins.section.mounts-of-mayhem": {
"message": "Sekasorron ratsut"
},
"app.skins.section.saved-skins": {
"message": "Tallennetut ulkoasut"
},
"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": "Pienten vallankumous"
},
"app.skins.sign-in.button": {
"message": "Kirjaudu sisään"
},
"app.skins.sign-in.description": {
"message": "Kirjaudu Minecraft-tilillesi käyttääksesi Modrinth-sovelluksen ulkoasujen hallintaominaisuuksia."
},
"app.skins.sign-in.rinthbot-alt": {
"message": "Innostunut Modrinth Botti"
},
"app.skins.sign-in.title": {
"message": "Kirjaudu sisään"
},
@@ -767,30 +545,15 @@
"instance.settings.tabs.general.name": {
"message": "Nimi"
},
"instance.settings.tabs.general.update-channel": {
"message": "Päivitys kanava"
},
"instance.settings.tabs.general.update-channel.alpha": {
"message": "Alfa"
},
"instance.settings.tabs.general.update-channel.alpha.description": {
"message": "Release-, beta- ja alpha-versiot näytetään saatavilla olevina päivityksinä."
},
"instance.settings.tabs.general.update-channel.beta": {
"message": "Beta"
},
"instance.settings.tabs.general.update-channel.beta.description": {
"message": "Release- ja beta-versiot näytetään saatavilla olevina päivityksinä."
},
"instance.settings.tabs.general.update-channel.release": {
"message": "Julkaisu"
},
"instance.settings.tabs.general.update-channel.release.description": {
"message": "Vain release-versiot näytetään saatavilla olevina päivityksinä."
},
"instance.settings.tabs.general.update-channel.select": {
"message": "Valitse päivitys kanava"
},
"instance.settings.tabs.hooks": {
"message": "Aloitustoiminnot"
},
@@ -914,9 +677,6 @@
"instance.worlds.copy_address": {
"message": "Kopioi osoite"
},
"instance.worlds.create_shortcut": {
"message": "Luo pikakuvake"
},
"instance.worlds.dont_show_on_home": {
"message": "Älä näytä koti näkymässä"
},
@@ -991,5 +751,26 @@
},
"search.filter.locked.server-loader.title": {
"message": "Modialusta on palvelimen tarjoama"
},
"unknown-pack-warning-modal.body": {
"message": "Tiedosto tarkistetaan vain jos se on ladattu Modrinthiin, riippumatta tiedoston muodosta (mukaanlukien .mrpack)."
},
"unknown-pack-warning-modal.dont-show-again": {
"message": "Älä näytä tätä varoitusta uudestaan"
},
"unknown-pack-warning-modal.header": {
"message": "Vahvista asennus"
},
"unknown-pack-warning-modal.install-anyway": {
"message": "Asenna jokatapauksessa"
},
"unknown-pack-warning-modal.malware-statement": {
"message": "Haittaohjelmia levitetään usein modipaketti tiedostojen kautta jakamalla niitä alustoilla kuten Discordissa."
},
"unknown-pack-warning-modal.warning.body": {
"message": "Emme löytäneet tätä tiedostoa Modrinthista. Suosittelemme vahvasti että asennat tiedostoja vain lähteistä joihin luotat."
},
"unknown-pack-warning-modal.warning.title": {
"message": "Tuntematon tiedosto varoitus"
}
}
@@ -11,9 +11,6 @@
"app.action-bar.hide-more-running-instances": {
"message": "Itago ang mas marami pang tumatakbong instansiya"
},
"app.action-bar.install.retry": {
"message": "Subukan muli"
},
"app.action-bar.make-primary-instance": {
"message": "Gumawa ng pangunahing instansiya"
},
@@ -146,6 +143,9 @@
"app.browse.discover-servers": {
"message": "Tumuklas ng mga server"
},
"app.browse.hide-added-servers": {
"message": "Itago ang mga nailagay na mga servers"
},
"app.browse.project-type.modpacks": {
"message": "Modpacks"
},
@@ -164,9 +164,18 @@
"app.export-modal.header": {
"message": "Iluwas ang modpack"
},
"app.export-modal.include-file-accessibility-label": {
"message": "Salihin ang \"{file}\"?"
},
"app.export-modal.modpack-name-label": {
"message": "Pangalan ng Modpack"
},
"app.export-modal.modpack-name-placeholder": {
"message": "Pangalan ng modpack"
},
"app.export-modal.select-files-label": {
"message": "Isaayos kung anong mga file ang isasali sa pagluwas"
},
"app.export-modal.version-number-label": {
"message": "Numero ng bersiyon"
},
@@ -242,6 +251,9 @@
"app.instance.worlds.search-worlds-placeholder": {
"message": "Hanapin sa {count} mundo..."
},
"app.modal.install-to-play.content-required": {
"message": "Nangangailangan ng kontento"
},
"app.modal.install-to-play.header": {
"message": "Mag-install upang malaro"
},
@@ -251,15 +263,27 @@
"app.modal.install-to-play.mod-count": {
"message": "{count, plural, one {# mod} other {# na mod}}"
},
"app.modal.install-to-play.required-modpack": {
"message": "Kinailangan na modpack"
},
"app.modal.install-to-play.server-requires-mods": {
"message": "Ang server rna ito ay nangangailangan ng mga mod upang makalaro. Pindutin ang install upang maihanda ang mga kinakailangang file galing sa Modrinth, matapos ay ilunsad nang diretso sa server."
},
"app.modal.install-to-play.shared-instance": {
"message": "Binahaging instansiya"
},
"app.modal.install-to-play.shared-server-instance": {
"message": "Binahaging instansiyang pang-server"
},
"app.modal.install-to-play.view-contents": {
"message": "Tingnan ang mga kontento"
},
"app.modal.update-to-play.header": {
"message": "Mag-update upang malaro"
},
"app.modal.update-to-play.update-required": {
"message": "Kailangang mag-update"
},
"app.modal.update-to-play.update-required-description": {
"message": "Kailangang mag-update upang malaro ang {name}. Mangyaring mag-update sa pinakabagong bersiyon upang ma-launch ang laro."
},
@@ -549,7 +573,7 @@
"message": "Mga launch hook"
},
"instance.settings.tabs.hooks.custom-hooks": {
"message": "Mga pasadyang launch hook"
"message": "Mga custom na launch hook"
},
"instance.settings.tabs.hooks.description": {
"message": "Binibigyan-daan ng mga hook ang mga ekspertong tagagamit na makapagtakbo ng mga pansistemang utos o system command bago at pagkatapos ma-launch ang laro."
@@ -612,7 +636,7 @@
"message": "Window"
},
"instance.settings.tabs.window.custom-window-settings": {
"message": "Mga setting sa pasadyang window"
"message": "Mga setting sa custom na window"
},
"instance.settings.tabs.window.fullscreen": {
"message": "Fullscreen"
@@ -721,5 +745,26 @@
},
"search.filter.locked.server-loader.title": {
"message": "Ang loader ay handog na ng server"
},
"unknown-pack-warning-modal.body": {
"message": "Ang file ay nasusuri lamang kapag ito ay na-upload sa Modrinth, walang pili sa file format nito (kabilang na ang .mrpack)."
},
"unknown-pack-warning-modal.dont-show-again": {
"message": "Huwag ipakita ang babalang ito ulit"
},
"unknown-pack-warning-modal.header": {
"message": "Kumpirmahin ang installation"
},
"unknown-pack-warning-modal.install-anyway": {
"message": "I-install pa rin"
},
"unknown-pack-warning-modal.malware-statement": {
"message": "Ang malware ay madalas naidadala sa mga modpack files sa pamamagitan ng pag-bigay ng mga ito sa mga platforms kagaya ng Discord."
},
"unknown-pack-warning-modal.warning.body": {
"message": "Hindi namin mahanap ang file na ito sa Modrinth. Mahalagang mag-install ka lamang ng files galing sa mga mapagkakatiwalang sources."
},
"unknown-pack-warning-modal.warning.title": {
"message": "Hindi kilalang file"
}
}
+47 -221
View File
@@ -9,106 +9,25 @@
"message": "Téléchargements"
},
"app.action-bar.hide-more-running-instances": {
"message": "Masquer plus d'instances en cours d'exécution"
},
"app.action-bar.install.copied-details": {
"message": "Copié"
},
"app.action-bar.install.copy-details": {
"message": "Copier les détails"
},
"app.action-bar.install.dismiss": {
"message": "Rejeter"
},
"app.action-bar.install.open-instance": {
"message": "Ouvrir l'instance"
},
"app.action-bar.install.retry": {
"message": "Réessayer"
},
"app.action-bar.install.summary.app-closing": {
"message": "Annulé en raison de la fermeture de l'application"
},
"app.action-bar.install.summary.bad-modpack-file": {
"message": "Impossible de lire le modpack"
},
"app.action-bar.install.summary.canceled": {
"message": "Annulé"
},
"app.action-bar.install.summary.cleanup-incomplete": {
"message": "Le nettoyage n'a pas terminé"
},
"app.action-bar.install.summary.content-download-failed": {
"message": "Impossible de télécharger les fichiers"
},
"app.action-bar.install.summary.corrupt-download": {
"message": "Le fichier téléchargé est corrompu"
},
"app.action-bar.install.summary.could-not-save-files": {
"message": "Impossible de sauvegarder les fichiers"
},
"app.action-bar.install.summary.download-failed": {
"message": "Impossible de terminer le téléchargement"
},
"app.action-bar.install.summary.instance-not-found": {
"message": "L'instance est introuvable"
},
"app.action-bar.install.summary.invalid-file-path": {
"message": "Le chemin d'accès au fichier est invalide"
},
"app.action-bar.install.summary.invalid-modpack": {
"message": "Les données du modpack sont invalides"
},
"app.action-bar.install.summary.invalid-modpack-files": {
"message": "Les fichiers du modpack contiennent des métadonnées invalides"
},
"app.action-bar.install.summary.java-setup-failed": {
"message": "L'installation de Java n'a pas pu se terminer"
},
"app.action-bar.install.summary.loader-setup-failed": {
"message": "La mise en place du loader a échoué"
},
"app.action-bar.install.summary.local-data-error": {
"message": "Impossible de mettre à jour les données locales"
},
"app.action-bar.install.summary.minecraft-setup-failed": {
"message": "La mise en place de Minecraft a échoué"
},
"app.action-bar.install.summary.modrinth-unreachable": {
"message": "Impossible d'atteindre les serveurs Modrinth"
},
"app.action-bar.install.summary.no-write-permission": {
"message": "Aucune permission d'écriture"
},
"app.action-bar.install.summary.pack-download-failed": {
"message": "Impossible de télécharger le pack"
},
"app.action-bar.install.summary.unexpected-error": {
"message": "Quelque chose s'est mal passé"
},
"app.action-bar.install.unknown-instance": {
"message": "Instance inconnue"
},
"app.action-bar.installs": {
"message": "Installations"
"message": "Masquer plus d'instances en exécution"
},
"app.action-bar.make-primary-instance": {
"message": "Définir en tant qu'instance principale"
"message": "Définir en tante qu'instance première"
},
"app.action-bar.no-instances-running": {
"message": "Aucune instance en cours d'exécution"
"message": "Aucune instance en exécution"
},
"app.action-bar.offline": {
"message": "Hors ligne"
},
"app.action-bar.primary-instance": {
"message": "Instance principale"
"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 cours d'exécution"
"message": "Montrer plus d'instances en exécution"
},
"app.action-bar.stop-instance": {
"message": "Arrêter l'instance"
@@ -182,12 +101,6 @@
"app.appearance-settings.show-play-time.title": {
"message": "Afficher le temps de jeu"
},
"app.appearance-settings.skip-non-essential-warnings.description": {
"message": "Ignore automatiquement les confirmations à faible risque, comme l'installation de modpacks en double, la suppression de contenu standard, les mises à jour groupées, la dissociation des modpacks et les demandes de réparation. Les avertissements concernant les actions dangereuses continueront d'être affichés."
},
"app.appearance-settings.skip-non-essential-warnings.title": {
"message": "Cacher les alertes non essentielles"
},
"app.appearance-settings.toggle-sidebar.description": {
"message": "Permet d'ouvrir ou de fermer la barre latérale."
},
@@ -234,7 +147,7 @@
"message": "Découvrir des serveurs"
},
"app.browse.hide-added-servers": {
"message": "Masquer les serveurs déjà installés"
"message": "Masquer les serveurs déjà ajoutés"
},
"app.browse.project-type.modpacks": {
"message": "Modpacks"
@@ -257,72 +170,24 @@
"app.export-modal.header": {
"message": "Exporter le modpack"
},
"app.export-modal.include-file-accessibility-label": {
"message": "Inclure « {file} » ?"
},
"app.export-modal.modpack-name-label": {
"message": "Nom du modpack"
},
"app.export-modal.modpack-name-placeholder": {
"message": "Nom du modpack"
},
"app.export-modal.select-files-label": {
"message": "Configurez quels fichiers sont inclus dans cette exportation"
},
"app.export-modal.version-number-label": {
"message": "Numéro de version"
},
"app.export-modal.version-number-placeholder": {
"message": "1.0.0"
},
"app.install.phase.downloading_content": {
"message": "Téléchargement du contenu"
},
"app.install.phase.downloading_minecraft": {
"message": "Téléchargement de Minecraft"
},
"app.install.phase.downloading_pack_file": {
"message": "Téléchargement du fichier de pack"
},
"app.install.phase.extracting_overrides": {
"message": "Extraction des fichiers personnalisés"
},
"app.install.phase.finalizing": {
"message": "Finalisation"
},
"app.install.phase.preparing_instance": {
"message": "En attente d'installation"
},
"app.install.phase.preparing_java": {
"message": "Préparation de Java"
},
"app.install.phase.preparing_java.downloading": {
"message": "Téléchargement de Java {version}"
},
"app.install.phase.preparing_java.extracting": {
"message": "Extraction de Java {version}"
},
"app.install.phase.preparing_java.fetching-metadata": {
"message": "Récupération de Java {version}"
},
"app.install.phase.preparing_java.resolving": {
"message": "Préparation de Java {version}"
},
"app.install.phase.preparing_java.validating": {
"message": "Validation de Java {version}"
},
"app.install.phase.reading_pack_manifest": {
"message": "Lecture du manifeste du pack"
},
"app.install.phase.resolving_loader": {
"message": "Résolution du loader"
},
"app.install.phase.resolving_minecraft": {
"message": "Résolution de Minecraft"
},
"app.install.phase.resolving_pack": {
"message": "Résolution du contenu"
},
"app.install.phase.rolling_back": {
"message": "Annulation"
},
"app.install.phase.running_loader_processors": {
"message": "Exécution des processeurs du chargeur"
},
"app.instance.confirm-delete.admonition-body": {
"message": "Toutes les données pour votre instance seront supprimées à jamais, y compris vos mondes, vos configurations, et le contenu installé."
},
@@ -347,15 +212,6 @@
"app.instance.modpack-already-installed.instance": {
"message": "Instance"
},
"app.instance.mods.bulk-update.downloading-projects": {
"message": "Téléchargement de {current, number}/{total, number} projets..."
},
"app.instance.mods.bulk-update.finishing": {
"message": "Finalisation de la mise à jour..."
},
"app.instance.mods.bulk-update.resolving-versions": {
"message": "Résolution des versions..."
},
"app.instance.mods.content-type-project": {
"message": "projet"
},
@@ -380,18 +236,6 @@
"app.instance.worlds.browse-servers": {
"message": "Parcourir des serveurs"
},
"app.instance.worlds.delete-world-modal.delete-button": {
"message": "Supprimer le monde"
},
"app.instance.worlds.delete-world-modal.title": {
"message": "Supprimer le monde"
},
"app.instance.worlds.delete-world-modal.warning-body": {
"message": "Ce monde sera définitivement supprimé de cette instance. Cette action est irréversible."
},
"app.instance.worlds.delete-world-modal.warning-header": {
"message": "Suppression de {name}"
},
"app.instance.worlds.filter-modded": {
"message": "Moddé"
},
@@ -410,53 +254,32 @@
"app.instance.worlds.no-worlds-heading": {
"message": "Aucun serveur ou monde ajoutés"
},
"app.instance.worlds.remove-server-modal.remove-button": {
"message": "Enlever le serveur"
},
"app.instance.worlds.remove-server-modal.title": {
"message": "Enlever le serveur"
},
"app.instance.worlds.remove-server-modal.warning-body": {
"message": "Ce serveur sera enlevé de votre liste de serveur et de la liste de serveur en jeu. Vous pourrez le rajouter plus tard si vous connaissez son adresse."
},
"app.instance.worlds.remove-server-modal.warning-header": {
"message": "Retrait de {name}"
},
"app.instance.worlds.search-worlds-placeholder": {
"message": "Rechercher {count} mondes..."
},
"app.modal.install-to-play.dont-install": {
"message": "Ne pas installer"
"app.modal.install-to-play.content-required": {
"message": "Contenu requis"
},
"app.modal.install-to-play.header": {
"message": "Installer pour jouer"
},
"app.modal.install-to-play.install-anyway": {
"message": "Installer quand même"
},
"app.modal.install-to-play.install-button": {
"message": "Installer"
},
"app.modal.install-to-play.invite-warning": {
"message": "Cette invitation a été crée par un autre utilisateur Modrinth, pas par Modrinth. N'acceptez seulement les invitations de personnes en qui vous avez confiance."
},
"app.modal.install-to-play.mod-count": {
"message": "{count, plural, one {# mod} other {# mods}}"
},
"app.modal.install-to-play.reviewed-files": {
"message": "Un fichier est seulement examiné s'il est publié sur Modrinth, peu importe son extension (y compris .mrpack)."
"app.modal.install-to-play.required-modpack": {
"message": "Modpack requis"
},
"app.modal.install-to-play.server-requires-mods": {
"message": "Ce serveur a besoin de mods pour jouer. Cliquez sur Installer pour mettre en place les fichiers requis depuis Modrinth, puis lancez directement dans le serveur."
},
"app.modal.install-to-play.shared-instance": {
"message": "Instance partagée"
},
"app.modal.install-to-play.unknown-files-description": {
"message": "Ce modpack de serveur contient des fichiers qui ne sont pas publiés sur Modrinth. Nous recommandons fortement de seulement installer des fichiers de sources en qui vous avez confiance."
},
"app.modal.install-to-play.unknown-files-warning": {
"message": "Avertissement de fichiers inconnus"
},
"app.modal.install-to-play.unrecognized-files": {
"message": "Fichiers non reconnus"
"app.modal.install-to-play.shared-server-instance": {
"message": "Instance serveur partagée"
},
"app.modal.install-to-play.view-contents": {
"message": "Voir le contenu"
@@ -464,8 +287,8 @@
"app.modal.update-to-play.header": {
"message": "Mettre à jour pour jouer"
},
"app.modal.update-to-play.removed": {
"message": "Supprimé"
"app.modal.update-to-play.update-required": {
"message": "Mise à jour requise"
},
"app.modal.update-to-play.update-required-description": {
"message": "Une mise à jour est requise pour jouer à {name}. Veuillez mettre à jour à la dernière version pour lancer le jeu."
@@ -473,23 +296,11 @@
"app.project.install-button.already-installed": {
"message": "Ce projet a déjà été installé"
},
"app.project.install-button.switch-version": {
"message": "Changer de version"
},
"app.project.install-context.back-to-browse": {
"message": "Retour vers la page découvrir"
},
"app.project.version.all-versions": {
"message": "Toutes les versions"
},
"app.project.version.download-in-browser": {
"message": "Télécharger dans le navigateur"
},
"app.project.version.installing": {
"message": "Installation"
},
"app.project.versions.already-installed": {
"message": "Déjà installé"
"app.project.install-context.install-content-to-instance": {
"message": "Installer du contenu à l'instance"
},
"app.settings.developer-mode-enabled": {
"message": "Mode développeur activé."
@@ -611,9 +422,6 @@
"app.skins.section.builders-and-biomes": {
"message": "Builders & Biomes"
},
"app.skins.section.chaos-cubed": {
"message": "Cubes du chaos"
},
"app.skins.section.chase-the-skies": {
"message": "Chassez les cieux"
},
@@ -642,7 +450,7 @@
"message": "Striding Hero"
},
"app.skins.section.the-copper-age": {
"message": "L'âge du cuivre"
"message": "The Copper Age"
},
"app.skins.section.the-garden-awakens": {
"message": "L'éveil du jardin"
@@ -1028,9 +836,6 @@
"instance.worlds.copy_address": {
"message": "Copier l'adresse"
},
"instance.worlds.create_shortcut": {
"message": "Créer un raccourci"
},
"instance.worlds.dont_show_on_home": {
"message": "Ne pas montrer dans l'Accueil"
},
@@ -1105,5 +910,26 @@
},
"search.filter.locked.server-loader.title": {
"message": "Le loader est procuré par le serveur"
},
"unknown-pack-warning-modal.body": {
"message": "Un fichier nest révisé que sil est téléchargé sur Modrinth, quel que soit son format de fichier (y compris .mrpack)."
},
"unknown-pack-warning-modal.dont-show-again": {
"message": "Ne plus m'avertir à ce sujet"
},
"unknown-pack-warning-modal.header": {
"message": "Confirmer l'installation"
},
"unknown-pack-warning-modal.install-anyway": {
"message": "Installer tout de même"
},
"unknown-pack-warning-modal.malware-statement": {
"message": "Les logiciels malveillants sont souvent distribués via des fichiers modpack en les partageant sur des plateformes comme Discord."
},
"unknown-pack-warning-modal.warning.body": {
"message": "Nous ne pouvions pas trouver ce fichier sur Modrinth. Nous vous recommandons fortement de n'installer que des fichiers de sources de confiance."
},
"unknown-pack-warning-modal.warning.title": {
"message": "Avertissement fichier inconnu"
}
}
@@ -59,6 +59,9 @@
"app.export-modal.header": {
"message": "יצוא חבילת מודים"
},
"app.export-modal.modpack-name-label": {
"message": "שם חבילת המודים"
},
"app.export-modal.modpack-name-placeholder": {
"message": "שם חבילת המודים"
},
@@ -137,6 +140,9 @@
"app.instance.worlds.search-worlds-placeholder": {
"message": "חיפוש ב-{count} עולמות..."
},
"app.modal.install-to-play.content-required": {
"message": "תוכן נדרש"
},
"app.modal.install-to-play.header": {
"message": "צריך להתקין כדי לשחק"
},
@@ -146,15 +152,27 @@
"app.modal.install-to-play.mod-count": {
"message": "{count, plural, one {מוד אחד} other {# מודים}}"
},
"app.modal.install-to-play.required-modpack": {
"message": "חבילת מודים נדרשת"
},
"app.modal.install-to-play.server-requires-mods": {
"message": "שרת זה דורש מודים כדי לשחק. לחץ על \"התקן\" כדי להגדיר את הקבצים הנדרשים מ-Modrinth, ולאחר מכן הפעל ישירות לשרת."
},
"app.modal.install-to-play.shared-instance": {
"message": "התקנה משותפת"
},
"app.modal.install-to-play.shared-server-instance": {
"message": "התקנת שרת משותפת"
},
"app.modal.install-to-play.view-contents": {
"message": "הצג תוכן"
},
"app.modal.update-to-play.header": {
"message": "צריך לעדכן כדי לשחק"
},
"app.modal.update-to-play.update-required": {
"message": "עדכון נדרש"
},
"app.modal.update-to-play.update-required-description": {
"message": "עדכון נדרש כדי לשחק ב{name}. ניתן להתחיל את המשחק רק לאחר עדכון לגרסה החדשה."
},
+55 -235
View File
@@ -11,87 +11,6 @@
"app.action-bar.hide-more-running-instances": {
"message": "További futó példányok elrejtése"
},
"app.action-bar.install.copied-details": {
"message": "Másolva"
},
"app.action-bar.install.copy-details": {
"message": "Részletek másolása"
},
"app.action-bar.install.dismiss": {
"message": "Elutasítás"
},
"app.action-bar.install.open-instance": {
"message": "Példány megnyitása"
},
"app.action-bar.install.retry": {
"message": "Újra"
},
"app.action-bar.install.summary.app-closing": {
"message": "Az alkalmazás bezárása miatt megszakítva"
},
"app.action-bar.install.summary.bad-modpack-file": {
"message": "Nem sikerült beolvasni a modcsomagot"
},
"app.action-bar.install.summary.canceled": {
"message": "Megszakítva"
},
"app.action-bar.install.summary.cleanup-incomplete": {
"message": "A tisztítás nem fejeződött be"
},
"app.action-bar.install.summary.content-download-failed": {
"message": "Nem sikerült letölteni a fájlokat"
},
"app.action-bar.install.summary.corrupt-download": {
"message": "A letöltött fájl sérült"
},
"app.action-bar.install.summary.could-not-save-files": {
"message": "Nem sikerült elmenteni a fájlokat"
},
"app.action-bar.install.summary.download-failed": {
"message": "A letöltés nem sikerült"
},
"app.action-bar.install.summary.instance-not-found": {
"message": "A játékprofil nem található"
},
"app.action-bar.install.summary.invalid-file-path": {
"message": "A fájl elérési útja érvénytelen"
},
"app.action-bar.install.summary.invalid-modpack": {
"message": "A modcsomag adatai érvénytelenek"
},
"app.action-bar.install.summary.invalid-modpack-files": {
"message": "A modcsomag fájljainak metaadatai érvénytelenek"
},
"app.action-bar.install.summary.java-setup-failed": {
"message": "A Java telepítése nem sikerült"
},
"app.action-bar.install.summary.loader-setup-failed": {
"message": "A betöltő beállítása sikertelen volt"
},
"app.action-bar.install.summary.local-data-error": {
"message": "A helyi adatok frissítése nem sikerült"
},
"app.action-bar.install.summary.minecraft-setup-failed": {
"message": "A Minecraft telepítése sikertelen volt"
},
"app.action-bar.install.summary.modrinth-unreachable": {
"message": "Nem sikerült elérni Modrinthot"
},
"app.action-bar.install.summary.no-write-permission": {
"message": "Nincs felülírási jogosultság"
},
"app.action-bar.install.summary.pack-download-failed": {
"message": "Nem sikerült letölteni a csomagot"
},
"app.action-bar.install.summary.unexpected-error": {
"message": "Valami hiba történt"
},
"app.action-bar.install.unknown-instance": {
"message": "Ismeretlen játékprofil"
},
"app.action-bar.installs": {
"message": "Telepítések"
},
"app.action-bar.make-primary-instance": {
"message": "Beállítás elsődleges példányként"
},
@@ -123,13 +42,13 @@
"message": "Példányok megtekintése"
},
"app.action-bar.view-logs": {
"message": "Naplózások megtekintése"
"message": "Naplók megtekintése"
},
"app.appearance-settings.advanced-rendering.description": {
"message": "Lehetővé teszi olyan speciális megjelenítési effektusok használatát, mint például az elmosódás, amelyek hardveres gyorsítás nélkül teljesítménycsökkenést okozhatnak."
},
"app.appearance-settings.advanced-rendering.title": {
"message": "Haladó megjelenítési beállítások"
"message": "Fejlett renderelés"
},
"app.appearance-settings.color-theme.description": {
"message": "Válaszd ki a Modrinth Apphoz a kívánt színsémát."
@@ -159,7 +78,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": "„Folytasd itt” a világokat is"
"message": "Visszaugrás a világokba"
},
"app.appearance-settings.minimize-launcher.description": {
"message": "Minimalizáld az indítót a Minecraft indításakor."
@@ -177,17 +96,11 @@
"message": "Válassz egy lehetőséget"
},
"app.appearance-settings.show-play-time.description": {
"message": "Megmutatja, mennyi időt töltöttél egy példányban."
"message": "Megmutatja, mennyi időt töltöttél játszva egy profillal."
},
"app.appearance-settings.show-play-time.title": {
"message": "Játékidő megjelenítése"
},
"app.appearance-settings.skip-non-essential-warnings.description": {
"message": "Az alacsony kockázatú megerősítéseket például a modcsomagok ismételt telepítését, a szokásos tartalomtörlést, a tömeges frissítéseket, a modcsomagok kapcsolatának megszüntetését és a javítási felhívásokat a rendszer automatikusan kihagyja. A veszélyes figyelmeztetéseket továbbra is megjeleníti."
},
"app.appearance-settings.skip-non-essential-warnings.title": {
"message": "Hagyd ki a lényegtelen figyelmeztetéseket"
},
"app.appearance-settings.toggle-sidebar.description": {
"message": "Lehetővé teszi az oldalsáv be- és kikapcsolását."
},
@@ -234,7 +147,7 @@
"message": "Szerverek böngészése"
},
"app.browse.hide-added-servers": {
"message": "A már hozzáadott szerverek elrejtése"
"message": "Már hozzáadott szerverek elrejtése"
},
"app.browse.project-type.modpacks": {
"message": "Modcsomagok"
@@ -243,7 +156,7 @@
"message": "A tartalom hozzáadása ronthatja a kompatibilitást a szerverhez való csatlakozáskor. A hozzáadott tartalom elveszik, ha frissíted a szerverprofil tartalmát."
},
"app.browse.server.installing": {
"message": "Letöltés"
"message": "Letöltés..."
},
"app.content-install.no-compatible-versions": {
"message": "Nincs a(z) {compatibilityLabel} verziónak megfelelő elérhető verzió. Válassz ki egy verziót, ha mégis telepíteni szeretnéd. A függőségek nem települnek automatikusan."
@@ -257,74 +170,26 @@
"app.export-modal.header": {
"message": "Modcsomag exportálása"
},
"app.export-modal.include-file-accessibility-label": {
"message": "Tartalmaz „{file}” fájlokat?"
},
"app.export-modal.modpack-name-label": {
"message": "Modcsamag neve"
"message": "A modcsomag neve"
},
"app.export-modal.modpack-name-placeholder": {
"message": "A modcsomag neve"
},
"app.export-modal.select-files-label": {
"message": "Állítsd be, hogy mely fájlok kerüljenek bele az exportba"
},
"app.export-modal.version-number-label": {
"message": "Verziószám"
},
"app.export-modal.version-number-placeholder": {
"message": "1.0.0"
},
"app.install.phase.downloading_content": {
"message": "Tartalom letöltése"
},
"app.install.phase.downloading_minecraft": {
"message": "Minecraft letöltése"
},
"app.install.phase.downloading_pack_file": {
"message": "Csomagfájlok letöltése"
},
"app.install.phase.extracting_overrides": {
"message": "Felülírások kibontása"
},
"app.install.phase.finalizing": {
"message": "Befejezés"
},
"app.install.phase.preparing_instance": {
"message": "Telepítésre vár"
},
"app.install.phase.preparing_java": {
"message": "Java előkészítése"
},
"app.install.phase.preparing_java.downloading": {
"message": "A Java {version} letöltése..."
},
"app.install.phase.preparing_java.extracting": {
"message": "A Java {version} kibontása..."
},
"app.install.phase.preparing_java.fetching-metadata": {
"message": "A Java {version} előkészítése..."
},
"app.install.phase.preparing_java.resolving": {
"message": "A Java {version} előkészítése..."
},
"app.install.phase.preparing_java.validating": {
"message": "A Java {version} ellenőrzése"
},
"app.install.phase.reading_pack_manifest": {
"message": "Csomag adati kiolvasása"
},
"app.install.phase.resolving_loader": {
"message": ""
},
"app.install.phase.resolving_minecraft": {
"message": "Minecraft Betöltése"
},
"app.install.phase.resolving_pack": {
"message": "Tartalom betöltése"
},
"app.install.phase.rolling_back": {
"message": "Visszavonás"
},
"app.install.phase.running_loader_processors": {
"message": "Betöltő folyamatok futtatása"
},
"app.instance.confirm-delete.admonition-body": {
"message": "A profilodhoz tartozó összes adat véglegesen törlődik, beleértve a világokat, a beállításokat és az összes telepített tartalmat."
"message": "Az profilodhoz tartozó összes adat véglegesen törlődik, beleértve a világokat, a beállításokat és az összes telepített tartalmat."
},
"app.instance.confirm-delete.admonition-header": {
"message": "Ezt a műveletet nem lehet visszavonni"
@@ -347,15 +212,6 @@
"app.instance.modpack-already-installed.instance": {
"message": "Példány"
},
"app.instance.mods.bulk-update.downloading-projects": {
"message": "{current, number}/{total, number} projekt letöltése..."
},
"app.instance.mods.bulk-update.finishing": {
"message": "Frissítés befejezése..."
},
"app.instance.mods.bulk-update.resolving-versions": {
"message": "Verziók meghatározása..."
},
"app.instance.mods.content-type-project": {
"message": "projekt"
},
@@ -380,18 +236,6 @@
"app.instance.worlds.browse-servers": {
"message": "Szerverek böngészése"
},
"app.instance.worlds.delete-world-modal.delete-button": {
"message": "Törlés"
},
"app.instance.worlds.delete-world-modal.title": {
"message": "Világ törlése"
},
"app.instance.worlds.delete-world-modal.warning-body": {
"message": "Ez a világ véglegesen törölve lesz ebből a példányból. Ezt NEM tudod visszavonni!"
},
"app.instance.worlds.delete-world-modal.warning-header": {
"message": "{name} törlése"
},
"app.instance.worlds.filter-modded": {
"message": "Modolt"
},
@@ -410,53 +254,32 @@
"app.instance.worlds.no-worlds-heading": {
"message": "Nincs szerver vagy világ"
},
"app.instance.worlds.remove-server-modal.remove-button": {
"message": "Szerver eltávolítása"
},
"app.instance.worlds.remove-server-modal.title": {
"message": "Szerver eltávolítása"
},
"app.instance.worlds.remove-server-modal.warning-body": {
"message": "Ez a szerver el lesz távolítva a szerverlistádból és a játékbeli szerverlistából is. Később újra hozzáadhatod, ha tudod a címet."
},
"app.instance.worlds.remove-server-modal.warning-header": {
"message": "{name} eltávolítása"
},
"app.instance.worlds.search-worlds-placeholder": {
"message": "Keresés {count} világ között..."
},
"app.modal.install-to-play.dont-install": {
"message": "Ne töltse le"
"app.modal.install-to-play.content-required": {
"message": "Szükséges tartalom"
},
"app.modal.install-to-play.header": {
"message": "Telepítés a játékhoz"
},
"app.modal.install-to-play.install-anyway": {
"message": "Telepítés mindenképpen"
},
"app.modal.install-to-play.install-button": {
"message": "Telepítés"
},
"app.modal.install-to-play.invite-warning": {
"message": "Ezt a meghívót nem a Modrinth, hanem egy másik Modrinth-felhasználó hozta létre. Csak olyan személyektől fogadd el a meghívásokat, akikben megbízol."
},
"app.modal.install-to-play.mod-count": {
"message": "{count} mod"
},
"app.modal.install-to-play.reviewed-files": {
"message": "Egy fájlt csak akkor vizsgálnak meg, ha azt a Modrinth-en közzétették, függetlenül a fájlformátumtól (beleértve a .mrpack formátumot is)."
"app.modal.install-to-play.required-modpack": {
"message": "Szükséges modcsomag"
},
"app.modal.install-to-play.server-requires-mods": {
"message": "Ehhez a szerverhez modok szükségesek a játékhoz. Kattints a Telepítés gombra, hogy telepítsd a szükséges fájlokat a Modrinth-ról, majd indítsd el közvetlenül a szervert."
},
"app.modal.install-to-play.shared-instance": {
"message": "Megosztott profil"
},
"app.modal.install-to-play.unknown-files-description": {
"message": "Ez a szerver-modpack olyan fájlokat tartalmaz, amelyek nem szerepelnek a Modrinth oldalon. Határozottan javasoljuk, hogy kizárólag olyan forrásokból telepíts fájlokat, amelyekben megbízol."
},
"app.modal.install-to-play.unknown-files-warning": {
"message": "Figyelmeztetés ismeretlen fájlokról"
},
"app.modal.install-to-play.unrecognized-files": {
"message": "Felismerhetetlen fájlok"
"app.modal.install-to-play.shared-server-instance": {
"message": "Megosztott szerverprofil"
},
"app.modal.install-to-play.view-contents": {
"message": "Tartalom megtekintése"
@@ -464,8 +287,8 @@
"app.modal.update-to-play.header": {
"message": "Frissítsd a játékhoz"
},
"app.modal.update-to-play.removed": {
"message": "Törölve"
"app.modal.update-to-play.update-required": {
"message": "Frissítés szükséges"
},
"app.modal.update-to-play.update-required-description": {
"message": "Frissítés szükséges ehhez: {name}. Kérlek, frissíts a legújabb verzióra a játék elindításához."
@@ -473,23 +296,11 @@
"app.project.install-button.already-installed": {
"message": "Ez a projekt már telepítve van"
},
"app.project.install-button.switch-version": {
"message": "Verzió váltása"
},
"app.project.install-context.back-to-browse": {
"message": "Vissza a böngészéshez"
},
"app.project.version.all-versions": {
"message": "Összes verzió"
},
"app.project.version.download-in-browser": {
"message": "Letöltés a böngészőben"
},
"app.project.version.installing": {
"message": "Telepítés..."
},
"app.project.versions.already-installed": {
"message": "Már le van töltve"
"app.project.install-context.install-content-to-instance": {
"message": "Tartalom telepítése a profilba"
},
"app.settings.developer-mode-enabled": {
"message": "Fejlesztői mód bekapcsolva."
@@ -602,18 +413,9 @@
"app.skins.rate-limit.title": {
"message": "Lassíts!"
},
"app.skins.reorder-error.text": {
"message": "A kinézeted sorrendjét nem sikerült menteni."
},
"app.skins.reorder-error.title": {
"message": "A skinek átrendezése sikertelen"
},
"app.skins.section.builders-and-biomes": {
"message": "Builders & Biomes"
},
"app.skins.section.chaos-cubed": {
"message": "Chaos Cubed"
},
"app.skins.section.chase-the-skies": {
"message": "Chase the Skies"
},
@@ -657,7 +459,7 @@
"message": "Kérlek jelentkezz be a Minecraft-fiókodba, hogy használni tudd a Modrinth App kinézetkezelési funkcióit."
},
"app.skins.sign-in.rinthbot-alt": {
"message": "Izgatott Modrinth bot"
"message": "Izgatott Modrinth-bot"
},
"app.skins.sign-in.title": {
"message": "Kérlek jelentkezz be"
@@ -903,7 +705,7 @@
"message": "Kiadás"
},
"instance.settings.tabs.general.update-channel.release.description": {
"message": "Csak a 'kiadás' verziók fognak megjelenni az elérhető frissítések között."
"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"
@@ -990,7 +792,7 @@
"message": "Hozzárendelt memória"
},
"instance.settings.tabs.java.java-path-placeholder": {
"message": "/eleresi/ut/a/javahoz"
"message": "/path/to/java"
},
"instance.settings.tabs.window": {
"message": "Ablak"
@@ -1031,14 +833,11 @@
"instance.worlds.copy_address": {
"message": "Cím másolása"
},
"instance.worlds.create_shortcut": {
"message": "Gyorshivatkozás létrehozása"
},
"instance.worlds.dont_show_on_home": {
"message": "Ne mutasd a kezdőlapon"
},
"instance.worlds.game_already_open": {
"message": "A profil már el van indítva"
"message": "A példány már el van indítva"
},
"instance.worlds.hardcore": {
"message": "Hardcore mód"
@@ -1056,7 +855,7 @@
"message": "Csak Minecraft Alpha 1.0.5+-tól tudsz egyből szerverhez csatlakozni"
},
"instance.worlds.no_singleplayer_quick_play": {
"message": "Használj 1.20+ verziót az azonnali egyjátékos módba lépéshez"
"message": "Csak Minecraft 1.20+-tól tudsz egyből egyjátékos világba belépni"
},
"instance.worlds.play_instance": {
"message": "Játék a profillal"
@@ -1104,9 +903,30 @@
"message": "Csak kliensoldali modok adhatók hozzá a szerverprofilhoz"
},
"search.filter.locked.server-game-version.title": {
"message": "A játékverzió a szerver által van megadva"
"message": "A játékverziót a szerver biztosítja"
},
"search.filter.locked.server-loader.title": {
"message": "A betöltő a szerver által van megadva"
"message": "A betöltőt a szerver biztosítja"
},
"unknown-pack-warning-modal.body": {
"message": "Egy fájlt csak akkor vizsgálunk meg, ha azt feltöltik a Modrinthra, függetlenül a fájlformátumtól (beleértve a .mrpack formátumot is)."
},
"unknown-pack-warning-modal.dont-show-again": {
"message": "Ne mutasd újra ezt a figyelmeztetést"
},
"unknown-pack-warning-modal.header": {
"message": "Telepítés megerősítése"
},
"unknown-pack-warning-modal.install-anyway": {
"message": "Letöltés mindenképpen"
},
"unknown-pack-warning-modal.malware-statement": {
"message": "A rosszindulatú programokat gyakran modcsomag-fájlokon keresztül terjesztik, például a Discordhoz hasonló platformokon történő megosztás révén."
},
"unknown-pack-warning-modal.warning.body": {
"message": "Ezt a fájlt nem találtuk meg a Modrinthon. Határozottan javasoljuk, hogy kizárólag megbízható forrásokból származó fájlokat telepíts."
},
"unknown-pack-warning-modal.warning.title": {
"message": "Figyelmeztetés ismeretlen fájlról"
}
}
@@ -146,6 +146,9 @@
"app.browse.discover-servers": {
"message": "Temukan server"
},
"app.browse.hide-added-servers": {
"message": "Sembunyikan server yang sudah ditambah"
},
"app.browse.project-type.modpacks": {
"message": "Paket Mod"
},
@@ -167,9 +170,18 @@
"app.export-modal.header": {
"message": "Ekspor paket mod"
},
"app.export-modal.include-file-accessibility-label": {
"message": "Sertakan \"{file}\"?"
},
"app.export-modal.modpack-name-label": {
"message": "Nama Paket Mod"
},
"app.export-modal.modpack-name-placeholder": {
"message": "Nama paket mod"
},
"app.export-modal.select-files-label": {
"message": "Konfigurasikan berkas mana yang disertakan dalam pengeksporan ini"
},
"app.export-modal.version-number-label": {
"message": "Nomor versi"
},
@@ -245,6 +257,9 @@
"app.instance.worlds.search-worlds-placeholder": {
"message": "Cari {count} dunia..."
},
"app.modal.install-to-play.content-required": {
"message": "Konten diperlukan"
},
"app.modal.install-to-play.header": {
"message": "Pasang untuk memainkan"
},
@@ -254,15 +269,27 @@
"app.modal.install-to-play.mod-count": {
"message": "{count, plural, other {# mod}}"
},
"app.modal.install-to-play.required-modpack": {
"message": "Paket mod yang diperlukan"
},
"app.modal.install-to-play.server-requires-mods": {
"message": "Mod diperlukan untuk bermain di server ini. Klik pasang untuk menyiapkan berkas-berkas yang diperlukan dari Modrinth, kemudian luncurkan langsung sari server."
},
"app.modal.install-to-play.shared-instance": {
"message": "Instans terbagi"
},
"app.modal.install-to-play.shared-server-instance": {
"message": "Instans server terbagi"
},
"app.modal.install-to-play.view-contents": {
"message": "Lihat konten"
},
"app.modal.update-to-play.header": {
"message": "Perbarui untuk memainkan"
},
"app.modal.update-to-play.update-required": {
"message": "Perlu diperbarui"
},
"app.modal.update-to-play.update-required-description": {
"message": "{name} perlu diperbarui sebelum dimainkan. Mohon perbarui ke versi terkini untuk meluncurkan permainan."
},
@@ -272,6 +299,9 @@
"app.project.install-context.back-to-browse": {
"message": "Kembali ke halaman pencarian"
},
"app.project.install-context.install-content-to-instance": {
"message": "Pasang konten ke instans"
},
"app.settings.developer-mode-enabled": {
"message": "Mode pengembang dihidupkan."
},
@@ -883,5 +913,26 @@
},
"search.filter.locked.server-loader.title": {
"message": "Pemuat disediakan oleh server"
},
"unknown-pack-warning-modal.body": {
"message": "Berkas hanya akan ditinjau bila ia diunggah ke Modrinth, tak peduli format berkasnya (termasuk .mrpack)."
},
"unknown-pack-warning-modal.dont-show-again": {
"message": "Jangan tampilkan peringatan ini lagi"
},
"unknown-pack-warning-modal.header": {
"message": "Konfirmasi pemasangan"
},
"unknown-pack-warning-modal.install-anyway": {
"message": "Tetap pasang"
},
"unknown-pack-warning-modal.malware-statement": {
"message": "Program jahat (malware) dibagikan melalui berkas paket mod dengan membagikannya melalui platform seperti Discord."
},
"unknown-pack-warning-modal.warning.body": {
"message": "Kami tidak dapat menemukan berkas ini di Modrinth. Kami sangat menyarankan Anda untuk hanya memasang berkas dari sumber-sumber terpercaya."
},
"unknown-pack-warning-modal.warning.title": {
"message": "Peringatan berkas tidak dikenal"
}
}
+40 -214
View File
@@ -11,87 +11,6 @@
"app.action-bar.hide-more-running-instances": {
"message": "Mostra meno istanze in esecuzione"
},
"app.action-bar.install.copied-details": {
"message": "Copiati"
},
"app.action-bar.install.copy-details": {
"message": "Copia dettagli"
},
"app.action-bar.install.dismiss": {
"message": "Ignora"
},
"app.action-bar.install.open-instance": {
"message": "Apri istanza"
},
"app.action-bar.install.retry": {
"message": "Riprova"
},
"app.action-bar.install.summary.app-closing": {
"message": "Annullato per app chiusa"
},
"app.action-bar.install.summary.bad-modpack-file": {
"message": "Impossibile leggere il pacchetto"
},
"app.action-bar.install.summary.canceled": {
"message": "Annullato"
},
"app.action-bar.install.summary.cleanup-incomplete": {
"message": "La pulizia è stata interrotta"
},
"app.action-bar.install.summary.content-download-failed": {
"message": "Impossibile scaricare i file"
},
"app.action-bar.install.summary.corrupt-download": {
"message": "Il file scaricato è corrotto"
},
"app.action-bar.install.summary.could-not-save-files": {
"message": "Impossibile salvare i file"
},
"app.action-bar.install.summary.download-failed": {
"message": "Il download è stato interrotto"
},
"app.action-bar.install.summary.instance-not-found": {
"message": "L'istanza non è stata trovata"
},
"app.action-bar.install.summary.invalid-file-path": {
"message": "Percorso del file errato"
},
"app.action-bar.install.summary.invalid-modpack": {
"message": "Dati del pacchetto non validi"
},
"app.action-bar.install.summary.invalid-modpack-files": {
"message": "Metadati del pacchetto non validi"
},
"app.action-bar.install.summary.java-setup-failed": {
"message": "La configurazione di Java è stata interrotta"
},
"app.action-bar.install.summary.loader-setup-failed": {
"message": "Il loader non è stato configurato"
},
"app.action-bar.install.summary.local-data-error": {
"message": "Impossibile aggiornare i dati locali"
},
"app.action-bar.install.summary.minecraft-setup-failed": {
"message": "Minecraft non è stato configurato"
},
"app.action-bar.install.summary.modrinth-unreachable": {
"message": "Impossibile collegarsi a Modrinth"
},
"app.action-bar.install.summary.no-write-permission": {
"message": "Permessi di scrittura mancanti"
},
"app.action-bar.install.summary.pack-download-failed": {
"message": "Impossibile scaricare il pacchetto"
},
"app.action-bar.install.summary.unexpected-error": {
"message": "Qualcosa è andato storto"
},
"app.action-bar.install.unknown-instance": {
"message": "Istanza sconosciuta"
},
"app.action-bar.installs": {
"message": "Installazioni"
},
"app.action-bar.make-primary-instance": {
"message": "Rendi primaria"
},
@@ -182,12 +101,6 @@
"app.appearance-settings.show-play-time.title": {
"message": "Mostra le ore di gioco"
},
"app.appearance-settings.skip-non-essential-warnings.description": {
"message": "Salta automaticamente le conferme a basso rischio, come l'installazione di pacchetti di mod duplicati, la normale eliminazione di contenuti, gli aggiornamenti simultanei, lo scollegare i pacchetti di mod e le richieste di riparazione. Le avvertenze verranno comunque mostrate."
},
"app.appearance-settings.skip-non-essential-warnings.title": {
"message": "Salta le conferme poco importanti"
},
"app.appearance-settings.toggle-sidebar.description": {
"message": "Mostra o nascondi la barra laterale."
},
@@ -257,72 +170,24 @@
"app.export-modal.header": {
"message": "Esporta pacchetto"
},
"app.export-modal.include-file-accessibility-label": {
"message": "Includere \"{file}\" nell'esportazione?"
},
"app.export-modal.modpack-name-label": {
"message": "Nome del pacchetto"
},
"app.export-modal.modpack-name-placeholder": {
"message": "Nome del pacchetto"
},
"app.export-modal.select-files-label": {
"message": "Seleziona i file da includere in questa esportazione"
},
"app.export-modal.version-number-label": {
"message": "Numero di versione"
},
"app.export-modal.version-number-placeholder": {
"message": "1.0.0"
},
"app.install.phase.downloading_content": {
"message": "Download dei contenuti"
},
"app.install.phase.downloading_minecraft": {
"message": "Download di Minecraft"
},
"app.install.phase.downloading_pack_file": {
"message": "Download del pacchetto"
},
"app.install.phase.extracting_overrides": {
"message": "Estrazione delle sovrascritture"
},
"app.install.phase.finalizing": {
"message": "Finalizzazione"
},
"app.install.phase.preparing_instance": {
"message": "In coda per l'installazione"
},
"app.install.phase.preparing_java": {
"message": "Preparazione di Java"
},
"app.install.phase.preparing_java.downloading": {
"message": "Download di Java {version}"
},
"app.install.phase.preparing_java.extracting": {
"message": "Estrazione di Java {version}"
},
"app.install.phase.preparing_java.fetching-metadata": {
"message": "Recupero di Java {version}"
},
"app.install.phase.preparing_java.resolving": {
"message": "Preparazione di Java {version}"
},
"app.install.phase.preparing_java.validating": {
"message": "Validazione di Java {version}"
},
"app.install.phase.reading_pack_manifest": {
"message": "Lettura del manifesto"
},
"app.install.phase.resolving_loader": {
"message": "Risoluzione del loader"
},
"app.install.phase.resolving_minecraft": {
"message": "Risoluzione di Minecraft"
},
"app.install.phase.resolving_pack": {
"message": "Risoluzione dei contenuti"
},
"app.install.phase.rolling_back": {
"message": "Annullamento"
},
"app.install.phase.running_loader_processors": {
"message": "Esecuzione dei processori del loader"
},
"app.instance.confirm-delete.admonition-body": {
"message": "Tutti i dati della tua istanza verranno eliminati permanentemente, inclusi i tuoi mondi, configurazioni e tutti i contenuti installati."
},
@@ -347,15 +212,6 @@
"app.instance.modpack-already-installed.instance": {
"message": "Istanza"
},
"app.instance.mods.bulk-update.downloading-projects": {
"message": "{current, number}/{total, number} progetti scaricati..."
},
"app.instance.mods.bulk-update.finishing": {
"message": "Finalizzando l'aggiornamento..."
},
"app.instance.mods.bulk-update.resolving-versions": {
"message": "Risolvendo le versioni..."
},
"app.instance.mods.content-type-project": {
"message": "progetto"
},
@@ -377,18 +233,6 @@
"app.instance.worlds.browse-servers": {
"message": "Esplora i server"
},
"app.instance.worlds.delete-world-modal.delete-button": {
"message": "Elimina mondo"
},
"app.instance.worlds.delete-world-modal.title": {
"message": "Eliminare il mondo"
},
"app.instance.worlds.delete-world-modal.warning-body": {
"message": "Questo mondo verrà eliminato permanentemente da questa istanza. Questa azione non può essere annullata."
},
"app.instance.worlds.delete-world-modal.warning-header": {
"message": "Eliminare {name}"
},
"app.instance.worlds.filter-modded": {
"message": "Moddato"
},
@@ -407,53 +251,32 @@
"app.instance.worlds.no-worlds-heading": {
"message": "Nessun server o mondo aggiunto"
},
"app.instance.worlds.remove-server-modal.remove-button": {
"message": "Rimuovi server"
},
"app.instance.worlds.remove-server-modal.title": {
"message": "Rimuovere il server"
},
"app.instance.worlds.remove-server-modal.warning-body": {
"message": "Questo server verrà rimosso dalla tua lista di server nell'app e nel gioco. Potrai aggiungerlo nuovamente se conosci l'indirizzo."
},
"app.instance.worlds.remove-server-modal.warning-header": {
"message": "Rimuovere {name}"
},
"app.instance.worlds.search-worlds-placeholder": {
"message": "Cerca tra {count} mondi..."
},
"app.modal.install-to-play.dont-install": {
"message": "Non installare"
"app.modal.install-to-play.content-required": {
"message": "Contenuto richiesto"
},
"app.modal.install-to-play.header": {
"message": "Installa per continuare"
},
"app.modal.install-to-play.install-anyway": {
"message": "Installa comunque"
},
"app.modal.install-to-play.install-button": {
"message": "Installa"
},
"app.modal.install-to-play.invite-warning": {
"message": "Questo invito è stato creato da un utente e non da Modrinth. Accetta solo gli inviti da persone fidate."
},
"app.modal.install-to-play.mod-count": {
"message": "{count} mod"
},
"app.modal.install-to-play.reviewed-files": {
"message": "Solo i file pubblicati su Modrinth vengono esaminati, qualunque sia il loro formato (.mrpack inclusi)."
"app.modal.install-to-play.required-modpack": {
"message": "Pacchetto di mod richiesto"
},
"app.modal.install-to-play.server-requires-mods": {
"message": "Questo server richiede alcune mod. Clicca Installa per scaricarle direttamente da Modrinth, poi sarai pronto a giocare."
},
"app.modal.install-to-play.shared-instance": {
"message": "Istanza condivisa"
},
"app.modal.install-to-play.unknown-files-description": {
"message": "Questo pacchetto contiene dei file che non sono su Modrinth. Consigliamo di installare file solo da fonti attendibili."
},
"app.modal.install-to-play.unknown-files-warning": {
"message": "File sconosciuti"
},
"app.modal.install-to-play.unrecognized-files": {
"message": "File non riconosciuti"
"app.modal.install-to-play.shared-server-instance": {
"message": "Istanza del server condivisa"
},
"app.modal.install-to-play.view-contents": {
"message": "Mostra contenuti"
@@ -461,8 +284,8 @@
"app.modal.update-to-play.header": {
"message": "Aggiorna per continuare"
},
"app.modal.update-to-play.removed": {
"message": "Rimosso"
"app.modal.update-to-play.update-required": {
"message": "Aggiornamento richiesto"
},
"app.modal.update-to-play.update-required-description": {
"message": "{name} richiede degli aggiornamenti. Installa l'ultima versione per poter giocare."
@@ -470,23 +293,11 @@
"app.project.install-button.already-installed": {
"message": "Questo progetto è già stato installato"
},
"app.project.install-button.switch-version": {
"message": "Cambia versione"
},
"app.project.install-context.back-to-browse": {
"message": "Torna a Esplora"
},
"app.project.version.all-versions": {
"message": "Tutte le versioni"
},
"app.project.version.download-in-browser": {
"message": "Scarica col browser"
},
"app.project.version.installing": {
"message": "Installazione"
},
"app.project.versions.already-installed": {
"message": "Già installate"
"app.project.install-context.install-content-to-instance": {
"message": "Installa il contenuto nell'istanza"
},
"app.settings.developer-mode-enabled": {
"message": "Modalità sviluppatore attiva."
@@ -608,9 +419,6 @@
"app.skins.section.builders-and-biomes": {
"message": "Builders & Biomes"
},
"app.skins.section.chaos-cubed": {
"message": "Chaos Cubed"
},
"app.skins.section.chase-the-skies": {
"message": "Chase the Skies"
},
@@ -1028,9 +836,6 @@
"instance.worlds.copy_address": {
"message": "Copia indirizzo"
},
"instance.worlds.create_shortcut": {
"message": "Crea collegamento"
},
"instance.worlds.dont_show_on_home": {
"message": "Non mostrare nella home"
},
@@ -1105,5 +910,26 @@
},
"search.filter.locked.server-loader.title": {
"message": "Il loader è determinato dal server"
},
"unknown-pack-warning-modal.body": {
"message": "Solo i file caricati su Modrinth vengono esaminati, qualunque sia il loro formato (.mrpack inclusi)."
},
"unknown-pack-warning-modal.dont-show-again": {
"message": "Non mostrare più questo avviso"
},
"unknown-pack-warning-modal.header": {
"message": "Conferma l'installazione"
},
"unknown-pack-warning-modal.install-anyway": {
"message": "Installa comunque"
},
"unknown-pack-warning-modal.malware-statement": {
"message": "Spesso i malware vengono nascosti nei pacchetti di mod, poi distribuiti su piattaforme come Discord."
},
"unknown-pack-warning-modal.warning.body": {
"message": "Non è stato possibile trovare questo file su Modrinth. Consigliamo di installare file solo da fonti attendibili."
},
"unknown-pack-warning-modal.warning.title": {
"message": "Tipo di file sconosciuto"
}
}
+40 -82
View File
@@ -11,51 +11,6 @@
"app.action-bar.hide-more-running-instances": {
"message": "実行中のインスタンスの一部を非表示"
},
"app.action-bar.install.copied-details": {
"message": "コピーしました"
},
"app.action-bar.install.copy-details": {
"message": "詳細をコピー"
},
"app.action-bar.install.dismiss": {
"message": "閉じる"
},
"app.action-bar.install.open-instance": {
"message": "インスタンスを表示"
},
"app.action-bar.install.retry": {
"message": "再試行"
},
"app.action-bar.install.summary.app-closing": {
"message": "アプリの終了によりキャンセルされました"
},
"app.action-bar.install.summary.bad-modpack-file": {
"message": "ModPackを読み込むことができませんでした"
},
"app.action-bar.install.summary.canceled": {
"message": "キャンセルしました"
},
"app.action-bar.install.summary.cleanup-incomplete": {
"message": "クリーンアップが完了しませんでした"
},
"app.action-bar.install.summary.content-download-failed": {
"message": "ファイルをダウンロードできませんでした"
},
"app.action-bar.install.summary.corrupt-download": {
"message": "ダウンロードしたファイルが破損しています"
},
"app.action-bar.install.summary.could-not-save-files": {
"message": "ファイルを保存できませんでした"
},
"app.action-bar.install.summary.download-failed": {
"message": "ダウンロードを完了することができませんでした"
},
"app.action-bar.install.unknown-instance": {
"message": "不明なインスタンス"
},
"app.action-bar.installs": {
"message": "インストール数"
},
"app.action-bar.make-primary-instance": {
"message": "デフォルトのインスタンスを作成"
},
@@ -74,9 +29,6 @@
"app.action-bar.stop-instance": {
"message": "インスタンスを停止"
},
"app.action-bar.update": {
"message": "アップデート"
},
"app.action-bar.view-active-downloads": {
"message": "進行中のダウンロードを表示"
},
@@ -143,12 +95,6 @@
"app.appearance-settings.show-play-time.title": {
"message": "プレイ時間を表示"
},
"app.appearance-settings.skip-non-essential-warnings.description": {
"message": "Modパックの重複インストール、通常のコンテンツ削除、一括更新、Modパックのリンク解除、修復の促しなど、リスクの低い確認画面は自動的にスキップされます。危険な警告は引き続き表示されます。"
},
"app.appearance-settings.skip-non-essential-warnings.title": {
"message": "重要でない警告をスキップ"
},
"app.appearance-settings.toggle-sidebar.description": {
"message": "サイドバーの表示/非表示を切り替える機能を有効にします。"
},
@@ -179,9 +125,6 @@
"app.browse.already-added": {
"message": "すでに追加済み"
},
"app.browse.back-to-instance": {
"message": "インスタンスに戻る"
},
"app.browse.discover-content": {
"message": "コンテンツを探す"
},
@@ -206,6 +149,9 @@
"app.export-modal.header": {
"message": "Modパックを書き出す"
},
"app.export-modal.modpack-name-label": {
"message": "Modパック名"
},
"app.export-modal.modpack-name-placeholder": {
"message": "Modパック名"
},
@@ -215,9 +161,6 @@
"app.export-modal.version-number-placeholder": {
"message": "1.0.0"
},
"app.install.phase.preparing_java.downloading": {
"message": "Java {version} をダウンロードしています"
},
"app.instance.confirm-delete.admonition-body": {
"message": "インスタンス内のすべてのデータは、ワールド、設定、インストール済みのコンテンツを含め、完全に削除されます。"
},
@@ -266,9 +209,6 @@
"app.instance.worlds.browse-servers": {
"message": "サーバーを探す"
},
"app.instance.worlds.delete-world-modal.warning-header": {
"message": "{name} を削除します"
},
"app.instance.worlds.filter-modded": {
"message": "Mod導入済み"
},
@@ -290,6 +230,9 @@
"app.instance.worlds.search-worlds-placeholder": {
"message": "{count} 個のワールドを検索…"
},
"app.modal.install-to-play.content-required": {
"message": "必須コンテンツ"
},
"app.modal.install-to-play.header": {
"message": "インストールしてプレイ"
},
@@ -299,21 +242,30 @@
"app.modal.install-to-play.mod-count": {
"message": "{count, plural, other {#個のMod}}"
},
"app.modal.install-to-play.required-modpack": {
"message": "必須のModパック"
},
"app.modal.install-to-play.server-requires-mods": {
"message": "このサーバーをプレイするにはModが必要です。インストールをクリックしてModrinthから必要なファイルを設定し、サーバーに接続してください。"
},
"app.modal.install-to-play.shared-instance": {
"message": "共有インスタンス"
},
"app.modal.install-to-play.shared-server-instance": {
"message": "共有サーバーインスタンス"
},
"app.modal.install-to-play.view-contents": {
"message": "コンテンツを見る"
},
"app.modal.update-to-play.header": {
"message": "更新してプレイ"
},
"app.modal.update-to-play.update-required": {
"message": "更新が必要です"
},
"app.modal.update-to-play.update-required-description": {
"message": "{name}をプレイするには更新が必要です。ゲームを起動するには最新版に更新してください。"
},
"app.project.version.all-versions": {
"message": "すべてのバージョン"
},
"app.settings.developer-mode-enabled": {
"message": "開発者モードがオンになっています。"
},
@@ -353,17 +305,8 @@
"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": "保存しています…"
"message": "置きかえる"
},
"app.skins.modal.texture-section": {
"message": "テクスチャ"
@@ -623,12 +566,6 @@
"instance.settings.tabs.general.name": {
"message": "名前"
},
"instance.settings.tabs.general.update-channel.beta": {
"message": "ベータ"
},
"instance.settings.tabs.general.update-channel.release": {
"message": "リリース"
},
"instance.settings.tabs.hooks": {
"message": "起動フック"
},
@@ -808,5 +745,26 @@
},
"search.filter.locked.server-loader.title": {
"message": "ローダーはサーバーによる条件です"
},
"unknown-pack-warning-modal.body": {
"message": "ファイル形式に関わらず、Modrinthにアップロードされたファイルのみが確認されます。(.mrpackを含む)"
},
"unknown-pack-warning-modal.dont-show-again": {
"message": "この警告を次回から表示しない"
},
"unknown-pack-warning-modal.header": {
"message": "インストールの確認"
},
"unknown-pack-warning-modal.install-anyway": {
"message": "インストールを続行"
},
"unknown-pack-warning-modal.malware-statement": {
"message": "一般的にマルウェアは、Discord等のプラットフォーム上でModパックファイルを配布して拡散されます"
},
"unknown-pack-warning-modal.warning.body": {
"message": "このファイルをModrinth上で見つけることができませんでした。信頼できるソースからインストールすることを強くお勧めします。"
},
"unknown-pack-warning-modal.warning.title": {
"message": "不明なファイルの警告"
}
}
+43 -244
View File
@@ -3,7 +3,7 @@
"message": "Java {version} 다운로드 중"
},
"app.action-bar.downloading-update": {
"message": "업데이트 다운로드 중"
"message": "업데이트 다운로드중"
},
"app.action-bar.downloads": {
"message": "다운로드"
@@ -11,87 +11,6 @@
"app.action-bar.hide-more-running-instances": {
"message": "실행 중인 인스턴스 숨기기"
},
"app.action-bar.install.copied-details": {
"message": "복사됨"
},
"app.action-bar.install.copy-details": {
"message": "세부 정보 복사"
},
"app.action-bar.install.dismiss": {
"message": "무시"
},
"app.action-bar.install.open-instance": {
"message": "인스턴스 열기"
},
"app.action-bar.install.retry": {
"message": "재시도"
},
"app.action-bar.install.summary.app-closing": {
"message": "앱이 종료되어 취소되었습니다"
},
"app.action-bar.install.summary.bad-modpack-file": {
"message": "모드팩을 읽을 수 없음"
},
"app.action-bar.install.summary.canceled": {
"message": "취소됨"
},
"app.action-bar.install.summary.cleanup-incomplete": {
"message": "파일 정리가 완료되지 않았습니다"
},
"app.action-bar.install.summary.content-download-failed": {
"message": "파일을 다운로드할 수 없습니다"
},
"app.action-bar.install.summary.corrupt-download": {
"message": "다운로드한 파일이 손상되었습니다"
},
"app.action-bar.install.summary.could-not-save-files": {
"message": "파일을 저장할 수 없습니다"
},
"app.action-bar.install.summary.download-failed": {
"message": "다운로드를 완료할 수 없습니다"
},
"app.action-bar.install.summary.instance-not-found": {
"message": "인스턴스를 찾을 수 없습니다"
},
"app.action-bar.install.summary.invalid-file-path": {
"message": "파일 경로가 올바르지 않습니다"
},
"app.action-bar.install.summary.invalid-modpack": {
"message": "모드팩 데이터가 올바르지 않습니다"
},
"app.action-bar.install.summary.invalid-modpack-files": {
"message": "모드팩 파일이 올바르지 않은 메타데이터를 가지고 있습니다"
},
"app.action-bar.install.summary.java-setup-failed": {
"message": "Java 설치를 완료할 수 없습니다"
},
"app.action-bar.install.summary.loader-setup-failed": {
"message": "로더 설치에 실패했습니다"
},
"app.action-bar.install.summary.local-data-error": {
"message": "로컬 데이터를 업데이트할 수 없습니다"
},
"app.action-bar.install.summary.minecraft-setup-failed": {
"message": "Minecraft 설치에 실패했습니다"
},
"app.action-bar.install.summary.modrinth-unreachable": {
"message": "Modrinth에 연결할 수 없습니다"
},
"app.action-bar.install.summary.no-write-permission": {
"message": "쓰기 권한이 없습니다"
},
"app.action-bar.install.summary.pack-download-failed": {
"message": "팩을 다운로드할 수 없습니다"
},
"app.action-bar.install.summary.unexpected-error": {
"message": "무언가 잘못되었습니다"
},
"app.action-bar.install.unknown-instance": {
"message": "알 수 없는 인스턴스"
},
"app.action-bar.installs": {
"message": "설치"
},
"app.action-bar.make-primary-instance": {
"message": "기본 인스턴스로 지정"
},
@@ -104,18 +23,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": "다운로드 목록 보기"
},
@@ -182,12 +95,6 @@
"app.appearance-settings.show-play-time.title": {
"message": "플레이 시간 표시"
},
"app.appearance-settings.skip-non-essential-warnings.description": {
"message": "중복되는 모드팩 설치와 일반적인 콘텐츠 삭제, 동시 업데이트, 모드팩 링크 제거, 프롬프트 복구처럼 리스크가 적은 행위에 대한 확인 절차를 자동으로 넘깁니다. 위험한 경고는 여전히 표시됩니다."
},
"app.appearance-settings.skip-non-essential-warnings.title": {
"message": "중요하지 않은 경고 넘기기"
},
"app.appearance-settings.toggle-sidebar.description": {
"message": "사이드바를 접거나 펼칠 수 있는 기능을 활성화합니다."
},
@@ -234,7 +141,7 @@
"message": "서버 탐색하기"
},
"app.browse.hide-added-servers": {
"message": "이미 추가 서버 숨기기"
"message": "이미 추가 서버 숨기기"
},
"app.browse.project-type.modpacks": {
"message": "모드팩"
@@ -257,72 +164,24 @@
"app.export-modal.header": {
"message": "모드팩 내보내기"
},
"app.export-modal.include-file-accessibility-label": {
"message": "\"{file}\"(을)를 포함할까요?"
},
"app.export-modal.modpack-name-label": {
"message": "모드팩 이름"
},
"app.export-modal.modpack-name-placeholder": {
"message": "모드팩 이름"
},
"app.export-modal.select-files-label": {
"message": "내보내기에 어느 파일이 포함될지 구성"
},
"app.export-modal.version-number-label": {
"message": "버전 구분"
},
"app.export-modal.version-number-placeholder": {
"message": "1.0.0"
},
"app.install.phase.downloading_content": {
"message": "콘텐츠 다운로드 중"
},
"app.install.phase.downloading_minecraft": {
"message": "Minecraft 다운로드 중"
},
"app.install.phase.downloading_pack_file": {
"message": "팩 파일 다운로드 중"
},
"app.install.phase.extracting_overrides": {
"message": "재정의 콘텐츠 추출 중"
},
"app.install.phase.finalizing": {
"message": "마무리 중"
},
"app.install.phase.preparing_instance": {
"message": "설치 대기열에 추가됨"
},
"app.install.phase.preparing_java": {
"message": "Java 준비 중"
},
"app.install.phase.preparing_java.downloading": {
"message": "Java {version} 다운로드 중"
},
"app.install.phase.preparing_java.extracting": {
"message": "Java {version} 추출 중"
},
"app.install.phase.preparing_java.fetching-metadata": {
"message": "Java {version} 가져오는 중"
},
"app.install.phase.preparing_java.resolving": {
"message": "Java {version} 준비 중"
},
"app.install.phase.preparing_java.validating": {
"message": "Java {version} 유효성 검사 중"
},
"app.install.phase.reading_pack_manifest": {
"message": "팩 manifest 읽는 중"
},
"app.install.phase.resolving_loader": {
"message": "로더 해결 중"
},
"app.install.phase.resolving_minecraft": {
"message": "Minecraft 해결 중"
},
"app.install.phase.resolving_pack": {
"message": "콘텐츠 해결 중"
},
"app.install.phase.rolling_back": {
"message": "뒤로 돌리는 중"
},
"app.install.phase.running_loader_processors": {
"message": "로더 진행기 실행 중"
},
"app.instance.confirm-delete.admonition-body": {
"message": "인스턴스의 모든 데이터가 삭제됩니다. 세계 폴더, 설정 폴더 등 설치된 모든 컨텐츠가 포함됩니다."
},
@@ -347,15 +206,6 @@
"app.instance.modpack-already-installed.instance": {
"message": "인스턴스"
},
"app.instance.mods.bulk-update.downloading-projects": {
"message": "{current, number}/{total, number} 프로젝트 다운로드 중..."
},
"app.instance.mods.bulk-update.finishing": {
"message": "업데이트 마무리 중..."
},
"app.instance.mods.bulk-update.resolving-versions": {
"message": "버전 해결 중..."
},
"app.instance.mods.content-type-project": {
"message": "프로젝트"
},
@@ -380,18 +230,6 @@
"app.instance.worlds.browse-servers": {
"message": "서버 탐색하기"
},
"app.instance.worlds.delete-world-modal.delete-button": {
"message": "세계 삭제"
},
"app.instance.worlds.delete-world-modal.title": {
"message": "세계 삭제"
},
"app.instance.worlds.delete-world-modal.warning-body": {
"message": "이 세계는 이 인스턴스로부터 영구적으로 삭제됩니다. 이 작업은 되돌릴 수 없습니다."
},
"app.instance.worlds.delete-world-modal.warning-header": {
"message": "{name} 삭제 중"
},
"app.instance.worlds.filter-modded": {
"message": "수정됨"
},
@@ -410,53 +248,32 @@
"app.instance.worlds.no-worlds-heading": {
"message": "추가된 서버 또는 세계 없음"
},
"app.instance.worlds.remove-server-modal.remove-button": {
"message": "서버 제거"
},
"app.instance.worlds.remove-server-modal.title": {
"message": "서버 제거"
},
"app.instance.worlds.remove-server-modal.warning-body": {
"message": "이 서버는 서버 목록과 게임 내 서버 목록으로부터 삭제됩니다. 주소를 안다면 추후 다시 추가할 수 있습니다."
},
"app.instance.worlds.remove-server-modal.warning-header": {
"message": "{name} 제거 중"
},
"app.instance.worlds.search-worlds-placeholder": {
"message": "{count}개의 세계 검색..."
},
"app.modal.install-to-play.dont-install": {
"message": "설치하지 않음"
"app.modal.install-to-play.content-required": {
"message": "콘텐츠 설치 필요"
},
"app.modal.install-to-play.header": {
"message": "설치하고 플레이"
},
"app.modal.install-to-play.install-anyway": {
"message": "무시하고 설치"
},
"app.modal.install-to-play.install-button": {
"message": "설치"
},
"app.modal.install-to-play.invite-warning": {
"message": "이 초대장은 다른 Modrinth 사용자가 생성했으며, Modrinth에서 생성하지 않았습니다. 신뢰할 수 있는 초대장만 수락하십시오."
},
"app.modal.install-to-play.mod-count": {
"message": "{count, plural, one {모드 #개} other {모드 #개}}"
},
"app.modal.install-to-play.reviewed-files": {
"message": "모든 파일은 형식(.mrpack 포함)에 무관하게 Modrinth에 게시되어야만 검수를 거칩니다."
"app.modal.install-to-play.required-modpack": {
"message": "필요한 모드팩"
},
"app.modal.install-to-play.server-requires-mods": {
"message": "이 서버를 플레이하려면 모드가 필요합니다. '설치'를 클릭하여 Modrinth에서 필수 파일을 내려받은 후, 서버에 바로 접속하세요."
},
"app.modal.install-to-play.shared-instance": {
"message": "인스턴스 공유됨"
},
"app.modal.install-to-play.unknown-files-description": {
"message": "이 서버의 모드팩은 Modrinth에 게시되지 않은 파일을 포함하고 있습니다. 신뢰할 수 있는 출처의 파일만 설치하는 것을 강력히 권장합니다."
},
"app.modal.install-to-play.unknown-files-warning": {
"message": "출처를 알 수 없는 파일"
},
"app.modal.install-to-play.unrecognized-files": {
"message": "확인되지 않은 파일"
"app.modal.install-to-play.shared-server-instance": {
"message": "서버 인스턴스 공유됨"
},
"app.modal.install-to-play.view-contents": {
"message": "구성 요소 보기"
@@ -464,8 +281,8 @@
"app.modal.update-to-play.header": {
"message": "업데이트하고 플레이"
},
"app.modal.update-to-play.removed": {
"message": "제거됨"
"app.modal.update-to-play.update-required": {
"message": "업데이트 필요"
},
"app.modal.update-to-play.update-required-description": {
"message": "{name}을(를) 플레이하려면 업데이트가 필요합니다. 게임을 실행하려면 최신 버전으로 업데이트해 주세요."
@@ -473,12 +290,12 @@
"app.project.install-button.already-installed": {
"message": "이미 설치되어 있음"
},
"app.project.install-button.switch-version": {
"message": "버전 변경"
},
"app.project.install-context.back-to-browse": {
"message": "탐색으로 돌아가기"
},
"app.project.install-context.install-content-to-instance": {
"message": "인스턴스에 콘텐츠 설치"
},
"app.settings.developer-mode-enabled": {
"message": "개발자 모드가 활성화되었습니다."
},
@@ -590,18 +407,9 @@
"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.chaos-cubed": {
"message": "카오스 큐브"
},
"app.skins.section.chase-the-skies": {
"message": "Chase the Skies"
},
@@ -671,9 +479,6 @@
"app.update-popup.download-complete": {
"message": "다운로드 완료"
},
"app.update-popup.reload": {
"message": "새로고침하여 업데이트"
},
"app.update-popup.title": {
"message": "업데이트 가능"
},
@@ -872,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": "실행 후크"
},
@@ -1019,9 +800,6 @@
"instance.worlds.copy_address": {
"message": "주소 복사"
},
"instance.worlds.create_shortcut": {
"message": "바로가기 만들기"
},
"instance.worlds.dont_show_on_home": {
"message": "홈에서 숨기기"
},
@@ -1096,5 +874,26 @@
},
"search.filter.locked.server-loader.title": {
"message": "로더가 서버에 의해 제공됩니다"
},
"unknown-pack-warning-modal.body": {
"message": "모든 파일은 형식(.mrpack 포함)에 무관하게 Modrinth에 업로드되어야만 검수를 거칩니다."
},
"unknown-pack-warning-modal.dont-show-again": {
"message": "이 경고를 다시 표시하지 않음"
},
"unknown-pack-warning-modal.header": {
"message": "설치 확인"
},
"unknown-pack-warning-modal.install-anyway": {
"message": "무시하고 설치"
},
"unknown-pack-warning-modal.malware-statement": {
"message": "악성코드는 흔히 디스코드와 같은 플랫폼을 통해 모드팩 파일을 공유하는 방식으로 유포됩니다."
},
"unknown-pack-warning-modal.warning.body": {
"message": "이 파일을 Modrinth에서 찾을 수 없습니다. 신뢰할 수 있는 출처의 파일만 설치하는 것을 권장합니다."
},
"unknown-pack-warning-modal.warning.title": {
"message": "출처를 알 수 없는 파일 경고"
}
}
+51 -39
View File
@@ -8,12 +8,6 @@
"app.action-bar.hide-more-running-instances": {
"message": "Sembunyikan lebih banyak pemasangan yang berjalan"
},
"app.action-bar.install.dismiss": {
"message": "Ketepikan"
},
"app.action-bar.install.retry": {
"message": "Cuba semula"
},
"app.action-bar.make-primary-instance": {
"message": "Jadikan pemasangan utama"
},
@@ -32,9 +26,6 @@
"app.action-bar.stop-instance": {
"message": "Hentikan pemasangan"
},
"app.action-bar.update": {
"message": "Kemas kini"
},
"app.action-bar.view-active-downloads": {
"message": "Lihat muat turun aktif"
},
@@ -95,9 +86,6 @@
"app.appearance-settings.select-option": {
"message": "Pilih satu pilihan"
},
"app.appearance-settings.skip-non-essential-warnings.description": {
"message": "Melangkau pengesahan berisiko rendah secara automatik seperti pemasangan pek mod pendua, pemadaman kandungan biasa, kemas kini pukal, menyahpautkan pek mod dan gesaan pembaikan. Amaran berbahaya masih akan ditunjukkan."
},
"app.appearance-settings.toggle-sidebar.description": {
"message": "Membolehkan keupayaan untuk menogol bar sisi."
},
@@ -143,6 +131,9 @@
"app.browse.discover-servers": {
"message": "Temui pelayan"
},
"app.browse.hide-added-servers": {
"message": "Sembunyikan pelayan yang sudah ditambah"
},
"app.browse.project-type.modpacks": {
"message": "Pek Mod"
},
@@ -164,33 +155,24 @@
"app.export-modal.header": {
"message": "Eksport pek mod"
},
"app.export-modal.include-file-accessibility-label": {
"message": "Sertakan \"{file}\"?"
},
"app.export-modal.modpack-name-label": {
"message": "Nama Pek Mod"
},
"app.export-modal.modpack-name-placeholder": {
"message": "Nama pek mod"
},
"app.export-modal.select-files-label": {
"message": "Konfigurasikan fail yang disertakan dalam eksport ini"
},
"app.export-modal.version-number-label": {
"message": "Nombor versi"
},
"app.export-modal.version-number-placeholder": {
"message": "1.0.0"
},
"app.install.phase.preparing_java.downloading": {
"message": "Sedang memuat turun Java {version}"
},
"app.install.phase.preparing_java.extracting": {
"message": "Sedang mengekstrak Java {version}"
},
"app.install.phase.preparing_java.fetching-metadata": {
"message": "Sedang mengambil Java {version}"
},
"app.install.phase.preparing_java.resolving": {
"message": "Sedang menyediakan Java {version}"
},
"app.install.phase.preparing_java.validating": {
"message": "Sedang mengesahkan Java {version}"
},
"app.install.phase.reading_pack_manifest": {
"message": "Sedang membaca manifest pek"
},
"app.instance.confirm-delete.admonition-body": {
"message": "Semua data untuk pemasangan anda akan dipadamkan secara kekal, termasuk dunia, konfigurasi dan semua kandungan yang dipasang."
},
@@ -260,6 +242,9 @@
"app.instance.worlds.search-worlds-placeholder": {
"message": "Cari {count} dunia..."
},
"app.modal.install-to-play.content-required": {
"message": "Kandungan yang diperlukan"
},
"app.modal.install-to-play.header": {
"message": "Pasang untuk mainkan"
},
@@ -269,15 +254,27 @@
"app.modal.install-to-play.mod-count": {
"message": "{count, plural, other {# mod}}"
},
"app.modal.install-to-play.required-modpack": {
"message": "Pek mod yang diperlukan"
},
"app.modal.install-to-play.server-requires-mods": {
"message": "Pelayan ini memerlukan mod untuk dimainkan. Klik Pasang untuk menyediakan fail yang diperlukan daripada Modrinth, kemudian lancarkan permainan terus ke dalam pelayan."
},
"app.modal.install-to-play.shared-instance": {
"message": "Pemasangan yang dikongsi"
},
"app.modal.install-to-play.shared-server-instance": {
"message": "Pemasangan pelayan yang dikongsi"
},
"app.modal.install-to-play.view-contents": {
"message": "Lihat kandungan"
},
"app.modal.update-to-play.header": {
"message": "Kemas kini untuk mainkan"
},
"app.modal.update-to-play.update-required": {
"message": "Kemas kini diperlukan"
},
"app.modal.update-to-play.update-required-description": {
"message": "Kemas kini diperlukan untuk memainkan {name}. Sila kemas kini kepada versi terkini untuk melancarkan permainan."
},
@@ -287,6 +284,9 @@
"app.project.install-context.back-to-browse": {
"message": "Kembali menemukan"
},
"app.project.install-context.install-content-to-instance": {
"message": "Pasang kandungan ke dalam pemasangan"
},
"app.settings.developer-mode-enabled": {
"message": "Mod pembangun didayakan."
},
@@ -632,15 +632,6 @@
"instance.settings.tabs.general.name": {
"message": "Nama"
},
"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": "Terbitan"
},
"instance.settings.tabs.hooks": {
"message": "Cangkuk pelancaran"
},
@@ -838,5 +829,26 @@
},
"search.filter.locked.server-loader.title": {
"message": "Pemuat adalah disediakan oleh pelayan"
},
"unknown-pack-warning-modal.body": {
"message": "Sesuatu fail hanya disemak jika ia dimuat naik ke Modrinth, tanpa mengira format failnya (termasuk .mrpack)."
},
"unknown-pack-warning-modal.dont-show-again": {
"message": "Jangan tunjukkan amaran ini lagi"
},
"unknown-pack-warning-modal.header": {
"message": "Sahkan pemasangan"
},
"unknown-pack-warning-modal.install-anyway": {
"message": "Pasangkan juga"
},
"unknown-pack-warning-modal.malware-statement": {
"message": "Perisian hasad sering diedarkan melalui fail pek mod dengan berkongsinya di platform seperti Discord."
},
"unknown-pack-warning-modal.warning.body": {
"message": "Kami tidak dapat menemui fail ini di Modrinth. Kami sangat mengesyorkan anda untuk hanya memasang fail daripada sumber yang anda percayai."
},
"unknown-pack-warning-modal.warning.title": {
"message": "Amaran fail tidak diketahui"
}
}
+51 -213
View File
@@ -11,87 +11,6 @@
"app.action-bar.hide-more-running-instances": {
"message": "Verberg meer actieve instanties"
},
"app.action-bar.install.copied-details": {
"message": "Gekopieerd"
},
"app.action-bar.install.copy-details": {
"message": "Details kopiëren"
},
"app.action-bar.install.dismiss": {
"message": "Sluiten"
},
"app.action-bar.install.open-instance": {
"message": "Instantie openen"
},
"app.action-bar.install.retry": {
"message": "Opnieuw proberen"
},
"app.action-bar.install.summary.app-closing": {
"message": "Geannuleerd vanwege het sluiten van de app"
},
"app.action-bar.install.summary.bad-modpack-file": {
"message": "Modpack kon niet gelezen worden"
},
"app.action-bar.install.summary.canceled": {
"message": "Geannuleerd"
},
"app.action-bar.install.summary.cleanup-incomplete": {
"message": "Opschonen is niet voltooid"
},
"app.action-bar.install.summary.content-download-failed": {
"message": "Bestanden konden niet gedownload worden"
},
"app.action-bar.install.summary.corrupt-download": {
"message": "Gedownloade bestand is beschadigd"
},
"app.action-bar.install.summary.could-not-save-files": {
"message": "Bestanden konden niet opgeslagen worden"
},
"app.action-bar.install.summary.download-failed": {
"message": "Downloaden kon niet worden voltooid"
},
"app.action-bar.install.summary.instance-not-found": {
"message": "Instantie kon niet gevonden worden"
},
"app.action-bar.install.summary.invalid-file-path": {
"message": "Bestandspad is ongeldig"
},
"app.action-bar.install.summary.invalid-modpack": {
"message": "Modpack-gegevens ongeldig"
},
"app.action-bar.install.summary.invalid-modpack-files": {
"message": "Modpack-bestanden bevatten ongeldige metagegevens"
},
"app.action-bar.install.summary.java-setup-failed": {
"message": "Installatie van Java kon niet worden voltooid"
},
"app.action-bar.install.summary.loader-setup-failed": {
"message": "Instellen van loader is mislukt"
},
"app.action-bar.install.summary.local-data-error": {
"message": "Lokale gegevens konden niet bijgewerkt worden"
},
"app.action-bar.install.summary.minecraft-setup-failed": {
"message": "Instellen van Minecraft is mislukt"
},
"app.action-bar.install.summary.modrinth-unreachable": {
"message": "Modrinth kon niet bereikt worden"
},
"app.action-bar.install.summary.no-write-permission": {
"message": "Geen schrijfrechten"
},
"app.action-bar.install.summary.pack-download-failed": {
"message": "Pack kon niet gedownload worden"
},
"app.action-bar.install.summary.unexpected-error": {
"message": "Er is iets misgegaan"
},
"app.action-bar.install.unknown-instance": {
"message": "Onbekende instantie"
},
"app.action-bar.installs": {
"message": "Installaties"
},
"app.action-bar.make-primary-instance": {
"message": "Maak primaire instantie"
},
@@ -114,7 +33,7 @@
"message": "Stop instantie"
},
"app.action-bar.update": {
"message": "Bijwerken"
"message": "Updaten"
},
"app.action-bar.view-active-downloads": {
"message": "Bekijk actieve downloads"
@@ -141,7 +60,7 @@
"message": "Wijzig de pagina waarop de launcher opent."
},
"app.appearance-settings.default-landing-page.home": {
"message": "Start"
"message": "Home"
},
"app.appearance-settings.default-landing-page.library": {
"message": "Bibliotheek"
@@ -182,12 +101,6 @@
"app.appearance-settings.show-play-time.title": {
"message": "Speeltijd laten zien"
},
"app.appearance-settings.skip-non-essential-warnings.description": {
"message": "Bevestigingen met een laag risico, zoals het installeren van dubbele modpacks, het verwijderen van normale inhoud, bulkupdates, het ontkoppelen van modpacks en reparatieverzoeken, worden automatisch overgeslagen. Waarschuwingen voor gevaarlijke dingen worden nog steeds weergegeven."
},
"app.appearance-settings.skip-non-essential-warnings.title": {
"message": "Niet-essentiële waarschuwingen overslaan"
},
"app.appearance-settings.toggle-sidebar.description": {
"message": "Schakelt de mogelijkheid om de zijbalk in- of uit te schakelen in."
},
@@ -228,19 +141,19 @@
"message": "Terug naar instantie"
},
"app.browse.discover-content": {
"message": "Inhoud ontdekken"
"message": "Ontdek content"
},
"app.browse.discover-servers": {
"message": "Ontdek servers"
},
"app.browse.hide-added-servers": {
"message": "Al toegevoegde servers verbergen"
"message": "Verberg al toegevoegde servers"
},
"app.browse.project-type.modpacks": {
"message": "Modpacks"
},
"app.browse.server-instance-content-warning": {
"message": "Het toevoegen van inhoud kan de compatibiliteit verstoren bij het verbinden met de server. Eventuele toegevoegde inhoud gaat bovendien verloren wanneer je de inhoud van de serverinstantie bijwerkt."
"message": "Het toevoegen van content kan de compatibiliteit verbreken bij het verbinden met de server. Alle toegevoegde inhoud gaat ook verloren wanneer u de content van de serverinstantie bijwerkt."
},
"app.browse.server.installing": {
"message": "Installeren"
@@ -257,74 +170,26 @@
"app.export-modal.header": {
"message": "Exporteer modpack"
},
"app.export-modal.include-file-accessibility-label": {
"message": "Betrek \"{file}\"?"
},
"app.export-modal.modpack-name-label": {
"message": "Naam van modpack"
"message": "Modpack Naam"
},
"app.export-modal.modpack-name-placeholder": {
"message": "Modpack naam"
},
"app.export-modal.select-files-label": {
"message": "Configureer welke bestanden bij deze export zijn betrokken"
},
"app.export-modal.version-number-label": {
"message": "Versie nummer"
},
"app.export-modal.version-number-placeholder": {
"message": "1.0.0"
},
"app.install.phase.downloading_content": {
"message": "Inhoud aan het downloaden"
},
"app.install.phase.downloading_minecraft": {
"message": "Minecraft aan het downloaden"
},
"app.install.phase.downloading_pack_file": {
"message": "Pakketbestand aan het downloaden"
},
"app.install.phase.extracting_overrides": {
"message": "Overschrijvingen eruit halen"
},
"app.install.phase.finalizing": {
"message": "Aan het afronden"
},
"app.install.phase.preparing_instance": {
"message": "In de wachtrij voor installatie"
},
"app.install.phase.preparing_java": {
"message": "Java aan het voorbereiden"
},
"app.install.phase.preparing_java.downloading": {
"message": "Java {version} aan het downloaden"
},
"app.install.phase.preparing_java.extracting": {
"message": "Java {version} aan het uitpakken"
},
"app.install.phase.preparing_java.fetching-metadata": {
"message": "Java {version} aan het ophalen"
},
"app.install.phase.preparing_java.resolving": {
"message": "Java {version} aan het voorbereiden"
},
"app.install.phase.preparing_java.validating": {
"message": "Java {version} aan het valideren"
},
"app.install.phase.reading_pack_manifest": {
"message": "Paketmanifest aan het lezen"
},
"app.install.phase.resolving_loader": {
"message": "Loader wordt bepaald"
},
"app.install.phase.resolving_minecraft": {
"message": "Minecraft wordt bepaald"
},
"app.install.phase.resolving_pack": {
"message": "Inhoud aan het bepalen"
},
"app.install.phase.rolling_back": {
"message": "Aan het terugdraaien"
},
"app.install.phase.running_loader_processors": {
"message": "Loader-processors aan het uitvoeren"
},
"app.instance.confirm-delete.admonition-body": {
"message": "Alle gegevens van je instantie worden definitief verwijderd, inclusief je werelden, configuraties en alle geïnstalleerde inhoud."
"message": "Alle data van je instantie zal permanent verwijderd worden, inclusief al je werelden, voorkeuren, en alle geïnstalleerde content."
},
"app.instance.confirm-delete.admonition-header": {
"message": "Deze actie kan niet ongedaan gemaakt worden"
@@ -347,15 +212,6 @@
"app.instance.modpack-already-installed.instance": {
"message": "Instantie"
},
"app.instance.mods.bulk-update.downloading-projects": {
"message": "{current, number}/{total, number} projecten aan het downloaden..."
},
"app.instance.mods.bulk-update.finishing": {
"message": "Update aan het afronden..."
},
"app.instance.mods.bulk-update.resolving-versions": {
"message": "Versies worden bepaald..."
},
"app.instance.mods.content-type-project": {
"message": "project"
},
@@ -380,18 +236,6 @@
"app.instance.worlds.browse-servers": {
"message": "Zoek servers"
},
"app.instance.worlds.delete-world-modal.delete-button": {
"message": "Wereld verwijderen"
},
"app.instance.worlds.delete-world-modal.title": {
"message": "Wereld verwijderen"
},
"app.instance.worlds.delete-world-modal.warning-body": {
"message": "Deze wereld wordt definitief uit deze instantie verwijderd. Deze actie kan niet ongedaan worden gemaakt."
},
"app.instance.worlds.delete-world-modal.warning-header": {
"message": "{name} verwijderen"
},
"app.instance.worlds.filter-modded": {
"message": "Gemod"
},
@@ -410,62 +254,41 @@
"app.instance.worlds.no-worlds-heading": {
"message": "Noch servers noch werelden toegevoegd"
},
"app.instance.worlds.remove-server-modal.remove-button": {
"message": "Server verwijderen"
},
"app.instance.worlds.remove-server-modal.title": {
"message": "Server verwijderen"
},
"app.instance.worlds.remove-server-modal.warning-body": {
"message": "Deze server wordt verwijderd uit je serverlijst en uit de serverlijst in het spel. Je kunt hem later weer toevoegen als je het adres weet."
},
"app.instance.worlds.remove-server-modal.warning-header": {
"message": "{name} verwijderen"
},
"app.instance.worlds.search-worlds-placeholder": {
"message": "Zoek werelden"
},
"app.modal.install-to-play.dont-install": {
"message": "Niet installeren"
"app.modal.install-to-play.content-required": {
"message": "Content vereist"
},
"app.modal.install-to-play.header": {
"message": "Installeer om te spelen"
},
"app.modal.install-to-play.install-anyway": {
"message": "Toch installeren"
},
"app.modal.install-to-play.install-button": {
"message": "Installeer"
},
"app.modal.install-to-play.invite-warning": {
"message": "Deze uitnodiging is aangemaakt door een andere Modrinth-gebruiker, niet door Modrinth zelf. Accepteer alleen uitnodigingen van mensen die je vertrouwt."
},
"app.modal.install-to-play.mod-count": {
"message": "{count, plural,one {# mod}other {# mods}}"
},
"app.modal.install-to-play.reviewed-files": {
"message": "Een bestand wordt alleen beoordeeld als het op Modrinth wordt gepubliceerd, ongeacht het bestandsformaat (inclusief .mrpack)."
"app.modal.install-to-play.required-modpack": {
"message": "Vereist modpack"
},
"app.modal.install-to-play.server-requires-mods": {
"message": "Deze server vereist mods om te spelen. Klik op Installeer om de vereiste bestanden van Modrinth in te stellen, en start direct in de server."
},
"app.modal.install-to-play.shared-instance": {
"message": "Gedeelde instantie"
},
"app.modal.install-to-play.unknown-files-description": {
"message": "Dit server-modpack bevat bestanden die niet op Modrinth zijn gepubliceerd. We raden je ten zeerste aan om alleen bestanden te installeren uit bronnen die je vertrouwt."
},
"app.modal.install-to-play.unknown-files-warning": {
"message": "Waarschuwing voor onbekende bestanden"
},
"app.modal.install-to-play.unrecognized-files": {
"message": "Onbekende bestanden"
"app.modal.install-to-play.shared-server-instance": {
"message": "Gedeelde server instantie"
},
"app.modal.install-to-play.view-contents": {
"message": "Inhoud bekijken"
"message": "Toon content"
},
"app.modal.update-to-play.header": {
"message": "Update om te spelen"
},
"app.modal.update-to-play.removed": {
"message": "Verwijderde"
"app.modal.update-to-play.update-required": {
"message": "Update vereist"
},
"app.modal.update-to-play.update-required-description": {
"message": "Een update is vereist om {name} te spelen. Update naar de laatste versie om het spel te starten."
@@ -473,12 +296,12 @@
"app.project.install-button.already-installed": {
"message": "Dit project is al geïnstalleerd"
},
"app.project.install-button.switch-version": {
"message": "Versie wijzigen"
},
"app.project.install-context.back-to-browse": {
"message": "Terug naar Ontdekken"
},
"app.project.install-context.install-content-to-instance": {
"message": "Installeer content naar instantie"
},
"app.settings.developer-mode-enabled": {
"message": "Ontwikkelaarsmodus ingeschakeld."
},
@@ -594,14 +417,11 @@
"message": "Je skinvolgorde kon niet worden opgeslagen."
},
"app.skins.reorder-error.title": {
"message": "Herschikken van skins is mislukt"
"message": "Het herschikken van skins is mislukt."
},
"app.skins.section.builders-and-biomes": {
"message": "Builders & Biomes"
},
"app.skins.section.chaos-cubed": {
"message": "Chaos Cubed"
},
"app.skins.section.chase-the-skies": {
"message": "Chase the Skies"
},
@@ -828,7 +648,7 @@
"message": "Verwijder instantie"
},
"instance.settings.tabs.general.delete.description": {
"message": "Hiermee wordt een instantie definitief van je apparaat verwijderd, inclusief je werelden, instellingen en alle geïnstalleerde inhoud. Wees voorzichtig, want zodra je een instantie hebt verwijderd, kun je deze niet meer herstellen."
"message": "Verwijdert permanent een instantie van je apparaat, inclusief je werelden, instellingen en alle geïnstalleerde inhoud. Wees voorzichtig, zodra je een instantie verwijdert, is het niet meer mogelijk om deze te herstellen."
},
"instance.settings.tabs.general.deleting.button": {
"message": "Verwijderen..."
@@ -1019,9 +839,6 @@
"instance.worlds.copy_address": {
"message": "Kopieer adres"
},
"instance.worlds.create_shortcut": {
"message": "Snelkoppeling maken"
},
"instance.worlds.dont_show_on_home": {
"message": "Niet tonen op de startpagina"
},
@@ -1096,5 +913,26 @@
},
"search.filter.locked.server-loader.title": {
"message": "Loader is gegeven door de server"
},
"unknown-pack-warning-modal.body": {
"message": "Een bestand wordt alleen beoordeeld als het naar Modrinth is geüpload, ongeacht het bestandsformaat (inclusief .mrpack)."
},
"unknown-pack-warning-modal.dont-show-again": {
"message": "Toon deze waarschuwing niet opnieuw"
},
"unknown-pack-warning-modal.header": {
"message": "Bevestig installatie"
},
"unknown-pack-warning-modal.install-anyway": {
"message": "Installeer toch"
},
"unknown-pack-warning-modal.malware-statement": {
"message": "Malware wordt vaak verspreid via modpack-bestanden door ze te delen op platforms zoals Discord."
},
"unknown-pack-warning-modal.warning.body": {
"message": "We konden dit bestand niet vinden op Modrinth. We raden ten zeerste aan om alleen bestanden te installeren van bronnen die u vertrouwt."
},
"unknown-pack-warning-modal.warning.title": {
"message": "Waarschuwing voor onbekend bestand"
}
}
+18 -225
View File
@@ -1,220 +1,10 @@
{
"app.action-bar.downloading-java": {
"message": "Laster ned Java {version}"
},
"app.action-bar.downloading-update": {
"message": "Laster ned oppdatering"
},
"app.action-bar.downloads": {
"message": "Nedlastninger"
},
"app.action-bar.hide-more-running-instances": {
"message": "Skjul flere kjørende tilfeller"
},
"app.action-bar.install.open-instance": {
"message": "Åpne tilfelle"
},
"app.action-bar.install.retry": {
"message": "Prøv igjen"
},
"app.action-bar.install.unknown-instance": {
"message": "Ukjent tilfelle"
},
"app.action-bar.installs": {
"message": "Installeringer"
},
"app.action-bar.make-primary-instance": {
"message": "Gjør om til primærtilfelle"
},
"app.action-bar.no-instances-running": {
"message": "Ingen kjørende tilfeller"
},
"app.action-bar.offline": {
"message": "Uten nett"
},
"app.action-bar.primary-instance": {
"message": "Primærtilfelle"
},
"app.action-bar.reload-to-update": {
"message": "Last inn på nytt for å oppdatere"
},
"app.action-bar.show-more-running-instances": {
"message": "Vis flere kjørende tilfeller"
},
"app.action-bar.stop-instance": {
"message": "Stopp tilfelle"
},
"app.action-bar.update": {
"message": "Oppdater"
},
"app.action-bar.view-active-downloads": {
"message": "Vis aktive nedlastninger"
},
"app.action-bar.view-instance": {
"message": "Vis tilfelle"
},
"app.action-bar.view-logs": {
"message": "Vis logger"
},
"app.appearance-settings.advanced-rendering.description": {
"message": "Skrur på avansert rendering, som for eksempel uskarphetseffekter, som kan forårsake ytelsesproblemer om man ikke har maskinvareaksellerert rendering."
},
"app.appearance-settings.advanced-rendering.title": {
"message": "Avansert rendering"
},
"app.appearance-settings.color-theme.description": {
"message": "Velg ditt foretrukne fargetema for Modrinth App."
},
"app.appearance-settings.color-theme.title": {
"message": "Fargetema"
},
"app.appearance-settings.default-landing-page.description": {
"message": "Endre sida som vises når launcheren åpnes."
},
"app.appearance-settings.default-landing-page.home": {
"message": "Hjem"
},
"app.appearance-settings.default-landing-page.library": {
"message": "Bibliotek"
},
"app.appearance-settings.default-landing-page.title": {
"message": "Åpningsside"
},
"app.appearance-settings.hide-nametag.description": {
"message": "Skjuler navnelappen over din spiller på utseende siden."
},
"app.appearance-settings.hide-nametag.title": {
"message": "Gjøm navnelapp"
},
"app.appearance-settings.minimize-launcher.title": {
"message": "Minimer launcheren"
},
"app.appearance-settings.select-option": {
"message": "Velg et alternativ"
},
"app.appearance-settings.show-play-time.description": {
"message": "Viser hvor lenge du har spilt et tilfelle."
},
"app.appearance-settings.show-play-time.title": {
"message": "Vis spilletid"
},
"app.appearance-settings.skip-non-essential-warnings.description": {
"message": "Hopper automatisk over lav-rissiko fastslåelser som modpakkeinstalasjonsduplikat, vanlig innholdsletting, bolkoppdateringer, avkoble modpakker, og reparasjonsprompter. Farlige advarsler vil fortsatt vises."
},
"app.appearance-settings.skip-non-essential-warnings.title": {
"message": "Hopp over mindre viktige advarsler"
},
"app.appearance-settings.toggle-sidebar.description": {
"message": "Aktiverer evnen til å skru sidefeltet av/på."
},
"app.appearance-settings.toggle-sidebar.title": {
"message": "Sidefeltsveksling"
},
"app.appearance-settings.unknown-pack-warning.description": {
"message": "Hvis du prøver å laste ned en Modrinth Pakke fil (.mrpack) som er ukjent for Modrinth så kommer vi til å forsikre oss om at du forstår risikoene før du installerer den."
},
"app.appearance-settings.unknown-pack-warning.title": {
"message": "Advar meg før jeg installerer ukjente modpakker"
},
"app.auth-servers.unreachable.body": {
"message": "Minecraft-autentiseringsservere kan være nede for øyeblikket. Kontroller internettforbindelsen din og prøv igjen senere."
},
"app.auth-servers.unreachable.header": {
"message": "Kan ikke nå autentiseringsservere"
},
"app.browse.add-servers-to-instance": {
"message": "Legger til server til tilfelle"
},
"app.browse.add-to-an-instance": {
"message": "Legg til til et tilfelle"
},
"app.browse.add-to-instance": {
"message": "Legg til til tilfelle"
},
"app.browse.add-to-instance-name": {
"message": "Legg til til {instanceName}"
},
"app.browse.added": {
"message": "Lagt til"
},
"app.browse.already-added": {
"message": "Allerede lagt til"
},
"app.browse.back-to-instance": {
"message": "Tilbake til tilfelle"
},
"app.browse.discover-content": {
"message": "Oppdag innhold"
},
"app.browse.discover-servers": {
"message": "Oppdag servere"
},
"app.browse.hide-added-servers": {
"message": "Gjem servere som allerede er lagt til"
},
"app.browse.project-type.modpacks": {
"message": "Modpakker"
},
"app.browse.server-instance-content-warning": {
"message": "Å legge til innhold kan ødelegge komatibilitet når du blir med i serveren. Alt tilleggsinnhold vil også forsvinne når du oppdaterer servertilfelleinnholdet."
},
"app.browse.server.installing": {
"message": "Installerer"
},
"app.content-install.no-compatible-versions": {
"message": "Ingen tilgjengelige versjoner matcher {compatibilityLabel}. Velg en versjon for å installere uansett. Avhengigheter vil ikke bli installert automatisk."
},
"app.export-modal.description-placeholder": {
"message": "Skriv en modpakkebeskrivelse..."
},
"app.export-modal.export-button": {
"message": "Eksperter"
},
"app.export-modal.header": {
"message": "Eksperter modpakke"
},
"app.export-modal.modpack-name-label": {
"message": "Navn på modpakke"
},
"app.install.phase.preparing_java.validating": {
"message": "Validerer Java {version}"
},
"app.install.phase.reading_pack_manifest": {
"message": "Leser pakkemanifest"
},
"app.instance.confirm-delete.delete-button": {
"message": "Slett tilfelle"
},
"app.instance.confirm-delete.header": {
"message": "Slett tilfelle"
},
"app.instance.modpack-already-installed.create": {
"message": "Opprett"
},
"app.instance.modpack-already-installed.header": {
"message": "Modpakka er allerede installert"
},
"app.instance.modpack-already-installed.instance": {
"message": "Tilfelle"
},
"app.instance.mods.bulk-update.downloading-projects": {
"message": "Laster ned {current, number}/{total, number} prosjekter..."
},
"app.instance.mods.bulk-update.finishing": {
"message": "Fullfører oppdatering..."
},
"app.instance.mods.content-type-project": {
"message": "prosjekt"
},
"app.instance.mods.project-was-added": {
"message": "\"{name}\" ble lagt til"
},
"app.instance.mods.projects-were-added": {
"message": "{count} prosjekter ble lagt til"
},
"app.instance.mods.share-text": {
"message": "Sjekk ut prosjektene jeg bruker i min modpakke!"
},
"app.modal.install-to-play.header": {
"message": "Installer for å spille"
},
@@ -222,14 +12,20 @@
"message": "Installer"
},
"app.modal.install-to-play.mod-count": {
"message": "{count, plural, one {#mod} other {# modder}}"
"message": "{count, plural, one {#mod} other {# mods}}"
},
"app.modal.install-to-play.shared-instance": {
"message": "Delt tilfelle"
"message": "Delt instans"
},
"app.modal.install-to-play.shared-server-instance": {
"message": "Delt serverinstans"
},
"app.modal.update-to-play.header": {
"message": "Oppdater for å spille"
},
"app.modal.update-to-play.update-required": {
"message": "Krever oppdatering"
},
"app.modal.update-to-play.update-required-description": {
"message": "Ei oppdatering er påkrevd for å spille {name}. Vær så snill å oppdater til den siste versjonen av spillet for å spille det."
},
@@ -243,7 +39,7 @@
"message": "Utseende"
},
"app.settings.tabs.default-instance-options": {
"message": "Vanlige tilfelleinstillinger"
"message": "Standard instansinstillinger"
},
"app.settings.tabs.java-installations": {
"message": "Java installasjoner"
@@ -306,10 +102,10 @@
"message": "Aktiv"
},
"friends.heading.offline": {
"message": "Av"
"message": "Offline"
},
"friends.heading.online": {
"message": ""
"message": "Online"
},
"friends.heading.pending": {
"message": "Ventende"
@@ -450,10 +246,10 @@
"message": "Før oppstart"
},
"instance.settings.tabs.hooks.pre-launch.description": {
"message": "Kjøres før tilfellet startes."
"message": "Kørt før instansen startes."
},
"instance.settings.tabs.hooks.pre-launch.enter": {
"message": "Skriv inn forstartskommando..."
"message": "Skriv inn kommando før start..."
},
"instance.settings.tabs.hooks.title": {
"message": "Spillstartkroker"
@@ -462,7 +258,7 @@
"message": "Wrapper"
},
"instance.settings.tabs.hooks.wrapper.description": {
"message": "Innpakningskommando for å starte Minecraft."
"message": "Wrapper-kommando for å starte Minecraft."
},
"instance.settings.tabs.hooks.wrapper.enter": {
"message": "Skriv inn wrapper-kommando..."
@@ -476,9 +272,6 @@
"instance.settings.tabs.java": {
"message": "Java og minne"
},
"instance.settings.tabs.java.custom-memory-allocation": {
"message": "Egendefinert minneallokasjon"
},
"instance.settings.tabs.java.environment-variables": {
"message": "Miljøvariabler"
},
@@ -510,7 +303,7 @@
"message": "Høyde"
},
"instance.settings.tabs.window.height.description": {
"message": "Høyden til spillvinduet når spillet startes."
"message": "Høyde av spillvindu når spillet startes."
},
"instance.settings.tabs.window.height.enter": {
"message": "Angi høyde..."
@@ -519,7 +312,7 @@
"message": "Bredde"
},
"instance.settings.tabs.window.width.description": {
"message": "Bredden til spillvinduet når spillet startes."
"message": "Bredde av spillvindu når spillet startes."
},
"instance.settings.tabs.window.width.enter": {
"message": "Angi bredde..."
@@ -537,7 +330,7 @@
"message": "Ikke vis på Hjem"
},
"instance.worlds.game_already_open": {
"message": "Tilfellet er allerede open"
"message": "Instansen er allerede open"
},
"instance.worlds.hardcore": {
"message": "Hardbarket modus"
@@ -555,7 +348,7 @@
"message": "Du kan bare hoppe rett inn i enkeltspiller-verdener på Minecraft 1.20+"
},
"instance.worlds.play_instance": {
"message": "Spill tilfelle"
"message": "Spill instans"
},
"instance.worlds.view_instance": {
"message": "Vis instans"
+61 -235
View File
@@ -9,94 +9,13 @@
"message": "Pobrania"
},
"app.action-bar.hide-more-running-instances": {
"message": "Ukryj aktywne instancje"
},
"app.action-bar.install.copied-details": {
"message": "Skopiowano"
},
"app.action-bar.install.copy-details": {
"message": "Kopiuj szczegóły"
},
"app.action-bar.install.dismiss": {
"message": "Odrzuć"
},
"app.action-bar.install.open-instance": {
"message": "Otwórz instancję"
},
"app.action-bar.install.retry": {
"message": "Spróbuj ponownie"
},
"app.action-bar.install.summary.app-closing": {
"message": "Anulowano z powodu zamknięcia aplikacji"
},
"app.action-bar.install.summary.bad-modpack-file": {
"message": "Nie można było odczytać paczki modów"
},
"app.action-bar.install.summary.canceled": {
"message": "Anulowano"
},
"app.action-bar.install.summary.cleanup-incomplete": {
"message": "Czyszczenie nie powiodło się"
},
"app.action-bar.install.summary.content-download-failed": {
"message": "Nie udało się pobrać plików"
},
"app.action-bar.install.summary.corrupt-download": {
"message": "Pobrany plik jest uszkodzony"
},
"app.action-bar.install.summary.could-not-save-files": {
"message": "Nie udało się zapisać plików"
},
"app.action-bar.install.summary.download-failed": {
"message": "Nie można było ukończyć pobierania"
},
"app.action-bar.install.summary.instance-not-found": {
"message": "Nie znaleziono instancji"
},
"app.action-bar.install.summary.invalid-file-path": {
"message": "Ścieżka plików jest niewłaściwa"
},
"app.action-bar.install.summary.invalid-modpack": {
"message": "Niewłaściwe dane paczki modów"
},
"app.action-bar.install.summary.invalid-modpack-files": {
"message": "Pliki paczki modów zawierają niewłaściwe metadane"
},
"app.action-bar.install.summary.java-setup-failed": {
"message": "Ustawianie Javy nie powiodło się"
},
"app.action-bar.install.summary.loader-setup-failed": {
"message": "Ustawianie loadera nie powiodło się"
},
"app.action-bar.install.summary.local-data-error": {
"message": "Nie udało się zaktualizować danych lokalnych"
},
"app.action-bar.install.summary.minecraft-setup-failed": {
"message": "Ustawianie Minecraft nie powiodło się"
},
"app.action-bar.install.summary.modrinth-unreachable": {
"message": "Nie udało się połączyć z serwerami Modrinth"
},
"app.action-bar.install.summary.no-write-permission": {
"message": "Brak permisji, aby zapisać pliki"
},
"app.action-bar.install.summary.pack-download-failed": {
"message": "Pobieranie paczki nie powiodło się"
},
"app.action-bar.install.summary.unexpected-error": {
"message": "Coś poszło nie tak"
},
"app.action-bar.install.unknown-instance": {
"message": "Nieznana instancja"
},
"app.action-bar.installs": {
"message": "Instalacje"
"message": "Ukryj włączone instancje"
},
"app.action-bar.make-primary-instance": {
"message": "Ustaw jako główną instancję"
},
"app.action-bar.no-instances-running": {
"message": "Brak włączonych instancji"
"message": "Żadna instancja nie jest włączona"
},
"app.action-bar.offline": {
"message": "Offline"
@@ -105,7 +24,7 @@
"message": "Główna instancja"
},
"app.action-bar.reload-to-update": {
"message": "Załaduj ponownie, aby zaktualizować"
"message": "Załaduj ponownie, by zaktualizować"
},
"app.action-bar.show-more-running-instances": {
"message": "Pokaż więcej włączonych instancji"
@@ -132,7 +51,7 @@
"message": "Zaawansowane renderowanie"
},
"app.appearance-settings.color-theme.description": {
"message": "Wybierz preferowany motyw kolorystyczny dla Modrinth App."
"message": "Wybierz preferowany motyw w Modrinth App."
},
"app.appearance-settings.color-theme.title": {
"message": "Motyw"
@@ -150,10 +69,10 @@
"message": "Domyślna strona główna"
},
"app.appearance-settings.hide-nametag.description": {
"message": "Wyłącza nazwę użytkownika nad modelem gracza na stronie skórek."
"message": "Wyłącza nazwę użytkownika na stronie skórek."
},
"app.appearance-settings.hide-nametag.title": {
"message": "Ukryj nazwę"
"message": "Schowaj nazwę"
},
"app.appearance-settings.jump-back-into-worlds.description": {
"message": "Pokazuje ostatnie światy w sekcji \"Wskocz z powrotem do światów\" na stronie głównej."
@@ -182,12 +101,6 @@
"app.appearance-settings.show-play-time.title": {
"message": "Pokaż czas gry"
},
"app.appearance-settings.skip-non-essential-warnings.description": {
"message": "Automatycznie pomija mało znaczące potwierdzenia takie jak duplikaty instalacji paczek modów, usuwanie zwykłej zawartości, masowe aktualizacje, odłączanie paczek modów i naprawianie. Ostrzeżenia o potencjalnie niebezpiecznych akcjach będą nadal pokazywane."
},
"app.appearance-settings.skip-non-essential-warnings.title": {
"message": "Pomiń mało znaczące ostrzeżenia"
},
"app.appearance-settings.toggle-sidebar.description": {
"message": "Umożliwia przełączenie paska bocznego."
},
@@ -257,77 +170,29 @@
"app.export-modal.header": {
"message": "Eksportuj paczkę modów"
},
"app.export-modal.include-file-accessibility-label": {
"message": "Dołączyć \"{file}\"?"
},
"app.export-modal.modpack-name-label": {
"message": "Nazwa paczki modów"
},
"app.export-modal.modpack-name-placeholder": {
"message": "Nazwa paczki modów"
},
"app.export-modal.select-files-label": {
"message": "Skonfiguruje jakie pliki zostaną załączone w tym eksporcie"
},
"app.export-modal.version-number-label": {
"message": "Numer wersji"
},
"app.export-modal.version-number-placeholder": {
"message": "1.0.0"
},
"app.install.phase.downloading_content": {
"message": "Pobieranie zawartości"
},
"app.install.phase.downloading_minecraft": {
"message": "Pobieranie Minecraft"
},
"app.install.phase.downloading_pack_file": {
"message": "Pobieranie pliku paczki"
},
"app.install.phase.extracting_overrides": {
"message": "Wyodrębnianie nadpisań"
},
"app.install.phase.finalizing": {
"message": "Finalizowanie"
},
"app.install.phase.preparing_instance": {
"message": "Oczekuje na instalację"
},
"app.install.phase.preparing_java": {
"message": "Przygotowywanie środowiska Java"
},
"app.install.phase.preparing_java.downloading": {
"message": "Pobieranie Java {version}"
},
"app.install.phase.preparing_java.extracting": {
"message": "Wypakowywanie Java {version}"
},
"app.install.phase.preparing_java.fetching-metadata": {
"message": "Pobieranie Java {version}"
},
"app.install.phase.preparing_java.resolving": {
"message": "Przygotowywanie Java {version}"
},
"app.install.phase.preparing_java.validating": {
"message": "Sprawdzanie Java {version}"
},
"app.install.phase.reading_pack_manifest": {
"message": "Sprawdzanie manifestu paczki"
},
"app.install.phase.resolving_loader": {
"message": "Ustalanie loadera"
},
"app.install.phase.resolving_minecraft": {
"message": "Ustalanie wersji Minecraft"
},
"app.install.phase.resolving_pack": {
"message": "Ustalanie treści"
},
"app.install.phase.rolling_back": {
"message": "Przywracanie"
},
"app.install.phase.running_loader_processors": {
"message": "Uruchamianie procesorów loadera"
},
"app.instance.confirm-delete.admonition-body": {
"message": "Wszystkie dane z Twojej instancji zostaną trwale usunięte, w tym Twoje światy, pliki konfiguracji i jakakolwiek dodana zawartość."
},
"app.instance.confirm-delete.admonition-header": {
"message": "Tej akcji nie można cofnąć"
"message": "Tej akcji nie można odwrócić"
},
"app.instance.confirm-delete.delete-button": {
"message": "Usuń instancję"
@@ -347,15 +212,6 @@
"app.instance.modpack-already-installed.instance": {
"message": "Instalacja"
},
"app.instance.mods.bulk-update.downloading-projects": {
"message": "Pobieranie {current, number}/{total, number} projektów..."
},
"app.instance.mods.bulk-update.finishing": {
"message": "Kończenie aktualizacji..."
},
"app.instance.mods.bulk-update.resolving-versions": {
"message": "Sprawdzanie wersji..."
},
"app.instance.mods.content-type-project": {
"message": "projekt"
},
@@ -363,13 +219,13 @@
"message": "Dodano \"{name}\""
},
"app.instance.mods.share-text": {
"message": "Sprawdź projekty, których używam w mojej paczce modów!"
"message": "Sprawdź projekty, które używam w mojej paczce modów!"
},
"app.instance.mods.share-title": {
"message": "Udostępnianie zawartości paczki modów"
},
"app.instance.mods.successfully-uploaded": {
"message": "Przesyłanie zakończone sukcesem"
"message": "Przesyłanie powiodło się"
},
"app.instance.worlds.add-server": {
"message": "Dodaj serwer"
@@ -377,18 +233,6 @@
"app.instance.worlds.browse-servers": {
"message": "Szukaj serwerów"
},
"app.instance.worlds.delete-world-modal.delete-button": {
"message": "Usuń świat"
},
"app.instance.worlds.delete-world-modal.title": {
"message": "Usuń świat"
},
"app.instance.worlds.delete-world-modal.warning-body": {
"message": "Ten świat zostanie trwale usunięty z tej instancji. Tej akcji nie będzie można cofnąć."
},
"app.instance.worlds.delete-world-modal.warning-header": {
"message": "Usuwanie {name}"
},
"app.instance.worlds.filter-modded": {
"message": "Zmodowany"
},
@@ -407,53 +251,32 @@
"app.instance.worlds.no-worlds-heading": {
"message": "Nie dodano żadnych światów ani serwerów"
},
"app.instance.worlds.remove-server-modal.remove-button": {
"message": "Usuń serwer"
},
"app.instance.worlds.remove-server-modal.title": {
"message": "Usuń serwer"
},
"app.instance.worlds.remove-server-modal.warning-body": {
"message": "Ten serwer zostanie trwale usunięty z tej instancji. Tej akcji nie będzie można cofnąć."
},
"app.instance.worlds.remove-server-modal.warning-header": {
"message": "Usuwanie {name}"
},
"app.instance.worlds.search-worlds-placeholder": {
"message": "Szukaj wśród {count} światów..."
},
"app.modal.install-to-play.dont-install": {
"message": "Nie instaluj"
"app.modal.install-to-play.content-required": {
"message": "Wymagana zawartość"
},
"app.modal.install-to-play.header": {
"message": "Zainstaluj, aby grać"
},
"app.modal.install-to-play.install-anyway": {
"message": "Instaluj mimo to"
},
"app.modal.install-to-play.install-button": {
"message": "Zainstaluj"
},
"app.modal.install-to-play.invite-warning": {
"message": "To zaproszenie zostało utworzone przez innego użytkownika Modrinth, nie przez firmę Modrinth. Tylko akceptuj zaproszenia od osób, którym ufasz."
},
"app.modal.install-to-play.mod-count": {
"message": "{count, plural, one {# mod} few {# mody} other {# modów}}"
},
"app.modal.install-to-play.reviewed-files": {
"message": "Plik jest sprawdzony tylko, jeżeli został opublikowany na Modrinth, niezależnie od jego formatu (w tym pliki .mrpack)."
"app.modal.install-to-play.required-modpack": {
"message": "Wymagana paczka modów"
},
"app.modal.install-to-play.server-requires-mods": {
"message": "Ten serwer wymaga modów, aby na nim grać. Kliknij \"Zainstaluj\" aby otrzymać potrzebne pliki z Modrinth, a potem dołącz bezpośrednio do serwera."
},
"app.modal.install-to-play.shared-instance": {
"message": "Wspólna instancja"
},
"app.modal.install-to-play.unknown-files-description": {
"message": "Ta serwerowa paczka modów zawiera pliki, które nie zostały opublikowane na Modrinth. Zalecamy instalowanie plików tylko ze źródeł, którym ufasz."
},
"app.modal.install-to-play.unknown-files-warning": {
"message": "Ostrzeżenie o nieznanych plikach"
},
"app.modal.install-to-play.unrecognized-files": {
"message": "Nieznane plki"
"app.modal.install-to-play.shared-server-instance": {
"message": "Wspólna instancja serwera"
},
"app.modal.install-to-play.view-contents": {
"message": "Pokaż zawartość"
@@ -461,8 +284,8 @@
"app.modal.update-to-play.header": {
"message": "Zaktualizuj, by grać"
},
"app.modal.update-to-play.removed": {
"message": "Usunięto"
"app.modal.update-to-play.update-required": {
"message": "Wymagana jest aktualizacja"
},
"app.modal.update-to-play.update-required-description": {
"message": "Aktualizacja jest wymagana, aby grać w {name}. Proszę zaktualizować do najnowszej wersji, aby uruchomić grę."
@@ -470,23 +293,11 @@
"app.project.install-button.already-installed": {
"message": "Ten projekt jest już zainstalowany"
},
"app.project.install-button.switch-version": {
"message": "Zmień wersję"
},
"app.project.install-context.back-to-browse": {
"message": "Powróć do odkrywania"
},
"app.project.version.all-versions": {
"message": "Wszystkie wersje"
},
"app.project.version.download-in-browser": {
"message": "Pobierz w przeglądarce"
},
"app.project.version.installing": {
"message": "Instalowanie"
},
"app.project.versions.already-installed": {
"message": "Już zainstalowano"
"app.project.install-context.install-content-to-instance": {
"message": "Zainstaluj zasoby do instancji"
},
"app.settings.developer-mode-enabled": {
"message": "Tryb dewelopera włączony."
@@ -525,25 +336,25 @@
"message": "Usuń skórkę"
},
"app.skins.delete-modal.description": {
"message": "To trwale usunie wybraną skórkę. Tej akcji nie można cofnąć."
"message": "To trwale usunie wybraną skórkę. Tej akcji nie można odwrócić."
},
"app.skins.delete-modal.title": {
"message": "Czy na pewno chcesz usunąć tę skórkę?"
"message": "Czy na pewno chcesz usunąć tą skórkę?"
},
"app.skins.dropped-file-error.text": {
"message": "Nie udało się odczytać upuszczonego pliku."
"message": "Nie udało się odczytać wybranego pliku."
},
"app.skins.dropped-file-error.title": {
"message": "Błąd podczas przetwarzania pliku"
},
"app.skins.edit-button": {
"message": "Edytuj skórkę"
"message": "Edytuj skina"
},
"app.skins.modal.add-skin-button": {
"message": "Dodaj skórkę"
"message": "Dodaj skina"
},
"app.skins.modal.add-title": {
"message": "Dodawanie skórki"
"message": "Dodawanie skina"
},
"app.skins.modal.arm-style-section": {
"message": "Styl ramion"
@@ -561,10 +372,10 @@
"message": "Peleryny"
},
"app.skins.modal.edit-title": {
"message": "Edytowanie skórki"
"message": "Edytuj skina"
},
"app.skins.modal.make-edit-first-tooltip": {
"message": "Zmodyfikuj najpierw skórkę!"
"message": "Wpierw zmień jakoś skórkę!"
},
"app.skins.modal.no-cape-tooltip": {
"message": "Brak peleryny"
@@ -576,7 +387,7 @@
"message": "Zamień teksturę"
},
"app.skins.modal.save-skin-button": {
"message": "Zapisz skórkę"
"message": "Zapisz skina"
},
"app.skins.modal.saving-tooltip": {
"message": "Zapisywanie..."
@@ -608,9 +419,6 @@
"app.skins.section.builders-and-biomes": {
"message": "Builders & Biomes"
},
"app.skins.section.chaos-cubed": {
"message": "Chaos Cubed"
},
"app.skins.section.chase-the-skies": {
"message": "Chase the Skies"
},
@@ -660,7 +468,7 @@
"message": "Zaloguj się"
},
"app.skins.title": {
"message": "Wybór skórki"
"message": "Wybierz skórkę"
},
"app.update-popup.body.download-complete": {
"message": "Wersja Modrinth App v{version} została pobrana. Załaduj ponownie, żeby zaktualizować teraz, albo automatycznie, gdy zamkniesz Modrinth App."
@@ -960,7 +768,7 @@
"message": "Niestandardowe argumenty Java"
},
"instance.settings.tabs.java.custom-java-installation": {
"message": "Niestandardowa instalacja Java"
"message": "Niestandardowa instalacja Javy"
},
"instance.settings.tabs.java.custom-memory-allocation": {
"message": "Niestandardowa alokacja pamięci"
@@ -969,7 +777,7 @@
"message": "Zmienne środowiskowe..."
},
"instance.settings.tabs.java.enter-java-arguments": {
"message": "Argumenty Java..."
"message": "Argumenty Javy..."
},
"instance.settings.tabs.java.environment-variables": {
"message": "Zmienne środowiskowe"
@@ -1028,9 +836,6 @@
"instance.worlds.copy_address": {
"message": "Kopiuj adres"
},
"instance.worlds.create_shortcut": {
"message": "Utwórz skrót"
},
"instance.worlds.dont_show_on_home": {
"message": "Nie pokazuj na stronie głównej"
},
@@ -1105,5 +910,26 @@
},
"search.filter.locked.server-loader.title": {
"message": "Loader jest dostarczony przez serwer"
},
"unknown-pack-warning-modal.body": {
"message": "Plik jest sprawdzony tylko, jeżeli został przesłany na Modrinth, niezależnie od formatu (w tym pliki .mrpack)."
},
"unknown-pack-warning-modal.dont-show-again": {
"message": "Nie pokazuj ponownie"
},
"unknown-pack-warning-modal.header": {
"message": "Potwierdź instalację"
},
"unknown-pack-warning-modal.install-anyway": {
"message": "Instaluj mimo to"
},
"unknown-pack-warning-modal.malware-statement": {
"message": "Wirusy są często kryte w plikach paczek modów przesyłanych na platformach takich jak Discord."
},
"unknown-pack-warning-modal.warning.body": {
"message": "Nie mogliśmy znaleźć tego pliku na Modrinth. Stanowczo zalecamy instalowanie plików tylko ze źródeł, którym ufasz."
},
"unknown-pack-warning-modal.warning.title": {
"message": "Ostrzeżenie o nieznanym pliku"
}
}
+41 -215
View File
@@ -11,87 +11,6 @@
"app.action-bar.hide-more-running-instances": {
"message": "Ocultar mais instâncias em execução"
},
"app.action-bar.install.copied-details": {
"message": "Copiado"
},
"app.action-bar.install.copy-details": {
"message": "Copiar detalhes"
},
"app.action-bar.install.dismiss": {
"message": "Dispensar"
},
"app.action-bar.install.open-instance": {
"message": "Abrir instância"
},
"app.action-bar.install.retry": {
"message": "Tentar novamente"
},
"app.action-bar.install.summary.app-closing": {
"message": "Cancelado devido ao aplicativo ter fechado"
},
"app.action-bar.install.summary.bad-modpack-file": {
"message": "Não foi possível ler o pacote de mods"
},
"app.action-bar.install.summary.canceled": {
"message": "Cancelado"
},
"app.action-bar.install.summary.cleanup-incomplete": {
"message": "A limpeza não foi finalizada"
},
"app.action-bar.install.summary.content-download-failed": {
"message": "Não foi possível baixar os arquivos"
},
"app.action-bar.install.summary.corrupt-download": {
"message": "O arquivo baixado é corrupto"
},
"app.action-bar.install.summary.could-not-save-files": {
"message": "Não foi possível salvar os arquivos"
},
"app.action-bar.install.summary.download-failed": {
"message": "A transferência não foi finalizada"
},
"app.action-bar.install.summary.instance-not-found": {
"message": "Instância não encontrada"
},
"app.action-bar.install.summary.invalid-file-path": {
"message": "O caminho do arquivo é inválido"
},
"app.action-bar.install.summary.invalid-modpack": {
"message": "Os dados do pacote de mods é inválido"
},
"app.action-bar.install.summary.invalid-modpack-files": {
"message": "Os arquivos do pacote de mods possui metadados inválidos"
},
"app.action-bar.install.summary.java-setup-failed": {
"message": "Configuração Java não foi finalizada"
},
"app.action-bar.install.summary.loader-setup-failed": {
"message": "Falha na configuração do loader"
},
"app.action-bar.install.summary.local-data-error": {
"message": "Não foi atualizar os dados locais"
},
"app.action-bar.install.summary.minecraft-setup-failed": {
"message": "Falha na configuração do Minecraft"
},
"app.action-bar.install.summary.modrinth-unreachable": {
"message": "Não foi possível alcançar o Modrinth"
},
"app.action-bar.install.summary.no-write-permission": {
"message": "Sem permissão para escrita"
},
"app.action-bar.install.summary.pack-download-failed": {
"message": "Não foi possível baixar o pacote"
},
"app.action-bar.install.summary.unexpected-error": {
"message": "Algo deu errado"
},
"app.action-bar.install.unknown-instance": {
"message": "Instância desconhecida"
},
"app.action-bar.installs": {
"message": "Instalações"
},
"app.action-bar.make-primary-instance": {
"message": "Tornar instância principal"
},
@@ -182,12 +101,6 @@
"app.appearance-settings.show-play-time.title": {
"message": "Exibir tempo jogado"
},
"app.appearance-settings.skip-non-essential-warnings.description": {
"message": "Pula automaticamente as confirmações desnecessárias como duplica de instalação de pacotes de mods, exclusão de conteúdo normal, atualizações em massa, desvinculo de pacotes de mods e diálogos de reparo. Os avisos de perigo ainda serão exibidos."
},
"app.appearance-settings.skip-non-essential-warnings.title": {
"message": "Pular avisos desnecessários"
},
"app.appearance-settings.toggle-sidebar.description": {
"message": "Permite alternar a barra lateral."
},
@@ -257,72 +170,24 @@
"app.export-modal.header": {
"message": "Exportar pacote de mods"
},
"app.export-modal.include-file-accessibility-label": {
"message": "Incluir \"{file}\"?"
},
"app.export-modal.modpack-name-label": {
"message": "Nome do pacote de mods"
},
"app.export-modal.modpack-name-placeholder": {
"message": "Nome do pacote de mods"
},
"app.export-modal.select-files-label": {
"message": "Configure quais arquivos serão incluídos na exportação"
},
"app.export-modal.version-number-label": {
"message": "Número da versão"
},
"app.export-modal.version-number-placeholder": {
"message": "1.0.0"
},
"app.install.phase.downloading_content": {
"message": "Instalando conteúdo"
},
"app.install.phase.downloading_minecraft": {
"message": "Instalando Minecraft"
},
"app.install.phase.downloading_pack_file": {
"message": "Instalando arquivo de pacote"
},
"app.install.phase.extracting_overrides": {
"message": "Extraindo substitutos"
},
"app.install.phase.finalizing": {
"message": "Finalizando"
},
"app.install.phase.preparing_instance": {
"message": "Na fila"
},
"app.install.phase.preparing_java": {
"message": "Preparando Java"
},
"app.install.phase.preparing_java.downloading": {
"message": "Baixando Java {version}"
},
"app.install.phase.preparing_java.extracting": {
"message": "Extraindo Java {version}"
},
"app.install.phase.preparing_java.fetching-metadata": {
"message": "Obtendo Java {version}"
},
"app.install.phase.preparing_java.resolving": {
"message": "Preparando Java {version}"
},
"app.install.phase.preparing_java.validating": {
"message": "Validando Java {version}"
},
"app.install.phase.reading_pack_manifest": {
"message": "Lendo manifestação de pacote"
},
"app.install.phase.resolving_loader": {
"message": "Resolvendo loader"
},
"app.install.phase.resolving_minecraft": {
"message": "Resolvendo Minecraft"
},
"app.install.phase.resolving_pack": {
"message": "Resolvendo conteúdo"
},
"app.install.phase.rolling_back": {
"message": "Indo de volta"
},
"app.install.phase.running_loader_processors": {
"message": "Executando processadores de loader"
},
"app.instance.confirm-delete.admonition-body": {
"message": "Todos os dados para sua instância serão excluídos permanentemente, incluindo seus mundos, configs e todo o conteúdo instalado."
},
@@ -347,15 +212,6 @@
"app.instance.modpack-already-installed.instance": {
"message": "Instância"
},
"app.instance.mods.bulk-update.downloading-projects": {
"message": "Instalando {current, number}/{total, number} projeto(s)..."
},
"app.instance.mods.bulk-update.finishing": {
"message": "Finalizando atualização..."
},
"app.instance.mods.bulk-update.resolving-versions": {
"message": "Resolvendo versões..."
},
"app.instance.mods.content-type-project": {
"message": "projeto"
},
@@ -380,18 +236,6 @@
"app.instance.worlds.browse-servers": {
"message": "Explorar servidores"
},
"app.instance.worlds.delete-world-modal.delete-button": {
"message": "Excluir mundo"
},
"app.instance.worlds.delete-world-modal.title": {
"message": "Excluir mundo"
},
"app.instance.worlds.delete-world-modal.warning-body": {
"message": "Este mundo será permanentemente excluído desta instância. Esta ação não pode ser desfeita."
},
"app.instance.worlds.delete-world-modal.warning-header": {
"message": "Excluindo {name}"
},
"app.instance.worlds.filter-modded": {
"message": "Modificado"
},
@@ -410,53 +254,32 @@
"app.instance.worlds.no-worlds-heading": {
"message": "Nenhum servidor ou mundo adicionado"
},
"app.instance.worlds.remove-server-modal.remove-button": {
"message": "Remover servidor"
},
"app.instance.worlds.remove-server-modal.title": {
"message": "Remover servidor"
},
"app.instance.worlds.remove-server-modal.warning-body": {
"message": "O servidor será removido da lista de servidores e da lista de servidores no jogo. Você pode re-adicioná-lo depois se souber o endereço."
},
"app.instance.worlds.remove-server-modal.warning-header": {
"message": "Removendo {name}"
},
"app.instance.worlds.search-worlds-placeholder": {
"message": "Buscar {count} mundos..."
},
"app.modal.install-to-play.dont-install": {
"message": "Não instalar"
"app.modal.install-to-play.content-required": {
"message": "Conteúdo necessário"
},
"app.modal.install-to-play.header": {
"message": "Instale para jogar"
},
"app.modal.install-to-play.install-anyway": {
"message": "Instalar mesmo assim"
},
"app.modal.install-to-play.install-button": {
"message": "Instalar"
},
"app.modal.install-to-play.invite-warning": {
"message": "Este convite foi criado por outro usuário do Modrinth, não pelo próprio Modrinth. Só aceite convites de pessoas que você confia."
},
"app.modal.install-to-play.mod-count": {
"message": "{count, plural, =0 {Nenhum mod} one {# mod} other {# mods}}"
},
"app.modal.install-to-play.reviewed-files": {
"message": "Um arquivo só é revisado se for publicado no Modrinth, independente do formato de arquivo (incluindo .mrpack)."
"app.modal.install-to-play.required-modpack": {
"message": "Pacote de mods necessário"
},
"app.modal.install-to-play.server-requires-mods": {
"message": "Este servidor exige mods para jogar. Clique em instalar para configurar os arquivos necessários através do Modrinth, e então iniciar diretamente no servidor."
},
"app.modal.install-to-play.shared-instance": {
"message": "Instância compartilhada"
},
"app.modal.install-to-play.unknown-files-description": {
"message": "Este pacote de mods do servidor contém arquivos não publicados no Modrinth. Recomendamos fortemente a instalar apenas arquivos de fontes que você confia."
},
"app.modal.install-to-play.unknown-files-warning": {
"message": "Aviso de arquivos desconhecidos"
},
"app.modal.install-to-play.unrecognized-files": {
"message": "Arquivos não reconhecidos"
"app.modal.install-to-play.shared-server-instance": {
"message": "Instância de servidor compartilhada"
},
"app.modal.install-to-play.view-contents": {
"message": "Ver conteúdo"
@@ -464,8 +287,8 @@
"app.modal.update-to-play.header": {
"message": "Atualize para jogar"
},
"app.modal.update-to-play.removed": {
"message": "Removido"
"app.modal.update-to-play.update-required": {
"message": "Atualização necessária"
},
"app.modal.update-to-play.update-required-description": {
"message": "Uma atualização é necessária para jogar {name}. Atualize para a versão mais recente para iniciar o jogo."
@@ -473,23 +296,11 @@
"app.project.install-button.already-installed": {
"message": "Este projeto já foi instalado"
},
"app.project.install-button.switch-version": {
"message": "Trocar versão"
},
"app.project.install-context.back-to-browse": {
"message": "Voltar a descobrir"
},
"app.project.version.all-versions": {
"message": "Todas as versões"
},
"app.project.version.download-in-browser": {
"message": "Baixar no navegador"
},
"app.project.version.installing": {
"message": "Instalando"
},
"app.project.versions.already-installed": {
"message": "Já instalado"
"app.project.install-context.install-content-to-instance": {
"message": "Instalar conteúdo para a instância"
},
"app.settings.developer-mode-enabled": {
"message": "Modo desenvolvedor ativado."
@@ -611,9 +422,6 @@
"app.skins.section.builders-and-biomes": {
"message": "Construtores & Biomas"
},
"app.skins.section.chaos-cubed": {
"message": "Chaos Cubed"
},
"app.skins.section.chase-the-skies": {
"message": "Chase the Skies"
},
@@ -678,7 +486,7 @@
"message": "Mudanças"
},
"app.update-popup.download": {
"message": "Instalar ({size})"
"message": "Baixar ({size})"
},
"app.update-popup.download-complete": {
"message": "Instalação concluída"
@@ -1031,9 +839,6 @@
"instance.worlds.copy_address": {
"message": "Copiar endereço"
},
"instance.worlds.create_shortcut": {
"message": "Criar atalho"
},
"instance.worlds.dont_show_on_home": {
"message": "Não exibir na página inicial"
},
@@ -1108,5 +913,26 @@
},
"search.filter.locked.server-loader.title": {
"message": "O loader é 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)."
},
"unknown-pack-warning-modal.dont-show-again": {
"message": "Não exibir aviso novamente"
},
"unknown-pack-warning-modal.header": {
"message": "Confirmar instalação"
},
"unknown-pack-warning-modal.install-anyway": {
"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."
},
"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."
},
"unknown-pack-warning-modal.warning.title": {
"message": "Aviso de arquivo desconhecido"
}
}
@@ -110,6 +110,9 @@
"app.export-modal.header": {
"message": "Exportar modpack"
},
"app.export-modal.modpack-name-label": {
"message": "Nome do Modpack"
},
"app.export-modal.modpack-name-placeholder": {
"message": "Nome do Modpack"
},
@@ -188,6 +191,9 @@
"app.instance.worlds.search-worlds-placeholder": {
"message": "Pesquisar {count} mundos..."
},
"app.modal.install-to-play.content-required": {
"message": "Conteúdo necessário"
},
"app.modal.install-to-play.header": {
"message": "Instala para jogar"
},
@@ -197,15 +203,27 @@
"app.modal.install-to-play.mod-count": {
"message": "{count, plural, one {mod} other {mods}}"
},
"app.modal.install-to-play.required-modpack": {
"message": "Modpack requerido"
},
"app.modal.install-to-play.server-requires-mods": {
"message": "Este servidor requer mods para jogares. Clica Instalar para transferir os ficheiros necessários do Modrinth, e então entrar diretamente no servidor."
},
"app.modal.install-to-play.shared-instance": {
"message": "Instância partilhada"
},
"app.modal.install-to-play.shared-server-instance": {
"message": "Instância de servidor partilhada"
},
"app.modal.install-to-play.view-contents": {
"message": "Ver conteúdo"
},
"app.modal.update-to-play.header": {
"message": "Atualiza para jogar"
},
"app.modal.update-to-play.update-required": {
"message": "Atualização necessária"
},
"app.modal.update-to-play.update-required-description": {
"message": "Uma atualização é necessária para jogar {name}. Por favor atualiza para a versão mais recente para iniciar o jogo."
},
@@ -5,6 +5,9 @@
"app.auth-servers.unreachable.header": {
"message": "Nu se pot accesa serverele de autentificare"
},
"app.modal.install-to-play.content-required": {
"message": "Conținut necesar"
},
"app.modal.install-to-play.header": {
"message": "Instalați pentru a juca"
},
@@ -14,15 +17,27 @@
"app.modal.install-to-play.mod-count": {
"message": "{count, plural,one {#mod} other {# moduri}}"
},
"app.modal.install-to-play.required-modpack": {
"message": "Pachet de mod necesar"
},
"app.modal.install-to-play.server-requires-mods": {
"message": "Acest server necesită modificări pentru a juca. Faceți clic pe Instalare pentru a configura fișierele necesare din Modrinth, apoi lansați direct pe server."
},
"app.modal.install-to-play.shared-instance": {
"message": "Instanță comună"
},
"app.modal.install-to-play.shared-server-instance": {
"message": "Instanță de server partajată"
},
"app.modal.install-to-play.view-contents": {
"message": "Vizualizați conținutul"
},
"app.modal.update-to-play.header": {
"message": "Actualizați pentru a juca"
},
"app.modal.update-to-play.update-required": {
"message": "Actualizare necesară"
},
"app.modal.update-to-play.update-required-description": {
"message": "Este necesară o actualizare pentru a juca {name}. Vă rugăm să actualizați la cea mai recentă versiune pentru a lansa jocul."
},
+44 -218
View File
@@ -3,7 +3,7 @@
"message": "Скачивание Java {version}"
},
"app.action-bar.downloading-update": {
"message": "Скачивание обновления"
"message": "Скачивание"
},
"app.action-bar.downloads": {
"message": "Загрузки"
@@ -11,87 +11,6 @@
"app.action-bar.hide-more-running-instances": {
"message": "Скрыть другие активные сборки"
},
"app.action-bar.install.copied-details": {
"message": "Скопировано"
},
"app.action-bar.install.copy-details": {
"message": "Копировать"
},
"app.action-bar.install.dismiss": {
"message": "Закрыть"
},
"app.action-bar.install.open-instance": {
"message": "Открыть сборку"
},
"app.action-bar.install.retry": {
"message": "Повторить"
},
"app.action-bar.install.summary.app-closing": {
"message": "Отменено из‑за закрытия приложения"
},
"app.action-bar.install.summary.bad-modpack-file": {
"message": "Не удалось прочитать сборку"
},
"app.action-bar.install.summary.canceled": {
"message": "Отменено"
},
"app.action-bar.install.summary.cleanup-incomplete": {
"message": "Очистка прервана"
},
"app.action-bar.install.summary.content-download-failed": {
"message": "Не удалось скачать файлы"
},
"app.action-bar.install.summary.corrupt-download": {
"message": "Скачанный файл повреждён"
},
"app.action-bar.install.summary.could-not-save-files": {
"message": "Не удалось сохранить файлы"
},
"app.action-bar.install.summary.download-failed": {
"message": "Скачивание прервано"
},
"app.action-bar.install.summary.instance-not-found": {
"message": "Сборка не найдена"
},
"app.action-bar.install.summary.invalid-file-path": {
"message": "Неверный путь к файлу"
},
"app.action-bar.install.summary.invalid-modpack": {
"message": "Неверные данные сборки"
},
"app.action-bar.install.summary.invalid-modpack-files": {
"message": "Неверные метаданные в файлах сборки "
},
"app.action-bar.install.summary.java-setup-failed": {
"message": "Установка Java прервана"
},
"app.action-bar.install.summary.loader-setup-failed": {
"message": "Не удалось установить загрузчик"
},
"app.action-bar.install.summary.local-data-error": {
"message": "Не удалось обновить локальные данные"
},
"app.action-bar.install.summary.minecraft-setup-failed": {
"message": "Не удалось установить Minecraft"
},
"app.action-bar.install.summary.modrinth-unreachable": {
"message": "Не удалось подключиться к Modrinth"
},
"app.action-bar.install.summary.no-write-permission": {
"message": "Нет разрешения на запись"
},
"app.action-bar.install.summary.pack-download-failed": {
"message": "Не удалось скачать сборку"
},
"app.action-bar.install.summary.unexpected-error": {
"message": "Произошла ошибка"
},
"app.action-bar.install.unknown-instance": {
"message": "Неизвестная сборка"
},
"app.action-bar.installs": {
"message": "Установки"
},
"app.action-bar.make-primary-instance": {
"message": "Сделать основной сборкой"
},
@@ -105,7 +24,7 @@
"message": "Основная сборка"
},
"app.action-bar.reload-to-update": {
"message": "Перезапустить и обновить"
"message": "Перезапустить"
},
"app.action-bar.show-more-running-instances": {
"message": "Показать другие активные сборки"
@@ -182,12 +101,6 @@
"app.appearance-settings.show-play-time.title": {
"message": "Время игры"
},
"app.appearance-settings.skip-non-essential-warnings.description": {
"message": "Скрывать малозначимые подтверждения: установку копий сборок, обычное удаление контента, массовые обновления, отвязку и исправление сборок. Критические предупреждения останутся."
},
"app.appearance-settings.skip-non-essential-warnings.title": {
"message": "Только опасные предупреждения"
},
"app.appearance-settings.toggle-sidebar.description": {
"message": "Показывать кнопку скрытия боковой панели."
},
@@ -257,72 +170,24 @@
"app.export-modal.header": {
"message": "Экспорт сборки"
},
"app.export-modal.include-file-accessibility-label": {
"message": "Добавить «{file}» в экспорт?"
},
"app.export-modal.modpack-name-label": {
"message": "Название сборки"
},
"app.export-modal.modpack-name-placeholder": {
"message": "Название сборки"
},
"app.export-modal.select-files-label": {
"message": "Выберите файлы для экспорта"
},
"app.export-modal.version-number-label": {
"message": "Номер версии"
},
"app.export-modal.version-number-placeholder": {
"message": "1.0.0"
},
"app.install.phase.downloading_content": {
"message": "Скачивание контента"
},
"app.install.phase.downloading_minecraft": {
"message": "Скачивание Minecraft"
},
"app.install.phase.downloading_pack_file": {
"message": "Скачивание файла сборки"
},
"app.install.phase.extracting_overrides": {
"message": "Распаковка переопределений"
},
"app.install.phase.finalizing": {
"message": "Завершение"
},
"app.install.phase.preparing_instance": {
"message": "В очереди на установку"
},
"app.install.phase.preparing_java": {
"message": "Подготовка Java"
},
"app.install.phase.preparing_java.downloading": {
"message": "Скачивание Java {version}"
},
"app.install.phase.preparing_java.extracting": {
"message": "Распаковка Java {version}"
},
"app.install.phase.preparing_java.fetching-metadata": {
"message": "Получение Java {version}"
},
"app.install.phase.preparing_java.resolving": {
"message": "Подготовка Java {version}"
},
"app.install.phase.preparing_java.validating": {
"message": "Проверка Java {version}"
},
"app.install.phase.reading_pack_manifest": {
"message": "Чтение манифеста сборки"
},
"app.install.phase.resolving_loader": {
"message": "Разрешение загрузчика"
},
"app.install.phase.resolving_minecraft": {
"message": "Разрешение Minecraft"
},
"app.install.phase.resolving_pack": {
"message": "Разрешение контента"
},
"app.install.phase.rolling_back": {
"message": "Откат"
},
"app.install.phase.running_loader_processors": {
"message": "Запуск процессоров загрузчика"
},
"app.instance.confirm-delete.admonition-body": {
"message": "Все данные сборки будут удалены навсегда, в том числе миры, настройки и весь установленный контент."
},
@@ -347,15 +212,6 @@
"app.instance.modpack-already-installed.instance": {
"message": "Сборка"
},
"app.instance.mods.bulk-update.downloading-projects": {
"message": "Скачивание проектов: {current, number}/{total, number}..."
},
"app.instance.mods.bulk-update.finishing": {
"message": "Завершение обновления..."
},
"app.instance.mods.bulk-update.resolving-versions": {
"message": "Разрешение версий..."
},
"app.instance.mods.content-type-project": {
"message": "проект"
},
@@ -377,18 +233,6 @@
"app.instance.worlds.browse-servers": {
"message": "Найти серверы"
},
"app.instance.worlds.delete-world-modal.delete-button": {
"message": "Удалить мир"
},
"app.instance.worlds.delete-world-modal.title": {
"message": "Удаление мира"
},
"app.instance.worlds.delete-world-modal.warning-body": {
"message": "Этот мир будет удалён навсегда из этой сборки. Отменить это действие нельзя."
},
"app.instance.worlds.delete-world-modal.warning-header": {
"message": "Удаление {name}"
},
"app.instance.worlds.filter-modded": {
"message": "Модовые"
},
@@ -407,50 +251,29 @@
"app.instance.worlds.no-worlds-heading": {
"message": "Нет серверов и миров"
},
"app.instance.worlds.remove-server-modal.remove-button": {
"message": "Удалить сервер"
},
"app.instance.worlds.remove-server-modal.title": {
"message": "Удаление сервера"
},
"app.instance.worlds.remove-server-modal.warning-body": {
"message": "Этот сервер будет удалён из списка серверов, в том числе и в игре. Его можно вернуть, зная адрес."
},
"app.instance.worlds.remove-server-modal.warning-header": {
"message": "Удаление {name}"
},
"app.modal.install-to-play.dont-install": {
"message": "Не устанавливать"
"app.modal.install-to-play.content-required": {
"message": "Требуется дополнительный контент"
},
"app.modal.install-to-play.header": {
"message": "Установка перед запуском"
},
"app.modal.install-to-play.install-anyway": {
"message": "Всё равно установить"
},
"app.modal.install-to-play.install-button": {
"message": "Установить"
},
"app.modal.install-to-play.invite-warning": {
"message": "Это приглашение создано другим пользователем Modrinth. Принимайте приглашения только от тех, кому доверяете."
},
"app.modal.install-to-play.mod-count": {
"message": "{count, plural, one {# мод} few {# мода} other {# модов}}"
},
"app.modal.install-to-play.reviewed-files": {
"message": "Файл проверяется только если он опубликован на Modrinth, независимо от его формата (включая .mrpack)."
"app.modal.install-to-play.required-modpack": {
"message": "Необходимая сборка"
},
"app.modal.install-to-play.server-requires-mods": {
"message": "Для игры на сервере требуются моды. Установите необходимые файлы с Modrinth, чтобы подключиться."
},
"app.modal.install-to-play.shared-instance": {
"message": "Сборка"
},
"app.modal.install-to-play.unknown-files-description": {
"message": "Сборка этого сервера содержит файлы, которые не опубликованы на Modrinth. Рекомендуется скачивать файлы только из надёжных источников."
},
"app.modal.install-to-play.unknown-files-warning": {
"message": "Предупреждение о неизвестных файлах"
},
"app.modal.install-to-play.unrecognized-files": {
"message": "Нераспознанные файлы"
"app.modal.install-to-play.shared-server-instance": {
"message": "Общая сборка сервера"
},
"app.modal.install-to-play.view-contents": {
"message": "Посмотреть содержимое"
@@ -458,8 +281,8 @@
"app.modal.update-to-play.header": {
"message": "Обновление перед запуском"
},
"app.modal.update-to-play.removed": {
"message": "Удалён"
"app.modal.update-to-play.update-required": {
"message": "Требуется обновление"
},
"app.modal.update-to-play.update-required-description": {
"message": "Обновите {name} до последней версии, чтобы запустить игру."
@@ -467,23 +290,11 @@
"app.project.install-button.already-installed": {
"message": "Этот проект уже установлен"
},
"app.project.install-button.switch-version": {
"message": "Сменить версию"
},
"app.project.install-context.back-to-browse": {
"message": "Вернуться к поиску"
},
"app.project.version.all-versions": {
"message": "Все версии"
},
"app.project.version.download-in-browser": {
"message": "Скачать в браузере"
},
"app.project.version.installing": {
"message": "Установка"
},
"app.project.versions.already-installed": {
"message": "Уже установлено"
"app.project.install-context.install-content-to-instance": {
"message": "Установка контента в сборку"
},
"app.settings.developer-mode-enabled": {
"message": "Режим разработчика включён."
@@ -597,7 +408,7 @@
"message": "Не так быстро!"
},
"app.skins.reorder-error.text": {
"message": "Порядок скинов не сохранился."
"message": "Ваш порядок скинов не удалось сохранить."
},
"app.skins.reorder-error.title": {
"message": "Не удалось изменить порядок скинов"
@@ -605,9 +416,6 @@
"app.skins.section.builders-and-biomes": {
"message": "Builders & Biomes"
},
"app.skins.section.chaos-cubed": {
"message": "Chaos Cubed"
},
"app.skins.section.chase-the-skies": {
"message": "Chase the Skies"
},
@@ -981,7 +789,7 @@
"message": "Выделенная память"
},
"instance.settings.tabs.java.java-path-placeholder": {
"message": "/путь/к/java"
"message": "/path/to/java"
},
"instance.settings.tabs.window": {
"message": "Окно"
@@ -1022,9 +830,6 @@
"instance.worlds.copy_address": {
"message": "Копировать адрес"
},
"instance.worlds.create_shortcut": {
"message": "Создать ярлык"
},
"instance.worlds.dont_show_on_home": {
"message": "Не показывать на главной"
},
@@ -1099,5 +904,26 @@
},
"search.filter.locked.server-loader.title": {
"message": "Загрузчик управляется сервером"
},
"unknown-pack-warning-modal.body": {
"message": "Файл проверяется только в том случае, если он загружен на Modrinth, независимо от его формата (включая .mrpack)."
},
"unknown-pack-warning-modal.dont-show-again": {
"message": "Больше не предупреждать"
},
"unknown-pack-warning-modal.header": {
"message": "Подтверждение установки"
},
"unknown-pack-warning-modal.install-anyway": {
"message": "Всё равно установить"
},
"unknown-pack-warning-modal.malware-statement": {
"message": "Вредоносное ПО часто распространяется через сборки на таких платформах, как Discord."
},
"unknown-pack-warning-modal.warning.body": {
"message": "Этот файл не найден на Modrinth. Рекомендуется скачивать файлы только из надёжных источников."
},
"unknown-pack-warning-modal.warning.title": {
"message": "Предупреждение о неизвестном файле"
}
}
File diff suppressed because it is too large Load Diff
+60 -225
View File
@@ -11,74 +11,11 @@
"app.action-bar.hide-more-running-instances": {
"message": "Göm fler aktiva instanser"
},
"app.action-bar.install.copied-details": {
"message": "Kopierat"
},
"app.action-bar.install.copy-details": {
"message": "Kopiera detaljer"
},
"app.action-bar.install.dismiss": {
"message": "Avvisa"
},
"app.action-bar.install.open-instance": {
"message": "Öppna instans"
},
"app.action-bar.install.retry": {
"message": "Försök igen"
},
"app.action-bar.install.summary.app-closing": {
"message": "Avbröts på grund av att appen stängdes"
},
"app.action-bar.install.summary.bad-modpack-file": {
"message": "Kunde inte läsa modpaket"
},
"app.action-bar.install.summary.canceled": {
"message": "Avbruten"
},
"app.action-bar.install.summary.content-download-failed": {
"message": "Kunde inte ladda ner filer"
},
"app.action-bar.install.summary.corrupt-download": {
"message": "Nedladdad fil är korrupt"
},
"app.action-bar.install.summary.could-not-save-files": {
"message": "Kunde inte spara filer"
},
"app.action-bar.install.summary.download-failed": {
"message": "Nedladdning kunde inte slutföras"
},
"app.action-bar.install.summary.instance-not-found": {
"message": "Instans kunde inte hittas"
},
"app.action-bar.install.summary.invalid-file-path": {
"message": "Sökväg är ogiltig"
},
"app.action-bar.install.summary.local-data-error": {
"message": "Kunde inte uppdatera lokal data"
},
"app.action-bar.install.summary.modrinth-unreachable": {
"message": "Kunde inte nå Modrinth"
},
"app.action-bar.install.summary.no-write-permission": {
"message": "Ingen behörighet att skriva"
},
"app.action-bar.install.summary.pack-download-failed": {
"message": "Kunde inte ladda ner paket"
},
"app.action-bar.install.summary.unexpected-error": {
"message": "Någonting gick fel"
},
"app.action-bar.install.unknown-instance": {
"message": "Okänd instans"
},
"app.action-bar.installs": {
"message": "Installationer"
},
"app.action-bar.make-primary-instance": {
"message": "Gör till primärinstans"
},
"app.action-bar.no-instances-running": {
"message": "Inga aktiva instanser"
"message": "Ingen aktiva instans"
},
"app.action-bar.offline": {
"message": "Offline"
@@ -87,7 +24,7 @@
"message": "Primärinstans"
},
"app.action-bar.reload-to-update": {
"message": "Uppdatera (ladda om)"
"message": "Ladda om för att uppdatera"
},
"app.action-bar.show-more-running-instances": {
"message": "Visa fler aktiva instanser"
@@ -137,15 +74,9 @@
"app.appearance-settings.hide-nametag.title": {
"message": "Dölj namnskylt"
},
"app.appearance-settings.jump-back-into-worlds.description": {
"message": "Inkluderar de senaste världarna i \"Hoppa tillbaka i världar\" på hemsidan."
},
"app.appearance-settings.jump-back-into-worlds.title": {
"message": "Hoppa tillbaka i världar"
},
"app.appearance-settings.minimize-launcher.description": {
"message": "Minimerar launchern när Minecraft startas."
},
"app.appearance-settings.minimize-launcher.title": {
"message": "Minimera programstartare"
},
@@ -164,12 +95,6 @@
"app.appearance-settings.show-play-time.title": {
"message": "Visa speltid"
},
"app.appearance-settings.skip-non-essential-warnings.description": {
"message": "Hoppar över bekräftelser med låg risk, såsom installation av dubbla modpaket, radering av vanligt innehåll, massuppdateringar, avlänkning av modpaket och reparationsmeddelanden. Varningar om farliga händelser visas fortfarande."
},
"app.appearance-settings.skip-non-essential-warnings.title": {
"message": "Hoppa över icke nödvändiga varningar"
},
"app.appearance-settings.toggle-sidebar.description": {
"message": "Låter dig växla sidofältet."
},
@@ -216,14 +141,11 @@
"message": "Upptäck servrar"
},
"app.browse.hide-added-servers": {
"message": "m redan tillagda servrar"
"message": "Dölj servrar som redan är tillagda"
},
"app.browse.project-type.modpacks": {
"message": "Modpaket"
},
"app.browse.server-instance-content-warning": {
"message": "Att lägga till innehåll kan bryta kompatibiliteten när du ansluter dig till servern. Allt tillagt innehåll kommer att förloras när du uppdaterar serverinstansen."
},
"app.browse.server.installing": {
"message": "Installerar"
},
@@ -239,77 +161,29 @@
"app.export-modal.header": {
"message": "Exportera modpaket"
},
"app.export-modal.include-file-accessibility-label": {
"message": "Inkludera \"{file}\"?"
},
"app.export-modal.modpack-name-label": {
"message": "Modpaketets namn"
},
"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"
},
"app.export-modal.version-number-placeholder": {
"message": "1.0.0"
},
"app.install.phase.downloading_content": {
"message": "Laddar ner innehåll"
},
"app.install.phase.downloading_minecraft": {
"message": "Laddar ner Minecraft"
},
"app.install.phase.downloading_pack_file": {
"message": "Laddar ner pack fil"
},
"app.install.phase.extracting_overrides": {
"message": "Extraherar överskrivningar"
},
"app.install.phase.finalizing": {
"message": "Avslutar"
},
"app.install.phase.preparing_instance": {
"message": "Köad för installering"
},
"app.install.phase.preparing_java": {
"message": "Förbereder Java"
},
"app.install.phase.preparing_java.downloading": {
"message": "Laddar ner Java {version}"
},
"app.install.phase.preparing_java.extracting": {
"message": "Extraherar Java {version}"
},
"app.install.phase.preparing_java.fetching-metadata": {
"message": "Hämtar Java {version}"
},
"app.install.phase.preparing_java.resolving": {
"message": "Förbereder Java {version}"
},
"app.install.phase.preparing_java.validating": {
"message": "Bekräftar Java {version}"
},
"app.install.phase.reading_pack_manifest": {
"message": "Läser pack information"
},
"app.install.phase.resolving_loader": {
"message": "Fastställer loader"
},
"app.install.phase.resolving_minecraft": {
"message": "Fastställer Minecraft"
},
"app.install.phase.resolving_pack": {
"message": "Fastställer innehåll"
},
"app.install.phase.rolling_back": {
"message": "Återställer"
},
"app.install.phase.running_loader_processors": {
"message": "Kör loader-processorer"
},
"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."
},
"app.instance.confirm-delete.admonition-header": {
"message": "Detta kan inte ångras"
"message": "Detta kan inte ogöras"
},
"app.instance.confirm-delete.delete-button": {
"message": "Radera instans"
@@ -329,15 +203,6 @@
"app.instance.modpack-already-installed.instance": {
"message": "Instans"
},
"app.instance.mods.bulk-update.downloading-projects": {
"message": "Laddar ner {current, number}/{total, number} projekt..."
},
"app.instance.mods.bulk-update.finishing": {
"message": "Slutför uppdatering..."
},
"app.instance.mods.bulk-update.resolving-versions": {
"message": "Fastställer versioner..."
},
"app.instance.mods.content-type-project": {
"message": "projekt"
},
@@ -362,18 +227,6 @@
"app.instance.worlds.browse-servers": {
"message": "Utforska servrar"
},
"app.instance.worlds.delete-world-modal.delete-button": {
"message": "Ta bort värld"
},
"app.instance.worlds.delete-world-modal.title": {
"message": "Ta bort värld"
},
"app.instance.worlds.delete-world-modal.warning-body": {
"message": "Denna värld kommer permanent raderas från instansen. Detta kan inte ångras."
},
"app.instance.worlds.delete-world-modal.warning-header": {
"message": "Raderar {name}"
},
"app.instance.worlds.filter-modded": {
"message": "Moddad"
},
@@ -392,47 +245,41 @@
"app.instance.worlds.no-worlds-heading": {
"message": "Inga servrar eller världar tillagda"
},
"app.instance.worlds.remove-server-modal.remove-button": {
"message": "Ta bort server"
},
"app.instance.worlds.remove-server-modal.title": {
"message": "Ta bort server"
},
"app.instance.worlds.remove-server-modal.warning-body": {
"message": "Denna server kommer att tas bort från din serverlista och serverlistan i spelet. Du kan lägga till den igen senare om du känner till adressen."
},
"app.instance.worlds.remove-server-modal.warning-header": {
"message": "Raderar {name}"
},
"app.instance.worlds.search-worlds-placeholder": {
"message": "Sök {count} världar..."
},
"app.modal.install-to-play.dont-install": {
"message": "Installera inte"
"app.modal.install-to-play.content-required": {
"message": "Innehåll krävs"
},
"app.modal.install-to-play.header": {
"message": "Installera för att spela"
},
"app.modal.install-to-play.install-anyway": {
"message": "Installera andå"
},
"app.modal.install-to-play.install-button": {
"message": "Installera"
},
"app.modal.install-to-play.mod-count": {
"message": "{count, plural, one {# mod} other {# moddar}}"
},
"app.modal.install-to-play.required-modpack": {
"message": "Modpaket som krävs"
},
"app.modal.install-to-play.server-requires-mods": {
"message": "Servern kräver moddar för att du ska kunna spela. Klicka på Installera för att sätta upp dem nödvändiga filerna från Modrinth, och starta sedan på servern direkt."
},
"app.modal.install-to-play.shared-instance": {
"message": "Delad instans"
},
"app.modal.install-to-play.shared-server-instance": {
"message": "Delad serverinstans"
},
"app.modal.install-to-play.view-contents": {
"message": "Visa innehåll"
},
"app.modal.update-to-play.header": {
"message": "Uppdatera för att spela"
},
"app.modal.update-to-play.removed": {
"message": "Tog bort"
"app.modal.update-to-play.update-required": {
"message": "Uppdatering krävs"
},
"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."
@@ -440,23 +287,8 @@
"app.project.install-button.already-installed": {
"message": "Detta projekt är redan installerat"
},
"app.project.install-button.switch-version": {
"message": "Byt version"
},
"app.project.install-context.back-to-browse": {
"message": "Tillbaka till upptäck"
},
"app.project.version.all-versions": {
"message": "Alla versioner"
},
"app.project.version.download-in-browser": {
"message": "Ladda ner i webbläsare"
},
"app.project.version.installing": {
"message": "Installerar"
},
"app.project.versions.already-installed": {
"message": "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."
@@ -569,18 +401,6 @@
"app.skins.rate-limit.title": {
"message": "Sakta ner!"
},
"app.skins.reorder-error.text": {
"message": "Din ordning av utseenden kunde inte sparas."
},
"app.skins.reorder-error.title": {
"message": "Misslyckades med att ordna om utseenden"
},
"app.skins.section.builders-and-biomes": {
"message": "Builders & Biomes"
},
"app.skins.section.chaos-cubed": {
"message": "Chaos Cubed"
},
"app.skins.section.chase-the-skies": {
"message": "Chase the Skies"
},
@@ -605,9 +425,6 @@
"app.skins.section.saved-skins": {
"message": "Sparade utseenden"
},
"app.skins.section.striding-hero": {
"message": "Striding Hero"
},
"app.skins.section.the-copper-age": {
"message": "The Copper Age"
},
@@ -621,7 +438,7 @@
"message": "Logga in"
},
"app.skins.sign-in.description": {
"message": "Vänligen logga in på ditt Minecraft-konto för att använda utseendehanteringfunktioner i Modrinth appen."
"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"
@@ -663,7 +480,7 @@
"message": "Version {version} har installerats!"
},
"app.world.server-modal.placeholder-address": {
"message": "exempel.modrinth.gg"
"message": "example.modrinth.gg"
},
"app.world.server-modal.select-an-option": {
"message": "Välj ett alternativ"
@@ -738,7 +555,7 @@
"message": "Sök efter vänner..."
},
"friends.section.heading": {
"message": "{title} - {count}"
"message": "{title}: {count}"
},
"friends.sign-in-to-add-friends": {
"message": "<link>Logga in på ett Modrinth-konto</link> för att lägga till vänner och se vad de spelar!"
@@ -858,19 +675,19 @@
"message": "Alfa"
},
"instance.settings.tabs.general.update-channel.alpha.description": {
"message": "Release-, beta- och alfaversioner kommer visas som tillgängliga uppdateringar."
"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": "Release- och betaversioner kommer visas som tillgängliga uppdateringar."
"message": "Utgåvor och beta versioner visas som tillgängliga uppdateringar."
},
"instance.settings.tabs.general.update-channel.release": {
"message": "Release"
"message": "Utgåva"
},
"instance.settings.tabs.general.update-channel.release.description": {
"message": "Endast realeseversioner kommer visas som tillgängliga uppdateringar."
"message": "Endast utgivna versioner visas som tillgängliga uppdateringar."
},
"instance.settings.tabs.general.update-channel.select": {
"message": "Välj uppdateringskanal"
@@ -936,10 +753,10 @@
"message": "Anpassad minnestilldelning"
},
"instance.settings.tabs.java.enter-environment-variables": {
"message": "Ange miljövariabler..."
"message": "Skriv in miljövariabler..."
},
"instance.settings.tabs.java.enter-java-arguments": {
"message": "Ange Java-argument..."
"message": "Skriv in Java-argument..."
},
"instance.settings.tabs.java.environment-variables": {
"message": "Miljövariabler"
@@ -998,17 +815,14 @@
"instance.worlds.copy_address": {
"message": "Kopiera adress"
},
"instance.worlds.create_shortcut": {
"message": "Skapa genväg"
},
"instance.worlds.dont_show_on_home": {
"message": "Visa inte på startsidan"
"message": "Visa inte på Startsidan"
},
"instance.worlds.game_already_open": {
"message": "Instans är redan öppen"
},
"instance.worlds.hardcore": {
"message": "Hardcore-läge"
"message": "Hardcore läge"
},
"instance.worlds.incompatible_server": {
"message": "Servern är inkompatibel"
@@ -1020,10 +834,10 @@
"message": "Servern kunde inte kontaktas"
},
"instance.worlds.no_server_quick_play": {
"message": "Du kan endast gå direkt in i servrar på Minecraft Alfa 1.0.5+"
"message": "Du kan bara hoppa rakt in till servers på Minecraft Alfa 1.0.5+"
},
"instance.worlds.no_singleplayer_quick_play": {
"message": "Du kan endast gå direkt in i enspelarläge världar på Minecraft 1.20+"
"message": "Du kan bara hoppa rakt in till enspelarläge världar på Minecraft 1,20+"
},
"instance.worlds.play_instance": {
"message": "Spela instans"
@@ -1059,7 +873,7 @@
"message": "Spel version är tillhandahållet av instansen"
},
"search.filter.locked.instance-loader.title": {
"message": "Loader tillhandahålls av instansen"
"message": "Laddaren tillhandahålls av instansen"
},
"search.filter.locked.instance.sync": {
"message": "Synkronisera med instansen"
@@ -1075,5 +889,26 @@
},
"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"
}
}
@@ -146,6 +146,9 @@
"app.browse.discover-servers": {
"message": "สำรวจเซิร์ฟเวอร์"
},
"app.browse.hide-added-servers": {
"message": "ซ่อนเซิร์ฟเวอร์ที่ดำเนินการเพิ่มแล้ว"
},
"app.browse.project-type.modpacks": {
"message": "แพ็กม็อด"
},
@@ -167,9 +170,18 @@
"app.export-modal.header": {
"message": "ส่งออกแพ็กม็อด"
},
"app.export-modal.include-file-accessibility-label": {
"message": "รวมถึง \"{file}\" ใช่หรือไม่"
},
"app.export-modal.modpack-name-label": {
"message": "ชื่อแพ็กม็อด"
},
"app.export-modal.modpack-name-placeholder": {
"message": "ชื่อแพ็กม็อด"
},
"app.export-modal.select-files-label": {
"message": "กำหนดไฟล์ที่ต้องการจะส่งออก"
},
"app.export-modal.version-number-label": {
"message": "หมายเลขเวอร์ชัน"
},
@@ -245,6 +257,9 @@
"app.instance.worlds.search-worlds-placeholder": {
"message": "ค้าหาโลกทั้งหมด {count} โลก"
},
"app.modal.install-to-play.content-required": {
"message": "เนื้อหาที่จำเป็น"
},
"app.modal.install-to-play.header": {
"message": "ติดตั้งเพื่อเล่น"
},
@@ -254,15 +269,27 @@
"app.modal.install-to-play.mod-count": {
"message": "{count, plural, other {# ม็อด}}"
},
"app.modal.install-to-play.required-modpack": {
"message": "มอดแพ็กที่จำเป็น"
},
"app.modal.install-to-play.server-requires-mods": {
"message": "เซิร์ฟเวอร์ดังกล่าวจำเป็นต้องใช้ม็อดเพื่อเล่น โปรดติดตั้งและตั้งค่าไฟล์อื่นใดที่จำเป็นจาก Modrinth ก่อน จากนั้นถึงจะสามารถเข้าเล่นเซิร์ฟเวอร์ได้"
},
"app.modal.install-to-play.shared-instance": {
"message": "โปรแกรมที่มีร่วมกัน"
},
"app.modal.install-to-play.shared-server-instance": {
"message": "เซิร์ฟเวอร์ของโปรแกรมที่มีร่วมกัน"
},
"app.modal.install-to-play.view-contents": {
"message": "ดูเนื้อหา"
},
"app.modal.update-to-play.header": {
"message": "อัปเดตเพื่อเล่น"
},
"app.modal.update-to-play.update-required": {
"message": "จำเป็นต้องอัปเดต"
},
"app.modal.update-to-play.update-required-description": {
"message": "จำเป็นต้องอัปเดตเพื่อเล่น {name} กรุณาอัปเดตเป็นเวอร์ชันล่าสุดเพื่อเปิดเกม"
},
@@ -272,6 +299,9 @@
"app.project.install-context.back-to-browse": {
"message": "กลับไปที่หน้าค้นหา"
},
"app.project.install-context.install-content-to-instance": {
"message": "ติดตั้งเนื้อหาลงสู่อินสแตนซ์"
},
"app.settings.developer-mode-enabled": {
"message": "โหมดนักพัฒนาเปิดอยู่"
},
@@ -832,5 +862,26 @@
},
"search.filter.locked.server-loader.title": {
"message": "ตัวรันถูกกำหนดโดยเซิร์ฟเวอร์แล้ว"
},
"unknown-pack-warning-modal.body": {
"message": "ไฟล์จะได้รับการตรวจสอบโดยไม่คำนึงถึงประเภทของไฟล์ (รวมทั้ง .mrpack) เมื่อไฟล์ถูกอัปโหลดขึ้น Modrinth"
},
"unknown-pack-warning-modal.dont-show-again": {
"message": "อย่าแสดงคำเตือนนี้อีก"
},
"unknown-pack-warning-modal.header": {
"message": "ยืนยันการติดตั้ง"
},
"unknown-pack-warning-modal.install-anyway": {
"message": "ดำเนินการติดตั้งต่อไป"
},
"unknown-pack-warning-modal.malware-statement": {
"message": "มัลแวร์มักแฝงตัวมากับไฟล์แพ็กม็อดผ่านการแชร์ผ่านแพลตฟอร์มที่ไม่ใช่แพลตฟอร์มเฉพาะ เช่น ดิสคอร์ด"
},
"unknown-pack-warning-modal.warning.body": {
"message": "เราไม่สามารถค้นหาไฟล์ดังกล่าวได้บน Modrinth พวกเราขอแนะนำอย่างมากกว่าควรติดตั้งไฟล์จากแหล่งที่น่าเชื่อถือเท่านั้น"
},
"unknown-pack-warning-modal.warning.title": {
"message": "แจ้งเตือนไฟล์ไม่รู้จัก"
}
}
+41 -215
View File
@@ -11,87 +11,6 @@
"app.action-bar.hide-more-running-instances": {
"message": "Diğer çalışan kurulumları gizle"
},
"app.action-bar.install.copied-details": {
"message": "Kopyalandı"
},
"app.action-bar.install.copy-details": {
"message": "Detayları kopyala"
},
"app.action-bar.install.dismiss": {
"message": "Reddet"
},
"app.action-bar.install.open-instance": {
"message": "Kurulumu aç"
},
"app.action-bar.install.retry": {
"message": "Yeniden Dene"
},
"app.action-bar.install.summary.app-closing": {
"message": "Uygulama kapatıldığı için iptal edildi"
},
"app.action-bar.install.summary.bad-modpack-file": {
"message": "Mod paketi okunamadı"
},
"app.action-bar.install.summary.canceled": {
"message": "İptal edildi"
},
"app.action-bar.install.summary.cleanup-incomplete": {
"message": "Temizleme bitmedi"
},
"app.action-bar.install.summary.content-download-failed": {
"message": "Dosyalar indirilemedi"
},
"app.action-bar.install.summary.corrupt-download": {
"message": "İndirilmiş dosya bozuk"
},
"app.action-bar.install.summary.could-not-save-files": {
"message": "Dosyalar kaydedilemedi"
},
"app.action-bar.install.summary.download-failed": {
"message": "İndirme tamamlanamadı"
},
"app.action-bar.install.summary.instance-not-found": {
"message": "Kurulum bulunamadı"
},
"app.action-bar.install.summary.invalid-file-path": {
"message": "Dosya yolu geçersiz"
},
"app.action-bar.install.summary.invalid-modpack": {
"message": "Mod paketi verileri geçersiz"
},
"app.action-bar.install.summary.invalid-modpack-files": {
"message": "Mod paketi dosyaları geçersiz meta verilere sahip"
},
"app.action-bar.install.summary.java-setup-failed": {
"message": "Java kurulumu bitirilemedi"
},
"app.action-bar.install.summary.loader-setup-failed": {
"message": "Yükleyici kurulumu başarısız oldu"
},
"app.action-bar.install.summary.local-data-error": {
"message": "Yerel veriler güncellenemedi"
},
"app.action-bar.install.summary.minecraft-setup-failed": {
"message": "Minecraft kurulumu başarısız oldu"
},
"app.action-bar.install.summary.modrinth-unreachable": {
"message": "Modrinth'e erişilemiyor"
},
"app.action-bar.install.summary.no-write-permission": {
"message": "Yazmak için yetkiniz yok"
},
"app.action-bar.install.summary.pack-download-failed": {
"message": "Paket indirilemedi"
},
"app.action-bar.install.summary.unexpected-error": {
"message": "Bir sorun oluştu"
},
"app.action-bar.install.unknown-instance": {
"message": "Bilinmeyen kurulum"
},
"app.action-bar.installs": {
"message": "Yüklenenler"
},
"app.action-bar.make-primary-instance": {
"message": "Birincil kurulum yap"
},
@@ -182,12 +101,6 @@
"app.appearance-settings.show-play-time.title": {
"message": "Oynama Süresini Göster"
},
"app.appearance-settings.skip-non-essential-warnings.description": {
"message": "Mükerrer mod paketi kurulumları, normal içerik silme, toplu güncellemeler, mod paketi bağlantısını kesme ve onarım uyarıları gibi düşük riskli onayları otomatik olarak atlar. Tehlikeli uyarılar gösterilmeye devam edecektir."
},
"app.appearance-settings.skip-non-essential-warnings.title": {
"message": "Gerekli olmayan uyarıları atla"
},
"app.appearance-settings.toggle-sidebar.description": {
"message": "Kenar çubuğunu ayarlama özelliğini aktifleştirir."
},
@@ -257,72 +170,24 @@
"app.export-modal.header": {
"message": "Mod paketini dışa aktar"
},
"app.export-modal.include-file-accessibility-label": {
"message": "\"{file}\" dahil mi?"
},
"app.export-modal.modpack-name-label": {
"message": "Mod paketi adı"
"message": "Mod Paketi Adı"
},
"app.export-modal.modpack-name-placeholder": {
"message": "Mod paketi adı"
},
"app.export-modal.select-files-label": {
"message": "Bu dışa aktarmaya hangi dosyaların dahil edileceğini yapılandırma"
},
"app.export-modal.version-number-label": {
"message": "Sürüm numarası"
},
"app.export-modal.version-number-placeholder": {
"message": "1.0.0"
},
"app.install.phase.downloading_content": {
"message": "İçerik indiriliyor"
},
"app.install.phase.downloading_minecraft": {
"message": "Minecraft yükleniyor"
},
"app.install.phase.downloading_pack_file": {
"message": "Paket dosyaları yükleniyor"
},
"app.install.phase.extracting_overrides": {
"message": "Geçersiz kılmalar ayıklanıyor"
},
"app.install.phase.finalizing": {
"message": "Tamamlanıyor"
},
"app.install.phase.preparing_instance": {
"message": "İndirmek için sıraya alındı"
},
"app.install.phase.preparing_java": {
"message": "Java Hazırlanıyor"
},
"app.install.phase.preparing_java.downloading": {
"message": "Java {version} İndiriliyor"
},
"app.install.phase.preparing_java.extracting": {
"message": "Java {version} ayıklanıyor"
},
"app.install.phase.preparing_java.fetching-metadata": {
"message": "Java {version} getiriliyor"
},
"app.install.phase.preparing_java.resolving": {
"message": "Java {version} Hazırlanıyor"
},
"app.install.phase.preparing_java.validating": {
"message": "Java {version} doğrulanıyor"
},
"app.install.phase.reading_pack_manifest": {
"message": "Paket manifesti okunuyor"
},
"app.install.phase.resolving_loader": {
"message": "Yükleyici çözümleniyor"
},
"app.install.phase.resolving_minecraft": {
"message": "Minecraft çözümleniyor"
},
"app.install.phase.resolving_pack": {
"message": "İçerik çözümleniyor"
},
"app.install.phase.rolling_back": {
"message": "Değişiklikler geri alınıyor"
},
"app.install.phase.running_loader_processors": {
"message": "Yükleyici işlemcileri çalıştırılıyor"
},
"app.instance.confirm-delete.admonition-body": {
"message": "Dünyalarınız, yapılandırmalarınız ve yüklü tüm içerikler ve tüm veriler kalıcı olarak silinecek."
},
@@ -347,15 +212,6 @@
"app.instance.modpack-already-installed.instance": {
"message": "Kurulum"
},
"app.instance.mods.bulk-update.downloading-projects": {
"message": "{current, number}/{total, number} proje indiriliyor..."
},
"app.instance.mods.bulk-update.finishing": {
"message": "Güncelleme tamamlanıyor..."
},
"app.instance.mods.bulk-update.resolving-versions": {
"message": "Sürümler çözümleniyor..."
},
"app.instance.mods.content-type-project": {
"message": "proje"
},
@@ -380,18 +236,6 @@
"app.instance.worlds.browse-servers": {
"message": "Sunucuları Keşfet"
},
"app.instance.worlds.delete-world-modal.delete-button": {
"message": "Dünya Sil"
},
"app.instance.worlds.delete-world-modal.title": {
"message": "Dünyayı sil"
},
"app.instance.worlds.delete-world-modal.warning-body": {
"message": "Bu dünya, bu profilden kalıcı olarak silinecek. Bu işlem geri alınamaz."
},
"app.instance.worlds.delete-world-modal.warning-header": {
"message": "{name} Siliniyor"
},
"app.instance.worlds.filter-modded": {
"message": "Modlu"
},
@@ -410,53 +254,32 @@
"app.instance.worlds.no-worlds-heading": {
"message": "Eklenmiş sunucu veya dünya yok"
},
"app.instance.worlds.remove-server-modal.remove-button": {
"message": "Sunucuyu kaldır"
},
"app.instance.worlds.remove-server-modal.title": {
"message": "Sunucuyu kaldır"
},
"app.instance.worlds.remove-server-modal.warning-body": {
"message": "Bu sunucu, sunucu listenizden ve oyun içi sunucu listesinden kaldırılacaktır. Adresini biliyorsanız daha sonra tekrar ekleyebilirsiniz."
},
"app.instance.worlds.remove-server-modal.warning-header": {
"message": "{name} kaldırılıyor"
},
"app.instance.worlds.search-worlds-placeholder": {
"message": "{count} dünya ara..."
},
"app.modal.install-to-play.dont-install": {
"message": ndirme"
"app.modal.install-to-play.content-required": {
"message": çerik gerekli"
},
"app.modal.install-to-play.header": {
"message": "Oynamak için yükleyin"
},
"app.modal.install-to-play.install-anyway": {
"message": "Yine de indir"
},
"app.modal.install-to-play.install-button": {
"message": "Yükle"
},
"app.modal.install-to-play.invite-warning": {
"message": "Bu davetiye Modrinth tarafından değil, başka bir Modrinth kullanıcısı tarafından oluşturulmuştur. Sadece güvendiğiniz kişilerden gelen davetleri kabul edin."
},
"app.modal.install-to-play.mod-count": {
"message": "{count, plural, one {#mod} other {#mods}}"
},
"app.modal.install-to-play.reviewed-files": {
"message": "Dosya formatı ne olursa olsun (.mrpack dahil), bir dosya yalnızca Modrinth'e yüklendiğinde denetlenir."
"app.modal.install-to-play.required-modpack": {
"message": "Gerekli mod paketi"
},
"app.modal.install-to-play.server-requires-mods": {
"message": "Bu sunucuya girebilmek için modlar gereklidir. Gerekli dosyaları Modrinth üzerinden kurmak için Yükle butonuna tıkla, ardından doğrudan sunucuya başlat."
},
"app.modal.install-to-play.shared-instance": {
"message": "Paylaşılan Kurulum"
},
"app.modal.install-to-play.unknown-files-description": {
"message": "Bu sunucunun mod paketi Modrinth'te yayınlanmamış doysaları mevcut. Yalnızca güvendiğiniz kaynaklardan dosya indirmenizi önemle tavsiye ederiz."
},
"app.modal.install-to-play.unknown-files-warning": {
"message": "Bilinmeyen dosya uyarısı"
},
"app.modal.install-to-play.unrecognized-files": {
"message": "Tanımlanamayan dosyalar"
"app.modal.install-to-play.shared-server-instance": {
"message": "Paylaşılan Sunucu Kurulumu"
},
"app.modal.install-to-play.view-contents": {
"message": "İçeriği görüntüle"
@@ -464,8 +287,8 @@
"app.modal.update-to-play.header": {
"message": "Oynamak için güncelle"
},
"app.modal.update-to-play.removed": {
"message": "Silindi"
"app.modal.update-to-play.update-required": {
"message": "Güncelleme gerekli"
},
"app.modal.update-to-play.update-required-description": {
"message": "{name} oyununu oynamak için güncelleme gereklidir. Oyunu başlatmak için lütfen en son sürüme güncelleyin."
@@ -473,23 +296,11 @@
"app.project.install-button.already-installed": {
"message": "Bu proje zaten kurulu"
},
"app.project.install-button.switch-version": {
"message": "Sürüm değiştir"
},
"app.project.install-context.back-to-browse": {
"message": "Keşfete geri dön"
},
"app.project.version.all-versions": {
"message": "Tüm sürümler"
},
"app.project.version.download-in-browser": {
"message": "Tarayıcıdan indir"
},
"app.project.version.installing": {
"message": "Yükleniyor"
},
"app.project.versions.already-installed": {
"message": "Zaten yüklenmiş"
"app.project.install-context.install-content-to-instance": {
"message": "İçeriği kuruluma yükle"
},
"app.settings.developer-mode-enabled": {
"message": "Geliştirici modu açıldı."
@@ -611,9 +422,6 @@
"app.skins.section.builders-and-biomes": {
"message": "İnşaatçılar & Biyomlar"
},
"app.skins.section.chaos-cubed": {
"message": "Kaos Küpü"
},
"app.skins.section.chase-the-skies": {
"message": "Gökyüzünü kovala"
},
@@ -1031,9 +839,6 @@
"instance.worlds.copy_address": {
"message": "Adresi kopyala"
},
"instance.worlds.create_shortcut": {
"message": "Kısayol yap"
},
"instance.worlds.dont_show_on_home": {
"message": "Ana sayfada gösterme"
},
@@ -1108,5 +913,26 @@
},
"search.filter.locked.server-loader.title": {
"message": "Yükleyici sunucu tarafından sağlanıyor"
},
"unknown-pack-warning-modal.body": {
"message": "Dosya formatı ne olursa olsun (.mrpack dahil), bir dosya yalnızca Modrinth'e yüklendiğinde denetlenir."
},
"unknown-pack-warning-modal.dont-show-again": {
"message": "Bu uyarıyı tekrar gösterme"
},
"unknown-pack-warning-modal.header": {
"message": "İndirmeyi onayla"
},
"unknown-pack-warning-modal.install-anyway": {
"message": "Yine de indir"
},
"unknown-pack-warning-modal.malware-statement": {
"message": "Kötü amaçlı yazılımlar genellikle Discord gibi platformlarda paylaşılan mod paketi dosyaları aracılığıyla yayılır."
},
"unknown-pack-warning-modal.warning.body": {
"message": "Bu dosyayı Modrinth üzerinde bulamadık. Yalnızca güvendiğiniz kaynaklardan gelen dosyaları yüklemenizi şiddetle öneririz."
},
"unknown-pack-warning-modal.warning.title": {
"message": "Bilinmeyen dosya uyarısı"
}
}
+43 -205
View File
@@ -11,87 +11,6 @@
"app.action-bar.hide-more-running-instances": {
"message": "Приховати більше запущених профілів"
},
"app.action-bar.install.copied-details": {
"message": "Скопійовано"
},
"app.action-bar.install.copy-details": {
"message": "Скопіювати деталі"
},
"app.action-bar.install.dismiss": {
"message": "Відхилити"
},
"app.action-bar.install.open-instance": {
"message": "Відкрити екземпляр"
},
"app.action-bar.install.retry": {
"message": "Спробувати знову"
},
"app.action-bar.install.summary.app-closing": {
"message": "Скасовано через закриття додатку"
},
"app.action-bar.install.summary.bad-modpack-file": {
"message": "Не вдалось прочитати збірку модифікацій"
},
"app.action-bar.install.summary.canceled": {
"message": "Скасовано"
},
"app.action-bar.install.summary.cleanup-incomplete": {
"message": "Очищення не завершено"
},
"app.action-bar.install.summary.content-download-failed": {
"message": "Не вдалося завантажити файли"
},
"app.action-bar.install.summary.corrupt-download": {
"message": "Завантажений файл пошкоджений"
},
"app.action-bar.install.summary.could-not-save-files": {
"message": "Не вдалося зберегти файли"
},
"app.action-bar.install.summary.download-failed": {
"message": "Завантаження не вдалося завершити"
},
"app.action-bar.install.summary.instance-not-found": {
"message": "Екземпляр не знайдено"
},
"app.action-bar.install.summary.invalid-file-path": {
"message": "Шлях до файлу недійсний"
},
"app.action-bar.install.summary.invalid-modpack": {
"message": "Дані збірки модів недійсні"
},
"app.action-bar.install.summary.invalid-modpack-files": {
"message": "Файли збірки модифікацій містять не дійсні метадані"
},
"app.action-bar.install.summary.java-setup-failed": {
"message": "Не вдалося завершити встановлення Java"
},
"app.action-bar.install.summary.loader-setup-failed": {
"message": "Помилка встановлення завантажувача"
},
"app.action-bar.install.summary.local-data-error": {
"message": "Не вдалося оновити локальні дані"
},
"app.action-bar.install.summary.minecraft-setup-failed": {
"message": "Не вдалося встановити Minecraft"
},
"app.action-bar.install.summary.modrinth-unreachable": {
"message": "Не вдалося зʼєднатися з Modrinth"
},
"app.action-bar.install.summary.no-write-permission": {
"message": "Немає прав для запису"
},
"app.action-bar.install.summary.pack-download-failed": {
"message": "Не вдалося встановити збірку"
},
"app.action-bar.install.summary.unexpected-error": {
"message": "Щось пішло не так"
},
"app.action-bar.install.unknown-instance": {
"message": "Невідомий екземпляр"
},
"app.action-bar.installs": {
"message": "Установки"
},
"app.action-bar.make-primary-instance": {
"message": "Зробити основним профілем"
},
@@ -182,12 +101,6 @@
"app.appearance-settings.show-play-time.title": {
"message": "Час гри"
},
"app.appearance-settings.skip-non-essential-warnings.description": {
"message": "Автоматично пропускає підтвердження з низьким рівнем ризику, такі як встановлення дублікатів інсталяцій, звичайне видалення вмісту, масові оновлення, від’єднання інсталяцій та запити на відновлення. Попередження про небезпеку й надалі будуть показуватися."
},
"app.appearance-settings.skip-non-essential-warnings.title": {
"message": "Пропустити несуттєві попередження"
},
"app.appearance-settings.toggle-sidebar.description": {
"message": "Умикає можливість перемикання бокової панелі."
},
@@ -234,7 +147,7 @@
"message": "Дослідити сервера"
},
"app.browse.hide-added-servers": {
"message": "Приховати вже додані сервери"
"message": "Сховати наявні сервери"
},
"app.browse.project-type.modpacks": {
"message": "Збірки"
@@ -257,72 +170,24 @@
"app.export-modal.header": {
"message": "Експортувати збірку"
},
"app.export-modal.include-file-accessibility-label": {
"message": "Уключити «{file}»?"
},
"app.export-modal.modpack-name-label": {
"message": "Назва збірки"
},
"app.export-modal.modpack-name-placeholder": {
"message": "Назва збірки"
},
"app.export-modal.select-files-label": {
"message": "Змініть файли, які додані до експорту"
},
"app.export-modal.version-number-label": {
"message": "Номер версії"
},
"app.export-modal.version-number-placeholder": {
"message": "1.0.0"
},
"app.install.phase.downloading_content": {
"message": "Завантаження вмісту"
},
"app.install.phase.downloading_minecraft": {
"message": "Завантаження Minecraft"
},
"app.install.phase.downloading_pack_file": {
"message": "Завантаження файлу пакета"
},
"app.install.phase.extracting_overrides": {
"message": "Розпакування файлів заміщення"
},
"app.install.phase.finalizing": {
"message": "Завершення"
},
"app.install.phase.preparing_instance": {
"message": "У черзі на встановлення"
},
"app.install.phase.preparing_java": {
"message": "Підготовка Java"
},
"app.install.phase.preparing_java.downloading": {
"message": "Завантаження Java {version}"
},
"app.install.phase.preparing_java.extracting": {
"message": "Розпакування Java {version}"
},
"app.install.phase.preparing_java.fetching-metadata": {
"message": "Отримання Java {version}"
},
"app.install.phase.preparing_java.resolving": {
"message": "Підготовка Java {version}"
},
"app.install.phase.preparing_java.validating": {
"message": "Перевірка Java {version}"
},
"app.install.phase.reading_pack_manifest": {
"message": "Читання маніфесту пакета"
},
"app.install.phase.resolving_loader": {
"message": "Визначення завантажувача"
},
"app.install.phase.resolving_minecraft": {
"message": "Узгодження версії Minecraft"
},
"app.install.phase.resolving_pack": {
"message": "Узгодження вмісту"
},
"app.install.phase.rolling_back": {
"message": "Повернення"
},
"app.install.phase.running_loader_processors": {
"message": "Запуск процесорів завантажувача"
},
"app.instance.confirm-delete.admonition-body": {
"message": "Усі дані вашого профілю будуть видалені назавжди, включно з вашими світами, налаштуваннями та всім установленим умістом."
},
@@ -347,15 +212,6 @@
"app.instance.modpack-already-installed.instance": {
"message": "Профіль"
},
"app.instance.mods.bulk-update.downloading-projects": {
"message": "Завантаження проєктів: {current, number}/{total, number}..."
},
"app.instance.mods.bulk-update.finishing": {
"message": "Завершуємо оновлення…"
},
"app.instance.mods.bulk-update.resolving-versions": {
"message": "Знаходимо версії…"
},
"app.instance.mods.content-type-project": {
"message": "проєкт"
},
@@ -380,18 +236,6 @@
"app.instance.worlds.browse-servers": {
"message": "Переглянути сервери"
},
"app.instance.worlds.delete-world-modal.delete-button": {
"message": "Видалити світ"
},
"app.instance.worlds.delete-world-modal.title": {
"message": "Видалити світ"
},
"app.instance.worlds.delete-world-modal.warning-body": {
"message": "Цей світ буде видалено назавжди з цього екземпляра. Це не можна скасувати."
},
"app.instance.worlds.delete-world-modal.warning-header": {
"message": "Видалення {name}"
},
"app.instance.worlds.filter-modded": {
"message": "Модифіковано"
},
@@ -410,53 +254,32 @@
"app.instance.worlds.no-worlds-heading": {
"message": "Сервера або світи не додано"
},
"app.instance.worlds.remove-server-modal.remove-button": {
"message": "Видалити сервер"
},
"app.instance.worlds.remove-server-modal.title": {
"message": "Видалити сервер"
},
"app.instance.worlds.remove-server-modal.warning-body": {
"message": "Цей сервер буде видалено з вашого списку серверів. Ви зможете повернути його, якщо знаєте адресу."
},
"app.instance.worlds.remove-server-modal.warning-header": {
"message": "Видалення {name}"
},
"app.instance.worlds.search-worlds-placeholder": {
"message": "Пошук {count} світів…"
},
"app.modal.install-to-play.dont-install": {
"message": "Не встановлювати"
"app.modal.install-to-play.content-required": {
"message": "Потрібний уміст"
},
"app.modal.install-to-play.header": {
"message": "Установлення для гри"
},
"app.modal.install-to-play.install-anyway": {
"message": "Усе одно встановити"
},
"app.modal.install-to-play.install-button": {
"message": "Установити"
},
"app.modal.install-to-play.invite-warning": {
"message": "Це запрошення створив інший користувач Modrinth, не сам Modrinth. Приймайте запрошення лише від тих людей, яким довіряєте."
},
"app.modal.install-to-play.mod-count": {
"message": "{count, plural, one {# мод} few {# мода} other {# модів}}"
},
"app.modal.install-to-play.reviewed-files": {
"message": "Файл перевірятиметься, лише якщо його завантажено на Modrinth, незалежно від його формату (включно з .mrpack)."
"app.modal.install-to-play.required-modpack": {
"message": "Потрібна збірка"
},
"app.modal.install-to-play.server-requires-mods": {
"message": "Для гри на цьому сервері потрібні моди. Натисніть «Установити», щоб налаштувати необхідні файли з Modrinth, а потім запустіть безпосередньо на сервері."
},
"app.modal.install-to-play.shared-instance": {
"message": "Профіль"
},
"app.modal.install-to-play.unknown-files-description": {
"message": "Ця збірка сервера містить файли, які не є опубліковані на Modrinth. Ми наполегливо рекомендуємо встановлювати лише ті файли, яким довіряєте."
},
"app.modal.install-to-play.unknown-files-warning": {
"message": "Попередження про невідомі файли"
},
"app.modal.install-to-play.unrecognized-files": {
"message": "Нерозпізнані файли"
"app.modal.install-to-play.shared-server-instance": {
"message": "Серверний профіль"
},
"app.modal.install-to-play.view-contents": {
"message": "Дивитися вміст"
@@ -464,8 +287,8 @@
"app.modal.update-to-play.header": {
"message": "Оновлення для гри"
},
"app.modal.update-to-play.removed": {
"message": "Видалено"
"app.modal.update-to-play.update-required": {
"message": "Необхідне оновлення"
},
"app.modal.update-to-play.update-required-description": {
"message": "«{name}» потребує оновлення, щоб грати. Будь ласка, оновіть до останньої версії, щоб запустити гру."
@@ -473,12 +296,12 @@
"app.project.install-button.already-installed": {
"message": "Проєкт уже встановлено"
},
"app.project.install-button.switch-version": {
"message": "Змінити версію"
},
"app.project.install-context.back-to-browse": {
"message": "Повернутися до дослідження"
},
"app.project.install-context.install-content-to-instance": {
"message": "Установити вміст до профілю"
},
"app.settings.developer-mode-enabled": {
"message": "Увімкнено режим розробника."
},
@@ -599,9 +422,6 @@
"app.skins.section.builders-and-biomes": {
"message": "Builders & Biomes"
},
"app.skins.section.chaos-cubed": {
"message": "Chaos Cubed"
},
"app.skins.section.chase-the-skies": {
"message": "Chase the Skies"
},
@@ -804,7 +624,7 @@
"message": "Додавання файлів ({completed}/{total})"
},
"instance.files.save-as": {
"message": "Зберегти як..."
"message": "Зберегти як"
},
"instance.server-modal.address": {
"message": "Адреса"
@@ -1019,9 +839,6 @@
"instance.worlds.copy_address": {
"message": "Скопіювати адресу"
},
"instance.worlds.create_shortcut": {
"message": "Створити ярлик"
},
"instance.worlds.dont_show_on_home": {
"message": "Не показувати на головній"
},
@@ -1096,5 +913,26 @@
},
"search.filter.locked.server-loader.title": {
"message": "Завантажувач наданий сервером"
},
"unknown-pack-warning-modal.body": {
"message": "Файл перевірятиметься лише, якщо його завантажено на Modrinth, незалежно від його формату (включно з .mrpack)."
},
"unknown-pack-warning-modal.dont-show-again": {
"message": "Не показувати більше це попередження"
},
"unknown-pack-warning-modal.header": {
"message": "Підтвердити встановлення"
},
"unknown-pack-warning-modal.install-anyway": {
"message": "Усе одно встановити"
},
"unknown-pack-warning-modal.malware-statement": {
"message": "Шкідливе програмне забезпечення часто поширюють через файли збірок, які публікуються на таких платформах, як Discord."
},
"unknown-pack-warning-modal.warning.body": {
"message": "Ми не змогли знайти цей файл на Modrinth. Ми рекомендуємо встановлювати файли лише з тих джерел яким ви довіряєте."
},
"unknown-pack-warning-modal.warning.title": {
"message": "Попередження про невідомий файл"
}
}
+49 -61
View File
@@ -11,33 +11,6 @@
"app.action-bar.hide-more-running-instances": {
"message": "Đóng các phiên bản đang chạy"
},
"app.action-bar.install.retry": {
"message": "Thử lại"
},
"app.action-bar.install.summary.app-closing": {
"message": "Đã hủy do đóng ứng dụng"
},
"app.action-bar.install.summary.bad-modpack-file": {
"message": "Không đọc được modpack"
},
"app.action-bar.install.summary.canceled": {
"message": "Đã hủy"
},
"app.action-bar.install.summary.cleanup-incomplete": {
"message": "Dọn dẹp không hoàn thện"
},
"app.action-bar.install.summary.content-download-failed": {
"message": "Không tải được tệp"
},
"app.action-bar.install.summary.corrupt-download": {
"message": "Tệp được tải bị hỏng"
},
"app.action-bar.install.summary.could-not-save-files": {
"message": "Không lưu được tệp"
},
"app.action-bar.install.summary.download-failed": {
"message": "Không hoàn tất tải xuống"
},
"app.action-bar.make-primary-instance": {
"message": "Đặt làm bản instance chính"
},
@@ -173,6 +146,9 @@
"app.browse.discover-servers": {
"message": "Khám phá máy chủ"
},
"app.browse.hide-added-servers": {
"message": "Ẩn các server đã thêm"
},
"app.browse.project-type.modpacks": {
"message": "Modpack"
},
@@ -194,9 +170,18 @@
"app.export-modal.header": {
"message": "Xuất modpack"
},
"app.export-modal.include-file-accessibility-label": {
"message": "Bao gồm \"{file}\"?"
},
"app.export-modal.modpack-name-label": {
"message": "Tên modpack"
},
"app.export-modal.modpack-name-placeholder": {
"message": "Tên modpack"
},
"app.export-modal.select-files-label": {
"message": "Cấu hình các tệp nào được bao gồm trong quá trình xuất phiên bản này"
},
"app.export-modal.version-number-label": {
"message": "Phiên bản"
},
@@ -251,15 +236,6 @@
"app.instance.worlds.browse-servers": {
"message": "Tìm kiếm máy chủ"
},
"app.instance.worlds.delete-world-modal.delete-button": {
"message": "Xóa thế giới"
},
"app.instance.worlds.delete-world-modal.title": {
"message": "Xóa thế giới"
},
"app.instance.worlds.delete-world-modal.warning-header": {
"message": "Đang xóa {name}"
},
"app.instance.worlds.filter-modded": {
"message": "Modded"
},
@@ -278,44 +254,41 @@
"app.instance.worlds.no-worlds-heading": {
"message": "Không có máy chủ hay thế giới nào đã được thêm"
},
"app.instance.worlds.remove-server-modal.remove-button": {
"message": "Bỏ khỏi danh sách"
},
"app.instance.worlds.remove-server-modal.title": {
"message": "Xóa máy chủ"
},
"app.instance.worlds.remove-server-modal.warning-header": {
"message": "{name} sẽ bị xóa"
},
"app.instance.worlds.search-worlds-placeholder": {
"message": "Tìm kiếm {count} world..."
},
"app.modal.install-to-play.dont-install": {
"message": "Ngừng tải"
"app.modal.install-to-play.content-required": {
"message": "Nội dung bắt buộc"
},
"app.modal.install-to-play.header": {
"message": "Tải xuống để chơi"
},
"app.modal.install-to-play.install-anyway": {
"message": "Tải tiếp"
},
"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"
},
"app.modal.install-to-play.server-requires-mods": {
"message": "Máy chủ này yêu cầu mod để có thể chơi. Vui lòng ấn vào tải xuống và tải các tệp bắt buộc từ Modrinth và khởi chạy trực tiếp để tham gia máy chủ."
},
"app.modal.install-to-play.shared-instance": {
"message": "Chia sẻ hồ sơ"
},
"app.modal.install-to-play.shared-server-instance": {
"message": "Chia sẻ hồ sơ máy chủ"
},
"app.modal.install-to-play.view-contents": {
"message": "Xem nội dung"
},
"app.modal.update-to-play.header": {
"message": "Cập nhật và bắt đầu chơi"
},
"app.modal.update-to-play.removed": {
"message": "Đã bỏ"
"app.modal.update-to-play.update-required": {
"message": "Yêu cầu cập nhật"
},
"app.modal.update-to-play.update-required-description": {
"message": "Bạn cần cập nhật {name} để có thể chơi. Vui lòng cập nhật lên bản mới nhất để khởi chạy trò chơi."
@@ -323,12 +296,12 @@
"app.project.install-button.already-installed": {
"message": "Dự án này đã được cài đặt"
},
"app.project.install-button.switch-version": {
"message": "Đổi phiên bản"
},
"app.project.install-context.back-to-browse": {
"message": "Quay lại trang Khám Phá"
},
"app.project.install-context.install-content-to-instance": {
"message": "Cài đặt nội dung vào phiên bản"
},
"app.settings.developer-mode-enabled": {
"message": "Chế độ nhà phát triển đã được bật."
},
@@ -449,9 +422,6 @@
"app.skins.section.builders-and-biomes": {
"message": "Builders & Biomes"
},
"app.skins.section.chaos-cubed": {
"message": "Chaos Cubed"
},
"app.skins.section.chase-the-skies": {
"message": "Chase the Skies"
},
@@ -869,9 +839,6 @@
"instance.worlds.copy_address": {
"message": "Sao chép địa chỉ"
},
"instance.worlds.create_shortcut": {
"message": "Tạo lối tắt"
},
"instance.worlds.dont_show_on_home": {
"message": "Không hiện ở Trang chủ"
},
@@ -946,5 +913,26 @@
},
"search.filter.locked.server-loader.title": {
"message": "Loader được cung cấp bởi máy chủ"
},
"unknown-pack-warning-modal.body": {
"message": "Tệp chỉ được xem xét nếu nó được tải lên Modrinth, bất kể định dạng tệp của nó là gì (bao gồm cả .mrpack)."
},
"unknown-pack-warning-modal.dont-show-again": {
"message": "Đừng hiển thị cảnh báo này nữa"
},
"unknown-pack-warning-modal.header": {
"message": "Xác nhận cài đặt"
},
"unknown-pack-warning-modal.install-anyway": {
"message": "Tiếp tục cài đặt"
},
"unknown-pack-warning-modal.malware-statement": {
"message": "Phần mềm độc hại thường được phát tán thông qua các tệp modpack bằng cách chia sẻ chúng trên các nền tảng như Discord."
},
"unknown-pack-warning-modal.warning.body": {
"message": "Chúng tôi không tìm thấy tập tin này trên Modrinth. Chúng tôi đặc biệt khuyên bạn chỉ nên cài đặt các tập tin từ các nguồn đáng tin cậy."
},
"unknown-pack-warning-modal.warning.title": {
"message": "Cảnh báo tệp không xác định"
}
}
+49 -223
View File
@@ -11,87 +11,6 @@
"app.action-bar.hide-more-running-instances": {
"message": "隐藏更多运行中的实例"
},
"app.action-bar.install.copied-details": {
"message": "已复制"
},
"app.action-bar.install.copy-details": {
"message": "复制详细信息"
},
"app.action-bar.install.dismiss": {
"message": "忽略"
},
"app.action-bar.install.open-instance": {
"message": "打开实例"
},
"app.action-bar.install.retry": {
"message": "重试"
},
"app.action-bar.install.summary.app-closing": {
"message": "由于应用关闭而取消"
},
"app.action-bar.install.summary.bad-modpack-file": {
"message": "整合包读取失败"
},
"app.action-bar.install.summary.canceled": {
"message": "已取消"
},
"app.action-bar.install.summary.cleanup-incomplete": {
"message": "未完成清理"
},
"app.action-bar.install.summary.content-download-failed": {
"message": "无法下载文件"
},
"app.action-bar.install.summary.corrupt-download": {
"message": "下载的文件已损坏"
},
"app.action-bar.install.summary.could-not-save-files": {
"message": "无法保存文件"
},
"app.action-bar.install.summary.download-failed": {
"message": "无法完成下载"
},
"app.action-bar.install.summary.instance-not-found": {
"message": "无法找到实例"
},
"app.action-bar.install.summary.invalid-file-path": {
"message": "文件路径无效"
},
"app.action-bar.install.summary.invalid-modpack": {
"message": "整合包数据无效"
},
"app.action-bar.install.summary.invalid-modpack-files": {
"message": "整合包文件含有无效元数据"
},
"app.action-bar.install.summary.java-setup-failed": {
"message": "无法完成 Java 配置"
},
"app.action-bar.install.summary.loader-setup-failed": {
"message": "加载器配置失败"
},
"app.action-bar.install.summary.local-data-error": {
"message": "无法更新本地数据"
},
"app.action-bar.install.summary.minecraft-setup-failed": {
"message": "Minecraft 配置失败"
},
"app.action-bar.install.summary.modrinth-unreachable": {
"message": "无法连接 Modrinth"
},
"app.action-bar.install.summary.no-write-permission": {
"message": "没有写入权限"
},
"app.action-bar.install.summary.pack-download-failed": {
"message": "无法下载包"
},
"app.action-bar.install.summary.unexpected-error": {
"message": "出错了"
},
"app.action-bar.install.unknown-instance": {
"message": "未知的实例"
},
"app.action-bar.installs": {
"message": "安装"
},
"app.action-bar.make-primary-instance": {
"message": "设为主实例"
},
@@ -182,12 +101,6 @@
"app.appearance-settings.show-play-time.title": {
"message": "显示总游玩时间"
},
"app.appearance-settings.skip-non-essential-warnings.description": {
"message": "自动跳过低风险确认,包括重复安装整合包、常规内容删除、批量更新、解除整合包关联及修复提示。危险警告仍会显示。"
},
"app.appearance-settings.skip-non-essential-warnings.title": {
"message": "跳过非必要的警告"
},
"app.appearance-settings.toggle-sidebar.description": {
"message": "启用切换侧边栏的功能。"
},
@@ -195,7 +108,7 @@
"message": "切换侧边栏"
},
"app.appearance-settings.unknown-pack-warning.description": {
"message": "如果你尝试安装一个未托管在 Modrinth 上的 Modrinth 整合包文件(.mrpack),我们会确保你在安装之前了解相关风险。"
"message": "如果你尝试安装一个未托管在 Modrinth 上的 Modrinth 包文件(.mrpack),我们会确保你在安装之前了解相关风险。"
},
"app.appearance-settings.unknown-pack-warning.title": {
"message": "安装未知整合包前提醒我"
@@ -207,7 +120,7 @@
"message": "无法连接到身份验证服务器"
},
"app.browse.add-servers-to-instance": {
"message": "添加服务器到实例"
"message": "正在添加服务器到实例"
},
"app.browse.add-to-an-instance": {
"message": "添加到实例"
@@ -257,72 +170,24 @@
"app.export-modal.header": {
"message": "导出整合包"
},
"app.export-modal.include-file-accessibility-label": {
"message": "包含 \"{file}\""
},
"app.export-modal.modpack-name-label": {
"message": "整合包名称"
},
"app.export-modal.modpack-name-placeholder": {
"message": "整合包名称"
},
"app.export-modal.select-files-label": {
"message": "配置此导出中包含哪些文件"
},
"app.export-modal.version-number-label": {
"message": "版本号"
},
"app.export-modal.version-number-placeholder": {
"message": "1.0.0"
},
"app.install.phase.downloading_content": {
"message": "正在下载内容"
},
"app.install.phase.downloading_minecraft": {
"message": "正在下载 Minecraft"
},
"app.install.phase.downloading_pack_file": {
"message": "正在下载包文件"
},
"app.install.phase.extracting_overrides": {
"message": "提取覆盖项"
},
"app.install.phase.finalizing": {
"message": "完成"
},
"app.install.phase.preparing_instance": {
"message": "已加入安装队列"
},
"app.install.phase.preparing_java": {
"message": "正在准备 Java"
},
"app.install.phase.preparing_java.downloading": {
"message": "正在下载 Java {version}"
},
"app.install.phase.preparing_java.extracting": {
"message": "正在提取 Java {version}"
},
"app.install.phase.preparing_java.fetching-metadata": {
"message": "正在获取 Java {version}"
},
"app.install.phase.preparing_java.resolving": {
"message": "正在准备 Java {version}"
},
"app.install.phase.preparing_java.validating": {
"message": "正在校验 Java {version}"
},
"app.install.phase.reading_pack_manifest": {
"message": "正在读取整合包清单"
},
"app.install.phase.resolving_loader": {
"message": "正在解析加载器"
},
"app.install.phase.resolving_minecraft": {
"message": "正在解析 Minecraft"
},
"app.install.phase.resolving_pack": {
"message": "正在解析内容"
},
"app.install.phase.rolling_back": {
"message": "回滚"
},
"app.install.phase.running_loader_processors": {
"message": "正在运行加载处理器"
},
"app.instance.confirm-delete.admonition-body": {
"message": "你实例的所有数据将被永久删除,包括你的世界、配置和所有已安装的内容。"
},
@@ -347,15 +212,6 @@
"app.instance.modpack-already-installed.instance": {
"message": "实例"
},
"app.instance.mods.bulk-update.downloading-projects": {
"message": "正在下载 {current, number}/{total, number}个 项目……"
},
"app.instance.mods.bulk-update.finishing": {
"message": "正在结束更新……"
},
"app.instance.mods.bulk-update.resolving-versions": {
"message": "正在解析版本……"
},
"app.instance.mods.content-type-project": {
"message": "项目"
},
@@ -380,20 +236,8 @@
"app.instance.worlds.browse-servers": {
"message": "浏览服务器"
},
"app.instance.worlds.delete-world-modal.delete-button": {
"message": "删除世界"
},
"app.instance.worlds.delete-world-modal.title": {
"message": "删除世界"
},
"app.instance.worlds.delete-world-modal.warning-body": {
"message": "这个世界将会被永久删除,此操作无法撤销。"
},
"app.instance.worlds.delete-world-modal.warning-header": {
"message": "正在删除 {name}"
},
"app.instance.worlds.filter-modded": {
"message": "模组"
"message": "模组适配"
},
"app.instance.worlds.filter-offline": {
"message": "离线"
@@ -410,53 +254,32 @@
"app.instance.worlds.no-worlds-heading": {
"message": "未添加服务器或世界"
},
"app.instance.worlds.remove-server-modal.remove-button": {
"message": "删除服务器"
},
"app.instance.worlds.remove-server-modal.title": {
"message": "删除服务器"
},
"app.instance.worlds.remove-server-modal.warning-body": {
"message": "该服务器将会被移出你的服务器列表,也包括游戏内的服务器列表。之后如果你记得地址,仍可将其添加回来。"
},
"app.instance.worlds.remove-server-modal.warning-header": {
"message": "正在删除 {name}"
},
"app.instance.worlds.search-worlds-placeholder": {
"message": "搜索 {count} 个世界……"
},
"app.modal.install-to-play.dont-install": {
"message": "不要安装"
"app.modal.install-to-play.content-required": {
"message": "需求内容"
},
"app.modal.install-to-play.header": {
"message": "安装以游玩"
},
"app.modal.install-to-play.install-anyway": {
"message": "无视风险继续安装"
},
"app.modal.install-to-play.install-button": {
"message": "安装"
},
"app.modal.install-to-play.invite-warning": {
"message": "这个邀请是由另一位 Modrinth 用户创建的,而不是 Modrinth 官方。只接受你信任的人的邀请。"
},
"app.modal.install-to-play.mod-count": {
"message": "{count} 个模组"
},
"app.modal.install-to-play.reviewed-files": {
"message": "只有上传到 Modrinth 的文件才会经过审核,无论其文件格式如何(包括 .mrpack)。"
"app.modal.install-to-play.required-modpack": {
"message": "需求整合包"
},
"app.modal.install-to-play.server-requires-mods": {
"message": "此服务器需要安装模组才能游玩。点击安装,从 Modrinth 下载所需文件,然后直接进入服务器。"
},
"app.modal.install-to-play.shared-instance": {
"message": "共享实例"
},
"app.modal.install-to-play.unknown-files-description": {
"message": "这个整合包包含一些未在 Modrinth 发布的文件。我们强烈建议只从你信任的来源安装文件。"
},
"app.modal.install-to-play.unknown-files-warning": {
"message": "未知文件警告"
},
"app.modal.install-to-play.unrecognized-files": {
"message": "未识别文件"
"app.modal.install-to-play.shared-server-instance": {
"message": "共享的服务端实例"
},
"app.modal.install-to-play.view-contents": {
"message": "查看内容"
@@ -464,8 +287,8 @@
"app.modal.update-to-play.header": {
"message": "更新以游玩"
},
"app.modal.update-to-play.removed": {
"message": "已移除"
"app.modal.update-to-play.update-required": {
"message": "需要更新"
},
"app.modal.update-to-play.update-required-description": {
"message": "需要更新至最新版本才能运行 {name}。请更新后启动游戏。"
@@ -473,23 +296,11 @@
"app.project.install-button.already-installed": {
"message": "此项目已安装"
},
"app.project.install-button.switch-version": {
"message": "切换版本"
},
"app.project.install-context.back-to-browse": {
"message": "返回发现页"
},
"app.project.version.all-versions": {
"message": "所有版本"
},
"app.project.version.download-in-browser": {
"message": "在浏览器中下载"
},
"app.project.version.installing": {
"message": "安装中"
},
"app.project.versions.already-installed": {
"message": "已安装"
"app.project.install-context.install-content-to-instance": {
"message": "将内容安装到实例中"
},
"app.settings.developer-mode-enabled": {
"message": "开发者模式已启用。"
@@ -611,9 +422,6 @@
"app.skins.section.builders-and-biomes": {
"message": "建造者与生物群系"
},
"app.skins.section.chaos-cubed": {
"message": "混沌立方"
},
"app.skins.section.chase-the-skies": {
"message": "追逐天空"
},
@@ -843,7 +651,7 @@
"message": "将从设备中永久删除该实例,包括所有存档、配置文件及已安装内容。此操作不可撤销,请谨慎操作。"
},
"instance.settings.tabs.general.deleting.button": {
"message": "删除中……"
"message": "删除中..."
},
"instance.settings.tabs.general.duplicate-button": {
"message": "复制"
@@ -924,7 +732,7 @@
"message": "在游戏关闭后运行。"
},
"instance.settings.tabs.hooks.post-exit.enter": {
"message": "输入退出后运行的命令…"
"message": "输入退出后运行的命令…"
},
"instance.settings.tabs.hooks.pre-launch": {
"message": "启动前"
@@ -933,7 +741,7 @@
"message": "在实例启动前运行。"
},
"instance.settings.tabs.hooks.pre-launch.enter": {
"message": "输入启动前命令……"
"message": "输入启动前命令..."
},
"instance.settings.tabs.hooks.title": {
"message": "游戏启动钩子"
@@ -945,7 +753,7 @@
"message": "用于启动 Minecraft 的包装器命令。"
},
"instance.settings.tabs.hooks.wrapper.enter": {
"message": "输入封装命令……"
"message": "输入封装命令..."
},
"instance.settings.tabs.installation": {
"message": "安装"
@@ -1011,7 +819,7 @@
"message": "游戏启动时的窗口高度。"
},
"instance.settings.tabs.window.height.enter": {
"message": "输入高度…"
"message": "输入高度…"
},
"instance.settings.tabs.window.width": {
"message": "宽度"
@@ -1020,7 +828,7 @@
"message": "游戏启动时的窗口宽度。"
},
"instance.settings.tabs.window.width.enter": {
"message": "输入宽度…"
"message": "输入宽度…"
},
"instance.worlds.a_minecraft_server": {
"message": "Minecraft 服务器"
@@ -1031,9 +839,6 @@
"instance.worlds.copy_address": {
"message": "复制地址"
},
"instance.worlds.create_shortcut": {
"message": "创建快捷方式"
},
"instance.worlds.dont_show_on_home": {
"message": "在主界面隐藏"
},
@@ -1108,5 +913,26 @@
},
"search.filter.locked.server-loader.title": {
"message": "加载器由服务器提供"
},
"unknown-pack-warning-modal.body": {
"message": "只有上传到 Modrinth 的文件才会经过审核,无论其文件格式如何(包括 .mrpack)。"
},
"unknown-pack-warning-modal.dont-show-again": {
"message": "不再显示此警告"
},
"unknown-pack-warning-modal.header": {
"message": "确认安装"
},
"unknown-pack-warning-modal.install-anyway": {
"message": "仍然安装"
},
"unknown-pack-warning-modal.malware-statement": {
"message": "恶意软件常常通过模组包文件在 Discord 等平台上分享而传播。"
},
"unknown-pack-warning-modal.warning.body": {
"message": "我们在 Modrinth 上找不到此文件。强烈建议你仅从可信来源安装文件。"
},
"unknown-pack-warning-modal.warning.title": {
"message": "未知文件警告"
}
}
+41 -215
View File
@@ -11,87 +11,6 @@
"app.action-bar.hide-more-running-instances": {
"message": "隱藏更多執行中的實例"
},
"app.action-bar.install.copied-details": {
"message": "已複製"
},
"app.action-bar.install.copy-details": {
"message": "複製詳細資訊"
},
"app.action-bar.install.dismiss": {
"message": "忽略"
},
"app.action-bar.install.open-instance": {
"message": "開啟實例"
},
"app.action-bar.install.retry": {
"message": "重試"
},
"app.action-bar.install.summary.app-closing": {
"message": "由於應用程式關閉而取消"
},
"app.action-bar.install.summary.bad-modpack-file": {
"message": "無法讀取模組包"
},
"app.action-bar.install.summary.canceled": {
"message": "已取消"
},
"app.action-bar.install.summary.cleanup-incomplete": {
"message": "未完成清理"
},
"app.action-bar.install.summary.content-download-failed": {
"message": "無法下載檔案"
},
"app.action-bar.install.summary.corrupt-download": {
"message": "下載的檔案已損毀"
},
"app.action-bar.install.summary.could-not-save-files": {
"message": "無法儲存檔案"
},
"app.action-bar.install.summary.download-failed": {
"message": "無法完成下載"
},
"app.action-bar.install.summary.instance-not-found": {
"message": "找不到實例"
},
"app.action-bar.install.summary.invalid-file-path": {
"message": "檔案路徑無效"
},
"app.action-bar.install.summary.invalid-modpack": {
"message": "模組包資料無效"
},
"app.action-bar.install.summary.invalid-modpack-files": {
"message": "模組包檔案的詮釋資料無效"
},
"app.action-bar.install.summary.java-setup-failed": {
"message": "無法完成 Java 設定"
},
"app.action-bar.install.summary.loader-setup-failed": {
"message": "無法設定載入器"
},
"app.action-bar.install.summary.local-data-error": {
"message": "無法更新本地資料"
},
"app.action-bar.install.summary.minecraft-setup-failed": {
"message": "無法設定 Minecraft"
},
"app.action-bar.install.summary.modrinth-unreachable": {
"message": "無法連線到 Modrinth"
},
"app.action-bar.install.summary.no-write-permission": {
"message": "沒有寫入權限"
},
"app.action-bar.install.summary.pack-download-failed": {
"message": "無法下載模組包"
},
"app.action-bar.install.summary.unexpected-error": {
"message": "出了點狀況"
},
"app.action-bar.install.unknown-instance": {
"message": "未知的實例"
},
"app.action-bar.installs": {
"message": "安裝"
},
"app.action-bar.make-primary-instance": {
"message": "設為主要實例"
},
@@ -182,12 +101,6 @@
"app.appearance-settings.show-play-time.title": {
"message": "顯示遊玩時數"
},
"app.appearance-settings.skip-non-essential-warnings.description": {
"message": "自動跳過低風險操作的確認提示,例如重複安裝模組包、常規內容刪除、批次更新、取消連結模組包以及修復提示。高風險警告仍會顯示。"
},
"app.appearance-settings.skip-non-essential-warnings.title": {
"message": "跳過非必要的警告"
},
"app.appearance-settings.toggle-sidebar.description": {
"message": "啟用切換側邊欄的功能。"
},
@@ -249,7 +162,7 @@
"message": "沒有符合 {compatibilityLabel} 的可用版本。請選擇一個版本強制安裝。不會自動安裝相依模組。"
},
"app.export-modal.description-placeholder": {
"message": "輸入模組包描述……"
"message": "輸入模組包描述..."
},
"app.export-modal.export-button": {
"message": "匯出"
@@ -257,72 +170,24 @@
"app.export-modal.header": {
"message": "匯出模組包"
},
"app.export-modal.include-file-accessibility-label": {
"message": "要包含「{file}」嗎?"
},
"app.export-modal.modpack-name-label": {
"message": "模組包名稱"
},
"app.export-modal.modpack-name-placeholder": {
"message": "模組包名稱"
},
"app.export-modal.select-files-label": {
"message": "設定要包含在此匯出檔案中的檔案"
},
"app.export-modal.version-number-label": {
"message": "版本號碼"
},
"app.export-modal.version-number-placeholder": {
"message": "1.0.0"
},
"app.install.phase.downloading_content": {
"message": "正在下載內容"
},
"app.install.phase.downloading_minecraft": {
"message": "正在下載 Minecraft"
},
"app.install.phase.downloading_pack_file": {
"message": "正在下載模組包檔案"
},
"app.install.phase.extracting_overrides": {
"message": "正在解壓縮覆寫檔案"
},
"app.install.phase.finalizing": {
"message": "即將完成"
},
"app.install.phase.preparing_instance": {
"message": "已排入安裝佇列"
},
"app.install.phase.preparing_java": {
"message": "正在準備 Java"
},
"app.install.phase.preparing_java.downloading": {
"message": "正在下載 Java {version}"
},
"app.install.phase.preparing_java.extracting": {
"message": "正在解壓縮 Java {version}"
},
"app.install.phase.preparing_java.fetching-metadata": {
"message": "正在擷取 Java {version}"
},
"app.install.phase.preparing_java.resolving": {
"message": "正在準備 Java {version}"
},
"app.install.phase.preparing_java.validating": {
"message": "正在驗證 Java {version}"
},
"app.install.phase.reading_pack_manifest": {
"message": "正在讀取模組包清單"
},
"app.install.phase.resolving_loader": {
"message": "正在解析載入器"
},
"app.install.phase.resolving_minecraft": {
"message": "正在解析 Minecraft"
},
"app.install.phase.resolving_pack": {
"message": "正在解析內容"
},
"app.install.phase.rolling_back": {
"message": "正在復原"
},
"app.install.phase.running_loader_processors": {
"message": "正在執行載入器處理程式"
},
"app.instance.confirm-delete.admonition-body": {
"message": "你實例中的所有資料將被永久刪除,包含你的世界、設定檔以及所有已安裝的內容。"
},
@@ -347,15 +212,6 @@
"app.instance.modpack-already-installed.instance": {
"message": "實例"
},
"app.instance.mods.bulk-update.downloading-projects": {
"message": "正在下載專案 {current, number}/{total, number}..."
},
"app.instance.mods.bulk-update.finishing": {
"message": "正在結束更新..."
},
"app.instance.mods.bulk-update.resolving-versions": {
"message": "正在解析版本..."
},
"app.instance.mods.content-type-project": {
"message": "專案"
},
@@ -380,18 +236,6 @@
"app.instance.worlds.browse-servers": {
"message": "瀏覽伺服器"
},
"app.instance.worlds.delete-world-modal.delete-button": {
"message": "刪除世界"
},
"app.instance.worlds.delete-world-modal.title": {
"message": "刪除世界"
},
"app.instance.worlds.delete-world-modal.warning-body": {
"message": "這個世界將會被永久刪除,此操作無法復原。"
},
"app.instance.worlds.delete-world-modal.warning-header": {
"message": "即將刪除 {name}"
},
"app.instance.worlds.filter-modded": {
"message": "模組"
},
@@ -410,53 +254,32 @@
"app.instance.worlds.no-worlds-heading": {
"message": "未新增任何伺服器或世界"
},
"app.instance.worlds.remove-server-modal.remove-button": {
"message": "移除伺服器"
},
"app.instance.worlds.remove-server-modal.title": {
"message": "移除伺服器"
},
"app.instance.worlds.remove-server-modal.warning-body": {
"message": "這個伺服器將從你的伺服器清單中移除。如果你記得伺服器位址,稍後可以將其重新新增回來。"
},
"app.instance.worlds.remove-server-modal.warning-header": {
"message": "即將移除 {name}"
},
"app.instance.worlds.search-worlds-placeholder": {
"message": "搜尋 {count} 個世界..."
},
"app.modal.install-to-play.dont-install": {
"message": "不要安裝"
"app.modal.install-to-play.content-required": {
"message": "所需內容"
},
"app.modal.install-to-play.header": {
"message": "安裝以遊玩"
},
"app.modal.install-to-play.install-anyway": {
"message": "仍要安裝"
},
"app.modal.install-to-play.install-button": {
"message": "安裝"
},
"app.modal.install-to-play.invite-warning": {
"message": "這份邀請是由另一位 Modrinth 使用者建立,並非由 Modrinth 官方建立。請僅接受來自你信任的人的邀請。"
},
"app.modal.install-to-play.mod-count": {
"message": "{count, plural, other {# 個模組}}"
},
"app.modal.install-to-play.reviewed-files": {
"message": "只有在 Modrinth 上發布的檔案才會經過審查,無論其檔案格式為何(包含 .mrpack)。"
"app.modal.install-to-play.required-modpack": {
"message": "所需模組包"
},
"app.modal.install-to-play.server-requires-mods": {
"message": "這個伺服器需要模組才能遊玩。請點選「安裝」以從 Modrinth 設定所需的檔案,完成後即可直接加入伺服器。"
},
"app.modal.install-to-play.shared-instance": {
"message": "共用實例"
},
"app.modal.install-to-play.unknown-files-description": {
"message": "這個伺服器模組包中包含未在 Modrinth 上發布的檔案。強烈建議你僅安裝來自信任來源的檔案。"
},
"app.modal.install-to-play.unknown-files-warning": {
"message": "未知檔案警告"
},
"app.modal.install-to-play.unrecognized-files": {
"message": "無法辨識的檔案"
"app.modal.install-to-play.shared-server-instance": {
"message": "共用伺服器實例"
},
"app.modal.install-to-play.view-contents": {
"message": "檢視內容"
@@ -464,8 +287,8 @@
"app.modal.update-to-play.header": {
"message": "更新以遊玩"
},
"app.modal.update-to-play.removed": {
"message": "已移除"
"app.modal.update-to-play.update-required": {
"message": "需要更新"
},
"app.modal.update-to-play.update-required-description": {
"message": "需要更新才能遊玩「{name}」。請更新至最新版本以啟動遊戲。"
@@ -473,23 +296,11 @@
"app.project.install-button.already-installed": {
"message": "這個專案已安裝"
},
"app.project.install-button.switch-version": {
"message": "切換版本"
},
"app.project.install-context.back-to-browse": {
"message": "返回瀏覽"
},
"app.project.version.all-versions": {
"message": "所有版本"
},
"app.project.version.download-in-browser": {
"message": "在瀏覽器中下載"
},
"app.project.version.installing": {
"message": "正在安裝"
},
"app.project.versions.already-installed": {
"message": "已安裝"
"app.project.install-context.install-content-to-instance": {
"message": "將內容安裝至實例"
},
"app.settings.developer-mode-enabled": {
"message": "開發人員模式已啟用。"
@@ -611,9 +422,6 @@
"app.skins.section.builders-and-biomes": {
"message": "建造者&生態域"
},
"app.skins.section.chaos-cubed": {
"message": "混沌立方"
},
"app.skins.section.chase-the-skies": {
"message": "追逐天空"
},
@@ -1031,9 +839,6 @@
"instance.worlds.copy_address": {
"message": "複製位址"
},
"instance.worlds.create_shortcut": {
"message": "建立捷徑"
},
"instance.worlds.dont_show_on_home": {
"message": "不要在首頁顯示"
},
@@ -1108,5 +913,26 @@
},
"search.filter.locked.server-loader.title": {
"message": "載入器由伺服器提供"
},
"unknown-pack-warning-modal.body": {
"message": "只有上傳至 Modrinth 的檔案才會經過審查,無論其檔案格式為何(包含 .mrpack)。"
},
"unknown-pack-warning-modal.dont-show-again": {
"message": "不要再顯示這則警告"
},
"unknown-pack-warning-modal.header": {
"message": "確認安裝"
},
"unknown-pack-warning-modal.install-anyway": {
"message": "仍要安裝"
},
"unknown-pack-warning-modal.malware-statement": {
"message": "惡意軟體經常透過 Discord 等平臺分享模組包檔案來進行傳播。"
},
"unknown-pack-warning-modal.warning.body": {
"message": "我們在 Modrinth 上找不到這個檔案。強烈建議你僅安裝來自信任來源的檔案。"
},
"unknown-pack-warning-modal.warning.title": {
"message": "未知檔案警告"
}
}
+6 -34
View File
@@ -22,8 +22,6 @@ import {
preferencesDiffer,
provideBrowseManager,
requestInstall,
stripServerRuntimeInstallFilters,
stripServerRuntimeInstallOverrides,
useBrowseSearch,
useDebugLogger,
useVIntl,
@@ -49,7 +47,6 @@ import {
get_installed_project_ids as getInstalledProjectIds,
} from '@/helpers/instance'
import { get_loader_versions as getLoaderManifest } from '@/helpers/metadata'
import { get as getSettings, set as setSettings } from '@/helpers/settings.ts'
import { get_categories, get_game_versions, get_loaders } from '@/helpers/tags'
import { get_instance_worlds } from '@/helpers/worlds'
import { injectContentInstall } from '@/providers/content-install'
@@ -72,7 +69,6 @@ const debugLog = useDebugLogger('Browse')
const router = useRouter()
const route = useRoute()
const themeStore = useTheming()
const browseRouteActive = computed(() => route.path.startsWith('/browse/'))
const serverSetupModalRef = ref<InstanceType<typeof CreationFlowModal> | null>(null)
const serverInstallContent = createServerInstallContent({ serverSetupModalRef })
provideServerInstallContent(serverInstallContent)
@@ -407,7 +403,7 @@ const messages = defineMessages({
},
hideAddedServers: {
id: 'app.browse.hide-added-servers',
defaultMessage: 'Hide servers already added',
defaultMessage: 'Hide already added servers',
},
installingToServer: {
id: 'app.browse.server.installing',
@@ -491,9 +487,6 @@ function resetInstanceContext() {
watch(
() => route.params.projectType as ProjectType,
async (newType) => {
if (!browseRouteActive.value) {
return
}
if (isSetupServerContext.value) {
enforceSetupModpackRoute(newType)
if (newType !== 'modpack') return
@@ -687,6 +680,7 @@ async function chooseInstanceInstallVersion(
const selectedVersion = getLatestMatchingInstallVersion(
await getInstallProjectVersions(project.project_id),
selectedPreferences,
projectTypeValue,
)
if (!selectedVersion) {
@@ -769,15 +763,11 @@ function getCardActions(
project: projectResult,
contentType,
mode: isModpack ? 'immediate' : 'queue',
selectedFilters: isModpack
? []
: stripServerRuntimeInstallFilters(searchState.currentFilters.value),
selectedFilters: isModpack ? [] : searchState.currentFilters.value,
providedFilters: isModpack ? [] : combinedProvidedFilters.value,
overriddenProvidedFilterTypes: isModpack
? []
: stripServerRuntimeInstallOverrides(
searchState.overriddenProvidedFilterTypes.value,
),
: searchState.overriddenProvidedFilterTypes.value,
targetPreferences: getServerInstallTargetPreferences(contentType),
getProjectVersions: getInstallProjectVersions,
queue: serverInstallQueue,
@@ -888,7 +878,7 @@ async function search(requestParams: string) {
const rawResults = await queryClient.fetchQuery({
queryKey: ['search', 'v3', requestParams],
queryFn: () =>
get_search_results_v3(requestParams, 'must_revalidate') as Promise<{
get_search_results_v3(requestParams) as Promise<{
result: Labrinth.Search.v3.SearchResults & {
hits: (Labrinth.Search.v3.ResultSearchProject & { installed?: boolean })[]
}
@@ -964,7 +954,6 @@ const lockedFilterMessages = computed(() => ({
const searchState = useBrowseSearch({
projectType,
tags,
active: browseRouteActive,
providedFilters: combinedProvidedFilters,
search,
persistentQueryParams: ['i', 'ai', 'shi', 'sid', 'wid', 'from'],
@@ -1040,9 +1029,6 @@ onUnmounted(() => {
})
function getProjectBrowseQuery() {
if (!browseRouteActive.value) {
return undefined
}
if (!installContext.value) return undefined
return {
...route.query,
@@ -1050,24 +1036,10 @@ function getProjectBrowseQuery() {
}
}
const advancedFiltersCollapsed = computed({
get: () => themeStore.getFeatureFlag('advanced_filters_collapsed'),
set: (value) => {
themeStore.featureFlags['advanced_filters_collapsed'] = value
getSettings()
.then((settings) => {
settings.feature_flags['advanced_filters_collapsed'] = value
return setSettings(settings)
})
.catch(handleError)
},
})
provideBrowseManager({
tags,
projectType,
...searchState,
advancedFiltersCollapsed,
getProjectLink: (result: Labrinth.Search.v2.ResultSearchProject) => ({
path: `/project/${result.project_id ?? result.slug}`,
query: getProjectBrowseQuery(),
@@ -1147,7 +1119,7 @@ provideBrowseManager({
@browse-modpacks="() => {}"
@create="handleServerModpackFlowCreate"
/>
<Teleport v-if="browseRouteActive" to="#sidebar-teleport-target">
<Teleport to="#sidebar-teleport-target">
<BrowseSidebar />
</Teleport>
</div>
+27 -215
View File
@@ -17,7 +17,6 @@ import {
injectModrinthClient,
injectNotificationManager,
SkinPreviewRenderer,
Toggle,
useVIntl,
} from '@modrinth/ui'
import { arrayBufferToBase64 } from '@modrinth/utils'
@@ -27,18 +26,13 @@ import { computedAsync } from '@vueuse/core'
import type { Ref } from 'vue'
import { computed, inject, onMounted, onUnmounted, ref, useTemplateRef, watch } from 'vue'
import EarsModIcon from '@/assets/skins/ears-mod.png'
import type AccountsCard from '@/components/ui/AccountsCard.vue'
import EditSkinModal from '@/components/ui/skin/EditSkinModal.vue'
import VirtualSkinSectionList from '@/components/ui/skin/VirtualSkinSectionList.vue'
import { trackEvent } from '@/helpers/analytics'
import { check_reachable, get_default_user, login as login_flow, users } from '@/helpers/auth'
import type { RenderResult } from '@/helpers/rendering/batch-skin-renderer.ts'
import {
generateSkinPreviews,
getSkinPreviewKey,
skinBlobUrlMap,
} from '@/helpers/rendering/batch-skin-renderer.ts'
import { generateSkinPreviews, skinBlobUrlMap } from '@/helpers/rendering/batch-skin-renderer.ts'
import type { Cape, Skin, SkinTextureUrl } from '@/helpers/skins.ts'
import {
equip_skin,
@@ -66,7 +60,6 @@ type VirtualSkinSectionListExpose = {
const PENDING_SKIN_REFRESH_DELAY_MS = 11_000
const DEFAULT_SKIN_SECTION_SORT_ORDER = ['Default skins', 'Modrinth Pride']
const EARS_NOTICE_PLACEHOLDER = '__EARS_MOD_NAME__'
const messages = defineMessages({
skinSelectorTitle: {
id: 'app.skins.title',
@@ -121,10 +114,6 @@ const messages = defineMessages({
id: 'app.skins.section.tiny-takeover',
defaultMessage: 'Tiny Takeover',
},
chaosCubedSection: {
id: 'app.skins.section.chaos-cubed',
defaultMessage: 'Chaos Cubed',
},
rateLimitTitle: {
id: 'app.skins.rate-limit.title',
defaultMessage: 'Slow down!',
@@ -170,18 +159,6 @@ const messages = defineMessages({
id: 'app.skins.preview.edit-button',
defaultMessage: 'Edit skin',
},
earsFeatureNotice: {
id: 'app.skins.ears-feature-notice',
defaultMessage: 'This skin uses features from the {ears} mod',
},
toggleEarsFeaturesOff: {
id: 'app.skins.toggle-ears-features-off',
defaultMessage: 'Toggle off',
},
toggleEarsFeaturesOn: {
id: 'app.skins.toggle-ears-features-on',
defaultMessage: 'Toggle on',
},
excitedRinthbotAlt: {
id: 'app.skins.sign-in.rinthbot-alt',
defaultMessage: 'Excited Modrinth Bot',
@@ -223,30 +200,8 @@ const currentUserId = ref<string | undefined>(undefined)
const username = computed(() => currentUser.value?.profile?.name ?? undefined)
const selectedSkin = ref<Skin | null>(null)
const isApplyingSkin = ref(false)
const earsFeaturesEnabled = ref(true)
const selectedSkinHasEarsFeatures = ref(false)
const originalSelectedSkin = ref<Skin | null>(null)
const earsFeatureNoticeParts = computed(() => {
const notice = formatMessage(messages.earsFeatureNotice, {
ears: EARS_NOTICE_PLACEHOLDER,
})
const placeholderIndex = notice.indexOf(EARS_NOTICE_PLACEHOLDER)
if (placeholderIndex === -1) {
return {
before: notice,
after: '',
hasEarsLink: false,
}
}
return {
before: notice.slice(0, placeholderIndex),
after: notice.slice(placeholderIndex + EARS_NOTICE_PLACEHOLDER.length),
hasEarsLink: true,
}
})
const savedSkins = computed(() => {
try {
@@ -492,8 +447,6 @@ function getDefaultSkinSectionTitle(section?: string) {
return formatMessage(messages.mountsOfMayhemSection)
case 'Tiny Takeover':
return formatMessage(messages.tinyTakeoverSection)
case 'Chaos Cubed':
return formatMessage(messages.chaosCubedSection)
case 'Default skins':
return formatMessage(messages.defaultSkinsSection)
default:
@@ -796,7 +749,8 @@ async function loadCurrentUser() {
}
function getBakedSkinTextures(skin: Skin): RenderResult | undefined {
return skinBlobUrlMap.get(getSkinPreviewKey(skin))
const key = `${skin.texture_key}+${skin.variant}+${skin.cape_id ?? 'no-cape'}`
return skinBlobUrlMap.get(key)
}
async function login() {
@@ -980,10 +934,6 @@ watch(
() => {},
)
watch(selectedSkin, () => {
earsFeaturesEnabled.value = true
})
watch(isSkinManagementReadOnly, (readOnly) => {
if (readOnly) {
isDraggingSkinFile.value = false
@@ -1079,12 +1029,9 @@ await loadSkins()
<SkinPreviewRenderer
:cape-src="capeTexture"
:texture-src="skinTexture || ''"
:ears-texture-src="selectedSkin?.texture"
:variant="skinVariant"
:nametag="skinNametag"
:initial-rotation="Math.PI / 8"
:ears-enabled="earsFeaturesEnabled"
@ears-features-detected="selectedSkinHasEarsFeatures = $event"
>
<template v-if="hasPendingSkinChange" #nametag-badge>
<div
@@ -1096,126 +1043,36 @@ await loadSkins()
</template>
<template #subtitle>
<div
class="skin-preview-subtitle flex w-full flex-col items-center gap-6"
:class="{ 'has-ears-features': selectedSkinHasEarsFeatures }"
v-if="hasPendingSkinChange"
class="flex max-w-[calc(100vw-2rem)] flex-wrap items-center justify-center gap-2 px-2"
>
<div
v-if="hasPendingSkinChange"
class="skin-preview-actions flex w-full items-center justify-center gap-1.5"
:class="selectedSkinHasEarsFeatures ? 'flex-nowrap' : 'flex-wrap'"
>
<button
v-tooltip="
selectedSkinHasEarsFeatures
? formatMessage(commonMessages.resetButton)
: undefined
"
class="skin-preview-action-button flex h-10 min-w-0 cursor-pointer items-center justify-center gap-2 rounded-[14px] border-0 bg-surface-4 px-4 py-2.5 text-base font-semibold leading-5 text-contrast shadow-md transition-[filter,transform] duration-200 enabled:hover:brightness-[--hover-brightness] enabled:focus-visible:brightness-[--hover-brightness] enabled:active:scale-95 disabled:cursor-not-allowed disabled:opacity-50 [&>svg]:size-5 [&>svg]:shrink-0"
:disabled="isApplyingSkin || isSkinManagementReadOnly"
:aria-label="formatMessage(commonMessages.resetButton)"
@click="resetSelectedSkin"
>
<RotateCounterClockwiseIcon />
<span class="skin-preview-action-label">
{{ formatMessage(commonMessages.resetButton) }}
</span>
</button>
<button
v-tooltip="
selectedSkinHasEarsFeatures ? formatMessage(messages.applyButton) : undefined
"
class="skin-preview-action-button flex h-10 min-w-0 cursor-pointer items-center justify-center gap-2 rounded-[14px] border-0 bg-brand px-4 py-2.5 text-base font-semibold leading-5 text-[rgba(0,0,0,0.9)] shadow-md transition-[filter,transform] duration-200 enabled:hover:brightness-[--hover-brightness] enabled:focus-visible:brightness-[--hover-brightness] enabled:active:scale-95 disabled:cursor-not-allowed disabled:opacity-50 [&>svg]:size-5 [&>svg]:shrink-0"
:disabled="isApplyingSkin || isSkinManagementReadOnly"
:aria-label="formatMessage(messages.applyButton)"
@click="applySelectedSkin"
>
<SpinnerIcon v-if="isApplyingSkin" class="animate-spin" />
<CheckIcon v-else />
<span class="skin-preview-action-label">
{{ formatMessage(messages.applyButton) }}
</span>
</button>
</div>
<button
v-else
class="flex h-10 min-w-0 cursor-pointer items-center justify-center gap-2 rounded-[14px] border-0 bg-surface-4 px-4 py-2.5 text-base font-semibold leading-5 shadow-md transition-[filter,transform] duration-200 enabled:hover:brightness-[--hover-brightness] enabled:focus-visible:brightness-[--hover-brightness] enabled:active:scale-95 disabled:cursor-not-allowed disabled:opacity-50 [&>svg]:size-5 [&>svg]:shrink-0"
:disabled="!selectedSkin || isSkinManagementReadOnly"
@click="(e: MouseEvent) => selectedSkin && editSkinModal?.show(e, selectedSkin)"
class="flex h-10 min-w-0 cursor-pointer items-center justify-center gap-2 rounded-[14px] border-0 bg-surface-4 px-4 py-2.5 text-base font-semibold leading-5 text-contrast shadow-md transition-[filter,transform] duration-200 enabled:hover:brightness-[--hover-brightness] enabled:focus-visible:brightness-[--hover-brightness] enabled:active:scale-95 disabled:cursor-not-allowed disabled:opacity-50 [&>svg]:size-5 [&>svg]:shrink-0"
:disabled="isApplyingSkin || isSkinManagementReadOnly"
@click="resetSelectedSkin"
>
<EditIcon />
{{ formatMessage(messages.editSkinButton) }}
<RotateCounterClockwiseIcon />
{{ formatMessage(commonMessages.resetButton) }}
</button>
<div
v-if="selectedSkinHasEarsFeatures"
class="ears-feature-notice box-border flex w-full max-w-[340px] items-center justify-center gap-1.5 px-2"
<button
class="flex h-10 min-w-0 cursor-pointer items-center justify-center gap-2 rounded-[14px] border-0 bg-brand px-4 py-2.5 text-base font-semibold leading-5 text-[rgba(0,0,0,0.9)] shadow-md transition-[filter,transform] duration-200 enabled:hover:brightness-[--hover-brightness] enabled:focus-visible:brightness-[--hover-brightness] enabled:active:scale-95 disabled:cursor-not-allowed disabled:opacity-50 [&>svg]:size-5 [&>svg]:shrink-0"
:disabled="isApplyingSkin || isSkinManagementReadOnly"
@click="applySelectedSkin"
>
<div class="ears-feature-copy flex min-w-0 flex-1 items-center gap-1.5">
<img
:src="EarsModIcon"
alt=""
class="size-10 shrink-0 rounded-[7px] border border-solid border-surface-5 object-cover"
/>
<p
class="ears-feature-description m-0 min-w-0 flex-1 text-sm font-medium leading-5 text-primary"
>
{{ earsFeatureNoticeParts.before
}}<router-link
v-if="earsFeatureNoticeParts.hasEarsLink"
to="/project/mfzaZK3Z"
class="text-inherit underline"
>Ears</router-link
>{{ earsFeatureNoticeParts.after }}
</p>
<router-link
to="/project/mfzaZK3Z"
class="ears-feature-compact-label hidden min-w-0 flex-1 text-sm font-medium leading-5 text-primary underline"
>Ears</router-link
>
</div>
<ButtonStyled type="outlined">
<button
class="ears-feature-toggle-button !h-10 !rounded-[14px] !px-4 shadow-md"
:aria-pressed="earsFeaturesEnabled"
:aria-label="
formatMessage(
earsFeaturesEnabled
? messages.toggleEarsFeaturesOff
: messages.toggleEarsFeaturesOn,
)
"
@click="earsFeaturesEnabled = !earsFeaturesEnabled"
>
{{
formatMessage(
earsFeaturesEnabled
? messages.toggleEarsFeaturesOff
: messages.toggleEarsFeaturesOn,
)
}}
</button>
</ButtonStyled>
<Toggle
v-model="earsFeaturesEnabled"
v-tooltip="
formatMessage(
earsFeaturesEnabled
? messages.toggleEarsFeaturesOff
: messages.toggleEarsFeaturesOn,
)
"
small
class="ears-feature-toggle-switch"
:aria-label="
formatMessage(
earsFeaturesEnabled
? messages.toggleEarsFeaturesOff
: messages.toggleEarsFeaturesOn,
)
"
/>
</div>
<SpinnerIcon v-if="isApplyingSkin" class="animate-spin" />
<CheckIcon v-else />
{{ formatMessage(messages.applyButton) }}
</button>
</div>
<button
v-else
class="flex h-10 min-w-0 cursor-pointer items-center justify-center gap-2 rounded-[14px] border-0 bg-surface-4 px-4 py-2.5 text-base font-semibold leading-5 shadow-md transition-[filter,transform] duration-200 enabled:hover:brightness-[--hover-brightness] enabled:focus-visible:brightness-[--hover-brightness] enabled:active:scale-95 disabled:cursor-not-allowed disabled:opacity-50 [&>svg]:size-5 [&>svg]:shrink-0"
:disabled="!selectedSkin || isSkinManagementReadOnly"
@click="(e: MouseEvent) => selectedSkin && editSkinModal?.show(e, selectedSkin)"
>
<EditIcon />
{{ formatMessage(messages.editSkinButton) }}
</button>
</template>
</SkinPreviewRenderer>
</div>
@@ -1284,51 +1141,6 @@ await loadSkins()
</template>
<style lang="scss" scoped>
.skin-preview-subtitle {
container-type: inline-size;
}
.ears-feature-toggle-switch {
display: none;
}
@container (max-width: 300px) {
.skin-preview-subtitle {
gap: 0.75rem;
}
.has-ears-features .skin-preview-action-button {
padding-left: 0 !important;
padding-right: 0 !important;
width: 2.5rem;
}
.has-ears-features .skin-preview-action-label {
display: none;
}
.ears-feature-toggle-button {
display: none !important;
}
.ears-feature-notice {
padding-left: 0;
padding-right: 0;
}
.ears-feature-description {
display: none;
}
.ears-feature-compact-label {
display: block;
}
.ears-feature-toggle-switch {
display: inline-flex;
}
}
.skin-layout {
display: grid;
grid-template-columns: minmax(0, 1fr) minmax(0, 2.5fr);
@@ -1,11 +1,7 @@
<template>
<div
class="h-full w-full pt-6"
:class="isContainedServerRoute ? 'box-border min-h-0 overflow-hidden' : ''"
>
<div class="h-full w-full pt-6">
<ServersManageRootLayout
:server-id="serverId"
:layout-mode="isContainedServerRoute ? 'contained' : 'page'"
:reload-page="() => router.go(0)"
:resolve-viewer="resolveViewer"
:show-copy-id-action="themeStore.devMode"
@@ -67,8 +63,6 @@ const queryClient = useQueryClient()
const themeStore = useTheming()
const breadcrumbs = useBreadcrumbs()
const isContainedServerRoute = computed(() => route.name === 'ServerManageOverview')
const serverId = computed(() => {
const rawId = route.params.id
return Array.isArray(rawId) ? rawId[0] : (rawId ?? '')
@@ -3,5 +3,5 @@ import { ServersManageOverviewPage } from '@modrinth/ui'
</script>
<template>
<ServersManageOverviewPage contained-console />
<ServersManageOverviewPage />
</template>
+250 -71
View File
@@ -13,31 +13,207 @@
@unlinked="fetchInstance"
/>
<UpdateToPlayModal ref="updateToPlayModal" :instance="instance" />
<InstancePageHeader
:instance="instance"
:icon-src="icon"
:is-server-instance="isServerInstance"
:show-instance-play-time="showInstancePlayTime"
:time-played="timePlayed"
:playing="playing"
:loading="loading"
:stopping="stopping"
:loading-server-ping="loadingServerPing"
:players-online="playersOnline"
:status-online="statusOnline"
:recent-plays="recentPlays"
:ping="ping"
:minecraft-server="minecraftServer"
:linked-project-v3="linkedProjectV3"
@repair="() => repairInstance()"
@stop="() => stopInstance('InstancePage')"
@play="() => startInstance('InstancePage')"
@play-server="() => handlePlayServer()"
@settings="() => settingsModal?.show()"
@open-folder="() => instance && showInstanceInFolder(instance.id)"
@export="() => exportModal?.show()"
@create-shortcut="() => createShortcut()"
/>
<ContentPageHeader>
<template #icon>
<Avatar
:src="icon ? icon : undefined"
:alt="instance.name"
size="64px"
:tint-by="instance.id"
/>
</template>
<template #title>
{{ instance.name }}
</template>
<template #stats>
<div class="flex items-center flex-wrap gap-2">
<template v-if="!isServerInstance">
<div class="flex items-center gap-2 capitalize font-medium">
{{ instance.loader }} {{ instance.game_version }}
</div>
<template v-if="showInstancePlayTime">
<div class="w-1.5 h-1.5 rounded-full bg-surface-5"></div>
<div class="flex items-center gap-2 font-medium">
<template v-if="timePlayed > 0">
{{ timePlayedHumanized }}
</template>
<template v-else> Never played </template>
</div>
</template>
</template>
<template v-else>
<template v-if="loadingServerPing">
<ServerOnlinePlayers
v-if="playersOnline !== undefined"
:online="playersOnline"
:status-online="statusOnline"
hide-label
/>
<ServerRecentPlays :recent-plays="recentPlays ?? 0" hide-label />
<div
v-if="
(playersOnline !== undefined || recentPlays !== undefined) &&
(minecraftServer?.region || ping)
"
class="w-1.5 h-1.5 rounded-full bg-surface-5"
></div>
<ServerPing v-if="ping" :ping="ping" />
</template>
<ServerRegion v-if="minecraftServer?.region" :region="minecraftServer?.region" />
<div
v-if="minecraftServer?.region || ping"
class="w-1.5 h-1.5 rounded-full bg-surface-5"
></div>
<div
v-if="linkedProjectV3"
class="flex gap-1.5 items-center font-medium text-primary"
>
Linked to
<Avatar
:src="linkedProjectV3.icon_url"
:alt="linkedProjectV3.name"
:tint-by="instance.id"
size="24px"
/>
<router-link
:to="`/project/${linkedProjectV3.slug ?? linkedProjectV3.id}`"
class="hover:underline text-primary truncate"
>
{{ linkedProjectV3.name }}
</router-link>
</div>
</template>
</div>
</template>
<template #actions>
<div class="flex gap-2">
<ButtonStyled
v-if="
[
'installing',
'pack_installing',
'pack_installed',
'not_installed',
'minecraft_installing',
].includes(instance.install_stage)
"
color="brand"
size="large"
>
<button disabled>Installing...</button>
</ButtonStyled>
<ButtonStyled
v-else-if="instance.install_stage !== 'installed'"
color="brand"
size="large"
>
<button @click="repairInstance()">
<DownloadIcon />
Repair
</button>
</ButtonStyled>
<ButtonStyled v-else-if="playing === true" color="red" size="large">
<button :disabled="stopping" @click="stopInstance('InstancePage')">
<StopCircleIcon />
{{ stopping ? 'Stopping...' : 'Stop' }}
</button>
</ButtonStyled>
<ButtonStyled
v-else-if="playing === false && loading === false && !isServerInstance"
color="brand"
size="large"
>
<button @click="startInstance('InstancePage')">
<PlayIcon />
Play
</button>
</ButtonStyled>
<div
v-else-if="playing === false && loading === false && isServerInstance"
class="joined-buttons"
>
<ButtonStyled color="brand" size="large">
<button @click="handlePlayServer()">
<PlayIcon />
Play
</button>
</ButtonStyled>
<ButtonStyled color="brand" size="large">
<OverflowMenu
:options="[
{
id: 'join_server',
action: () => handlePlayServer(),
},
{
id: 'launch_instance',
action: () => startInstance('InstancePage'),
},
]"
>
<div class="w-0 text-xl relative top-0.5 right-2.5">
<DropdownIcon />
</div>
<template #join_server>
<PlayIcon />
Join server
</template>
<template #launch_instance>
<PlayIcon />
Launch instance
</template>
</OverflowMenu>
</ButtonStyled>
</div>
<ButtonStyled
v-else-if="loading === true && playing === false"
color="brand"
size="large"
>
<button disabled>Starting...</button>
</ButtonStyled>
<ButtonStyled circular size="large">
<button v-tooltip="'Instance settings'" @click="settingsModal?.show()">
<SettingsIcon />
</button>
</ButtonStyled>
<ButtonStyled type="transparent" circular size="large">
<OverflowMenu
:options="[
{
id: 'open-folder',
action: () => {
if (instance) showInstanceInFolder(instance.id)
},
},
{
id: 'export-mrpack',
action: () => exportModal?.show(),
},
{
id: 'create-shortcut',
action: () => createShortcut(),
},
]"
>
<MoreVerticalIcon />
<template #share-instance> <UserPlusIcon /> Share instance </template>
<template #host-a-server> <ServerIcon /> Create a server </template>
<template #open-folder> <FolderOpenIcon /> Open folder </template>
<template #export-mrpack> <PackageIcon /> Export modpack </template>
<template #create-shortcut> <ExternalIcon /> Create shortcut </template>
</OverflowMenu>
</ButtonStyled>
</div>
</template>
</ContentPageHeader>
</div>
<div :class="['px-6', { 'shrink-0': isFixedRender }]">
<NavTabs :links="tabs" />
@@ -96,36 +272,51 @@ import {
BoxesIcon,
CheckCircleIcon,
ClipboardCopyIcon,
DownloadIcon,
DropdownIcon,
EditIcon,
ExternalIcon,
EyeIcon,
FolderOpenIcon,
GlobeIcon,
HashIcon,
MoreVerticalIcon,
PackageIcon,
PlayIcon,
PlusIcon,
ServerIcon,
SettingsIcon,
StopCircleIcon,
TerminalSquareIcon,
UpdatedIcon,
UserPlusIcon,
XIcon,
} from '@modrinth/assets'
import { injectNotificationManager, NavTabs, useLoadingBarToken } from '@modrinth/ui'
import {
Avatar,
ButtonStyled,
ContentPageHeader,
injectNotificationManager,
NavTabs,
OverflowMenu,
ServerOnlinePlayers,
ServerPing,
ServerRecentPlays,
ServerRegion,
useLoadingBarToken,
} from '@modrinth/ui'
import { useQueryClient } from '@tanstack/vue-query'
import { convertFileSrc } from '@tauri-apps/api/core'
import dayjs from 'dayjs'
import duration from 'dayjs/plugin/duration'
import relativeTime from 'dayjs/plugin/relativeTime'
import { computed, onUnmounted, ref, shallowRef, watch } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import ContextMenu from '@/components/ui/ContextMenu.vue'
import ExportModal from '@/components/ui/ExportModal.vue'
import InstancePageHeader from '@/components/ui/instance-page-header/index.vue'
import InstanceSettingsModal from '@/components/ui/modal/InstanceSettingsModal.vue'
import UpdateToPlayModal from '@/components/ui/modal/UpdateToPlayModal.vue'
import {
fetchCachedServerStatus,
getFreshCachedServerStatus,
} from '@/composables/instances/use-server-status-query'
import { useInstanceConsole } from '@/composables/useInstanceConsole'
import { trackEvent } from '@/helpers/analytics'
import { get_project_v3 } from '@/helpers/cache.js'
@@ -136,11 +327,12 @@ import { type InstanceContentData, loadInstanceContentData } from '@/helpers/ins
import { get_by_instance_id } from '@/helpers/process'
import type { GameInstance } from '@/helpers/types'
import { createInstanceShortcut, showInstanceInFolder } from '@/helpers/utils.js'
import { refreshWorlds, type ServerStatus } from '@/helpers/worlds'
import { get_server_status, refreshWorlds } from '@/helpers/worlds'
import { injectServerInstall } from '@/providers/server-install'
import { handleSevereError } from '@/store/error.js'
import { useBreadcrumbs, useTheming } from '@/store/state'
dayjs.extend(duration)
dayjs.extend(relativeTime)
const { addNotification, handleError } = injectNotificationManager()
@@ -180,15 +372,13 @@ const selected = ref<unknown[]>([])
const minecraftServer = computed(() => linkedProjectV3.value?.minecraft_server)
const javaServerPingData = computed(() => linkedProjectV3.value?.minecraft_java_server?.ping?.data)
const liveServerStatusOnline = ref(false)
const statusOnline = computed(() => liveServerStatusOnline.value || !!javaServerPingData.value)
const statusOnline = computed(() => !!javaServerPingData.value)
const recentPlays = computed(
() => linkedProjectV3.value?.minecraft_java_server?.verified_plays_2w ?? undefined,
)
const playersOnline = ref<number | undefined>(undefined)
const ping = ref<number | undefined>(undefined)
const loadingServerPing = ref(false)
const activeInstanceId = ref<string>()
watch(
() => router.currentRoute.value,
@@ -200,20 +390,6 @@ watch(
{ immediate: true },
)
function applyServerStatus(status: ServerStatus) {
playersOnline.value = status.players?.online
ping.value = status.ping
liveServerStatusOnline.value = true
loadingServerPing.value = true
}
function resetServerStatus() {
ping.value = undefined
playersOnline.value = undefined
liveServerStatusOnline.value = false
loadingServerPing.value = false
}
function isContentSubpageRoute(routeName = displayedInstanceRoute.value.name) {
return typeof routeName === 'string' && contentSubpageRouteNames.has(routeName)
}
@@ -222,7 +398,9 @@ async function fetchInstance() {
isServerInstance.value = false
linkedProjectV3.value = undefined
preloadedContent.value = null
resetServerStatus()
ping.value = undefined
playersOnline.value = undefined
loadingServerPing.value = false
const nextInstance = await get(route.params.id as string).catch(handleError)
let nextLinkedProjectV3: Labrinth.Projects.v3.Project | undefined
@@ -251,9 +429,8 @@ async function fetchInstance() {
linkedProjectV3.value = nextLinkedProjectV3
isServerInstance.value = nextIsServerInstance
preloadedContent.value = nextPreloadedContent
activeInstanceId.value = nextInstance?.id
fetchDeferredData(nextInstance?.id)
fetchDeferredData()
if (nextInstance) {
queryClient.prefetchQuery({
@@ -264,32 +441,18 @@ async function fetchInstance() {
}
}
function fetchDeferredData(instanceId?: string) {
function fetchDeferredData() {
const serverAddress = linkedProjectV3.value?.minecraft_java_server?.address
if (isServerInstance.value && serverAddress) {
const cachedStatus = getFreshCachedServerStatus(queryClient, serverAddress)
if (cachedStatus) {
applyServerStatus(cachedStatus)
} else {
playersOnline.value = undefined
ping.value = undefined
loadingServerPing.value = false
}
fetchCachedServerStatus(queryClient, serverAddress)
get_server_status(serverAddress)
.then((status) => {
if (
activeInstanceId.value !== instanceId ||
linkedProjectV3.value?.minecraft_java_server?.address !== serverAddress
)
return
applyServerStatus(status)
playersOnline.value = status.players?.online
ping.value = status.ping
})
.catch((error) => {
console.error(`Failed to fetch server status for ${serverAddress}:`, error)
})
.finally(() => {
if (activeInstanceId.value !== instanceId) return
loadingServerPing.value = true
})
} else {
@@ -513,7 +676,7 @@ const handleOptionsClick = async (args: { option: string; item: unknown }) => {
break
case 'copy_path': {
if (instance.value) {
const fullPath = await get_full_path(instance.value.id)
const fullPath = await get_full_path(instance.value?.path)
await navigator.clipboard.writeText(fullPath)
}
break
@@ -562,6 +725,22 @@ const timePlayed = computed(() => {
: 0
})
const timePlayedHumanized = computed(() => {
const duration = dayjs.duration(timePlayed.value, 'seconds')
const hours = Math.floor(duration.asHours())
if (hours >= 1) {
return hours + ' hour' + (hours > 1 ? 's' : '')
}
const minutes = Math.floor(duration.asMinutes())
if (minutes >= 1) {
return minutes + ' minute' + (minutes > 1 ? 's' : '')
}
const seconds = Math.floor(duration.asSeconds())
return seconds + ' second' + (seconds > 1 ? 's' : '')
})
onUnmounted(() => {
unlistenProcesses()
unlistenInstances()
+6 -77
View File
@@ -2,13 +2,6 @@
<ReadyTransition :pending="loading">
<ContentPageLayout>
<template #modals>
<UnknownFileWarningModal
ref="unknownFileWarningModal"
mode="mod"
:file-name="unknownFileName"
@cancel="resolveUnknownFileWarning(false)"
@continue="handleUnknownFileContinue"
/>
<ShareModalWrapper
ref="shareModal"
:share-title="formatMessage(messages.shareTitle)"
@@ -94,7 +87,6 @@ import {
provideAppBackup,
provideContentManager,
ReadyTransition,
UnknownFileWarningModal,
useDebugLogger,
useVIntl,
versionChangesGameVersion,
@@ -121,7 +113,6 @@ import {
add_project_from_path,
edit,
get_linked_modpack_content,
is_file_on_modrinth,
list,
remove_project,
switch_project_version_with_dependencies,
@@ -130,12 +121,10 @@ import {
update_managed_modrinth_version,
} from '@/helpers/instance'
import { type InstanceContentData, loadInstanceContentData } from '@/helpers/instance-content'
import { get as getSettings, set as setSettings } from '@/helpers/settings'
import type { CacheBehaviour, GameInstance } from '@/helpers/types'
import { highlightModInInstance } from '@/helpers/utils.js'
import { injectContentInstall } from '@/providers/content-install'
import { useTheming } from '@/store/state'
import type { FeatureFlag } from '@/store/theme'
const messages = defineMessages({
shareTitle: {
@@ -186,7 +175,6 @@ const router = useRouter()
const queryClient = useQueryClient()
const debug = useDebugLogger('Mods:ContentUpdate')
const themeStore = useTheming()
const skipUnknownFileWarningFeatureFlag = 'skip_unknown_pack_warning' as FeatureFlag
const skipNonEssentialWarnings = computed(() =>
themeStore.getFeatureFlag('skip_non_essential_warnings'),
)
@@ -296,9 +284,6 @@ const exportModal = ref(null)
const contentUpdaterModal = ref<InstanceType<typeof ContentUpdaterModal> | null>()
const modpackContentModal = ref<InstanceType<typeof ModpackContentModal> | null>()
const modpackUpdateConfirmModal = ref<InstanceType<typeof ConfirmModpackUpdateModal> | null>()
const unknownFileWarningModal = ref<InstanceType<typeof UnknownFileWarningModal> | null>()
const unknownFileName = ref('')
let resolveUnknownFileConfirmation: ((confirmed: boolean) => void) | null = null
const modpackContentQueryKey = computed(() => ['linkedModpackContent', props.instance.id])
const modpackContentQuery = useQuery({
@@ -497,34 +482,14 @@ async function handleUploadFiles() {
if (!props.instance) return
const files = await open({ multiple: true })
if (!files) return
const selectedFiles: Array<{ path: string; filename: string }> = []
for (const file of files) {
const path = (file as { path?: string }).path ?? file
if (typeof path !== 'string') continue
selectedFiles.push({
path,
filename: path.split(/[\\/]/).pop() ?? path,
})
}
const fileRecognition = await Promise.all(
selectedFiles.map(async ({ path }) => {
try {
return await is_file_on_modrinth(path)
} catch {
return true
}
}),
)
const addedFiles: string[] = []
for (const [index, { path, filename }] of selectedFiles.entries()) {
if (!fileRecognition[index] && !(await confirmUnknownFileInstallation(filename))) {
continue
}
for (const file of files) {
const path = (file as { path?: string }).path ?? file
const fileName = typeof path === 'string' ? (path.split('/').pop() ?? path) : String(path)
try {
await add_project_from_path(props.instance.id, path)
addedFiles.push(filename)
addedFiles.push(fileName)
} catch (e) {
handleError(e as Error)
}
@@ -549,39 +514,6 @@ async function handleUploadFiles() {
}
}
function confirmUnknownFileInstallation(fileName: string) {
if (themeStore.getFeatureFlag(skipUnknownFileWarningFeatureFlag)) {
return Promise.resolve(true)
}
unknownFileName.value = fileName
return new Promise<boolean>((resolve) => {
resolveUnknownFileConfirmation = resolve
void nextTick(() => unknownFileWarningModal.value?.show())
})
}
function resolveUnknownFileWarning(confirmed: boolean) {
const resolve = resolveUnknownFileConfirmation
resolveUnknownFileConfirmation = null
unknownFileName.value = ''
resolve?.(confirmed)
}
async function handleUnknownFileContinue(dontShowAgain: boolean) {
if (dontShowAgain) {
themeStore.featureFlags[skipUnknownFileWarningFeatureFlag] = true
try {
const settings = await getSettings()
settings.feature_flags[skipUnknownFileWarningFeatureFlag] = true
await setSettings(settings)
} catch (error) {
handleError(error as Error)
}
}
resolveUnknownFileWarning(true)
}
async function toggleDisableMod(mod: ContentItem, desiredEnabled?: boolean) {
if (!mod.file_path) return
const operation = beginContentOperation(mod)
@@ -1419,9 +1351,7 @@ provideContentManager({
title: item.file_name.replace('.disabled', ''),
icon_url: null,
},
projectLink: item.project?.id
? { path: `/project/${item.project.id}`, query: { i: props.instance.id } }
: undefined,
projectLink: item.project?.id ? { path: `/project/${item.project.id}` } : undefined,
version: item.version ?? {
id: item.file_name,
version_number: formatMessage(commonMessages.unknownLabel),
@@ -1431,7 +1361,6 @@ provideContentManager({
item.project?.id && item.version?.id
? {
path: `/project/${item.project.id}/version/${item.version.id}`,
query: { i: props.instance.id },
}
: undefined,
owner: item.owner
@@ -1514,7 +1443,7 @@ onMounted(() => {
props.instance &&
event.instance_id === props.instance.id &&
event.event === 'synced' &&
props.instance.install_stage === 'installed' &&
props.instance.install_stage !== 'pack_installing' &&
!isBulkOperating.value
) {
await initProjects()
@@ -618,7 +618,10 @@ const filterOptions = computed(() => {
const hasSingleplayer = dedupedWorlds.value.some((x) => x.type === 'singleplayer')
const hasServer = dedupedWorlds.value.some((x) => x.type === 'server')
if (hasSingleplayer && hasServer) {
const hasStatusFilter =
selectedFilters.value.includes('online') || selectedFilters.value.includes('offline')
if (hasSingleplayer && hasServer && !hasStatusFilter) {
options.push({ id: 'singleplayer', label: formatMessage(commonMessages.singleplayerLabel) })
}
@@ -630,11 +633,13 @@ const filterOptions = computed(() => {
options.push({ id: 'vanilla', label: formatMessage(messages.vanillaFilter) })
options.push({ id: 'modded', label: formatMessage(messages.moddedFilter) })
}
const hasOnline = servers.some((x) => !!serverData.value[x.address]?.status)
const hasOffline = servers.some((x) => !serverData.value[x.address]?.status)
if (hasOnline && hasOffline) {
options.push({ id: 'online', label: formatMessage(messages.onlineFilter) })
options.push({ id: 'offline', label: formatMessage(messages.offlineFilter) })
if (!selectedFilters.value.includes('singleplayer')) {
const hasOnline = servers.some((x) => !!serverData.value[x.address]?.status)
const hasOffline = servers.some((x) => !serverData.value[x.address]?.status)
if (hasOnline && hasOffline) {
options.push({ id: 'online', label: formatMessage(messages.onlineFilter) })
options.push({ id: 'offline', label: formatMessage(messages.offlineFilter) })
}
}
}
+136 -199
View File
@@ -59,100 +59,126 @@
>
<ProjectBackgroundGradient :project="data" />
</Teleport>
<ProjectPageHeader
<ProjectHeader
v-else
:project="data"
:project-v3="projectV3"
:show-status-badge="data.status !== 'approved'"
:ping="serverPing"
@contextmenu.prevent.stop="handleRightClick"
@category="(category) => router.push(`${projectSearchUrl}?f=categories:${category}`)"
>
<template #actions>
<template v-if="isServerProject">
<ButtonStyled v-if="serverPlaying" color="red" size="large">
<button type="button" @click="handleStopServer">
<StopCircleIcon />
{{ formatMessage(commonMessages.stopButton) }}
</button>
</ButtonStyled>
<ButtonStyled v-else color="brand" size="large">
<button type="button" :disabled="serverInstallLoading" @click="handleClickPlay">
<PlayIcon />
{{
serverInstallLoading
? formatMessage(commonMessages.installingLabel)
: formatMessage(commonMessages.playButton)
}}
</button>
</ButtonStyled>
<ButtonStyled circular size="large">
<button
v-tooltip="formatMessage(commonMessages.addServerToInstanceButton)"
type="button"
:aria-label="formatMessage(commonMessages.addServerToInstanceButton)"
@click="handleAddServerToInstance"
>
<PlusIcon />
</button>
</ButtonStyled>
<ButtonStyled circular size="large" type="transparent">
<TeleportOverflowMenu
:options="serverProjectHeaderMoreActions"
tooltip="More options"
aria-label="More options"
>
<MoreVerticalIcon />
</TeleportOverflowMenu>
</ButtonStyled>
</template>
<template v-else>
<ButtonStyled v-if="showSwitchVersion && onVersionsPage" size="large">
<button v-tooltip="formatMessage(messages.alreadyInstalled)" type="button" disabled>
<CheckIcon />
{{ formatMessage(commonMessages.installedLabel) }}
</button>
</ButtonStyled>
<ButtonStyled v-else-if="showSwitchVersion" size="large">
<button type="button" @click="goToVersions">
<SwapIcon />
{{ formatMessage(messages.switchVersion) }}
</button>
</ButtonStyled>
<ButtonStyled v-else color="brand" size="large">
<button
v-tooltip="
installButtonInstalled ? formatMessage(messages.alreadyInstalled) : undefined
"
type="button"
:disabled="installButtonDisabled"
@click="install(null)"
>
<component :is="installButtonIcon" :class="installButtonIconClass" />
{{
installButtonInstalled
? formatMessage(commonMessages.installedLabel)
: installButtonValidating
? formatMessage(commonMessages.validatingLabel)
: installButtonLoading
? formatMessage(commonMessages.installingLabel)
: serverProjectSelected
? formatMessage(commonMessages.selectedLabel)
: formatMessage(commonMessages.installButton)
}}
</button>
</ButtonStyled>
<ButtonStyled circular size="large" type="transparent">
<TeleportOverflowMenu
:options="projectHeaderMoreActions"
tooltip="More options"
aria-label="More options"
>
<MoreVerticalIcon />
</TeleportOverflowMenu>
</ButtonStyled>
</template>
<template v-if="isServerProject" #actions>
<ButtonStyled v-if="serverPlaying" size="large" color="red">
<button @click="handleStopServer">
<StopCircleIcon />
{{ formatMessage(commonMessages.stopButton) }}
</button>
</ButtonStyled>
<ButtonStyled v-else size="large" color="brand">
<button
:disabled="data && installingServerProjects.includes(data.id)"
@click="handleClickPlay"
>
<PlayIcon />
{{
data && installingServerProjects.includes(data.id)
? formatMessage(commonMessages.installingLabel)
: formatMessage(commonMessages.playButton)
}}
</button>
</ButtonStyled>
<ButtonStyled size="large" circular>
<button
v-tooltip="formatMessage(commonMessages.addServerToInstanceButton)"
@click="handleAddServerToInstance"
>
<PlusIcon />
</button>
</ButtonStyled>
<ButtonStyled size="large" circular type="transparent">
<OverflowMenu
:tooltip="`More options`"
:options="[
{
id: 'open-in-browser',
link: `https://modrinth.com/project/${data.slug}`,
external: true,
},
{
divider: true,
},
{
id: 'report',
color: 'red',
hoverFilled: true,
link: `https://modrinth.com/report?item=project&itemID=${data.id}`,
},
]"
aria-label="More options"
>
<MoreVerticalIcon aria-hidden="true" />
<template #open-in-browser> <ExternalIcon /> Open in browser </template>
<template #report> <ReportIcon /> Report </template>
</OverflowMenu>
</ButtonStyled>
</template>
</ProjectPageHeader>
<template v-else #actions>
<ButtonStyled size="large" color="brand">
<button
v-tooltip="installButtonTooltip"
:disabled="installButtonDisabled"
@click="install(null)"
>
<SpinnerIcon
v-if="installButtonLoading && !installButtonInstalled"
class="animate-spin"
/>
<DownloadIcon v-else-if="!installButtonInstalled && !serverProjectSelected" />
<CheckIcon v-else />
{{ installButtonLabel }}
</button>
</ButtonStyled>
<ButtonStyled size="large" circular type="transparent">
<OverflowMenu
:tooltip="`More options`"
:options="[
{
id: 'follow',
disabled: true,
tooltip: 'Coming soon',
action: () => {},
},
{
id: 'save',
disabled: true,
tooltip: 'Coming soon',
action: () => {},
},
{
id: 'open-in-browser',
link: `https://modrinth.com/${data.project_type}/${data.slug}`,
external: true,
},
{
divider: true,
},
{
id: 'report',
color: 'red',
hoverFilled: true,
link: `https://modrinth.com/report?item=project&itemID=${data.id}`,
},
]"
aria-label="More options"
>
<MoreVerticalIcon aria-hidden="true" />
<template #open-in-browser> <ExternalIcon /> Open in browser </template>
<template #follow> <HeartIcon /> Follow </template>
<template #save> <BookmarkIcon /> Save </template>
<template #report> <ReportIcon /> Report </template>
</OverflowMenu>
</ButtonStyled>
</template>
</ProjectHeader>
<NavTabs
:links="[
{
@@ -247,8 +273,9 @@ import {
getTargetInstallPreferences,
injectNotificationManager,
NavTabs,
OverflowMenu,
ProjectBackgroundGradient,
ProjectPageHeader,
ProjectHeader,
ProjectSidebarCompatibility,
ProjectSidebarCreators,
ProjectSidebarDetails,
@@ -257,10 +284,8 @@ import {
ProjectSidebarTags,
requestInstall,
SelectedProjectsFloatingBar,
TeleportOverflowMenu,
useVIntl,
} from '@modrinth/ui'
import { useQueryClient } from '@tanstack/vue-query'
import { convertFileSrc } from '@tauri-apps/api/core'
import { openUrl } from '@tauri-apps/plugin-opener'
import dayjs from 'dayjs'
@@ -268,13 +293,8 @@ import relativeTime from 'dayjs/plugin/relativeTime'
import { computed, onUnmounted, ref, shallowRef, watch } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { SwapIcon } from '@/assets/icons/index.js'
import ContextMenu from '@/components/ui/ContextMenu.vue'
import InstanceIndicator from '@/components/ui/InstanceIndicator.vue'
import {
fetchCachedServerStatus,
getFreshCachedServerStatus,
} from '@/composables/instances/use-server-status-query'
import {
get_organization,
get_project,
@@ -293,7 +313,7 @@ import {
import { get_loader_versions as getLoaderManifest } from '@/helpers/metadata'
import { get_by_instance_id } from '@/helpers/process'
import { get_categories, get_game_versions, get_loaders } from '@/helpers/tags'
import { getServerAddress } from '@/helpers/worlds'
import { getServerAddress, getServerLatency } from '@/helpers/worlds'
import { injectContentInstall } from '@/providers/content-install'
import { injectServerInstall } from '@/providers/server-install'
import { createServerInstallContent } from '@/providers/setup/server-install-content'
@@ -306,7 +326,6 @@ const { handleError } = injectNotificationManager()
const { install: installVersion } = injectContentInstall()
const route = useRoute()
const router = useRouter()
const queryClient = useQueryClient()
const breadcrumbs = useBreadcrumbs()
const themeStore = useTheming()
const { formatMessage } = useVIntl()
@@ -316,14 +335,14 @@ const messages = defineMessages({
id: 'app.project.install-context.back-to-browse',
defaultMessage: 'Back to discover',
},
installContentToInstance: {
id: 'app.project.install-context.install-content-to-instance',
defaultMessage: 'Install content to instance',
},
alreadyInstalled: {
id: 'app.project.install-button.already-installed',
defaultMessage: 'This project is already installed',
},
switchVersion: {
id: 'app.project.install-button.switch-version',
defaultMessage: 'Switch version',
},
})
const { installingServerProjects, playServerProject, showAddServerToInstanceModal } =
@@ -446,7 +465,7 @@ const projectInstallContext = computed(() => {
iconSrc: instance.value.icon_path ? convertFileSrc(instance.value.icon_path) : null,
backUrl: projectBrowseBackUrl.value,
backLabel: formatMessage(messages.backToBrowse),
heading: formatMessage(commonMessages.installingContentLabel),
heading: formatMessage(messages.installContentToInstance),
}
}
@@ -483,79 +502,17 @@ const installButtonInstalled = computed(() =>
const installButtonDisabled = computed(
() => installButtonInstalled.value || installButtonLoading.value,
)
const serverInstallLoading = computed(
() => !!data.value && installingServerProjects.value.includes(data.value.id),
)
const installButtonIcon = computed(() => {
if (installButtonLoading.value && !installButtonInstalled.value) return SpinnerIcon
if (!installButtonInstalled.value && !serverProjectSelected.value) return DownloadIcon
return CheckIcon
const installButtonLabel = computed(() => {
if (installButtonInstalled.value) return formatMessage(commonMessages.installedLabel)
if (installButtonValidating.value) return formatMessage(commonMessages.validatingLabel)
if (installButtonLoading.value) return formatMessage(commonMessages.installingLabel)
if (serverProjectSelected.value) return formatMessage(commonMessages.selectedLabel)
return formatMessage(commonMessages.installButton)
})
const installButtonTooltip = computed(() => {
if (installButtonInstalled.value) return formatMessage(messages.alreadyInstalled)
return null
})
const installButtonIconClass = computed(() =>
installButtonLoading.value && !installButtonInstalled.value ? 'animate-spin' : undefined,
)
const serverProjectHeaderMoreActions = computed(() => [
{
id: 'open-in-browser',
label: formatMessage(commonMessages.openInModrinthButton),
icon: ExternalIcon,
action: openProjectInBrowser,
},
{
divider: true,
},
{
id: 'report',
label: formatMessage(commonMessages.reportButton),
icon: ReportIcon,
color: 'red',
action: reportProject,
},
])
const projectHeaderMoreActions = computed(() => [
{
id: 'follow',
label: formatMessage(commonMessages.followButton),
icon: HeartIcon,
disabled: true,
tooltip: 'Coming soon',
action: () => {},
},
{
id: 'save',
label: formatMessage(commonMessages.saveButton),
icon: BookmarkIcon,
disabled: true,
tooltip: 'Coming soon',
action: () => {},
},
{
id: 'open-in-browser',
label: formatMessage(commonMessages.openInModrinthButton),
icon: ExternalIcon,
action: openProjectInBrowser,
},
{
divider: true,
},
{
id: 'report',
label: formatMessage(commonMessages.reportButton),
icon: ReportIcon,
color: 'red',
action: reportProject,
},
])
const projectSearchUrl = computed(
() => `/browse/${isServerProject.value ? 'server' : data.value?.project_type}`,
)
const showSwitchVersion = computed(() => !!instance.value && installed.value)
const onVersionsPage = computed(() => route.name === 'Versions')
function goToVersions() {
router.push(versionsHref.value)
}
const [allLoaders, allGameVersions] = await Promise.all([
get_loaders().catch(handleError).then(ref),
@@ -594,17 +551,6 @@ function handleAddServerToInstance() {
showAddServerToInstanceModal(data.value.title, address)
}
function openProjectInBrowser() {
if (!data.value) return
const type = isServerProject.value ? 'project' : data.value.project_type
void openUrl(`https://modrinth.com/${type}/${data.value.slug}`)
}
function reportProject() {
if (!data.value) return
void openUrl(`https://modrinth.com/report?item=project&itemID=${data.value.id}`)
}
async function fetchProjectData() {
const [project, projectV3Result] = await Promise.all([
get_project(route.params.id, 'must_revalidate').catch(handleError),
@@ -654,19 +600,10 @@ async function fetchProjectData() {
function fetchDeferredServerData(project) {
const serverAddress = projectV3.value?.minecraft_java_server?.address
if (serverAddress) {
const cachedStatus = getFreshCachedServerStatus(queryClient, serverAddress)
if (cachedStatus) {
serverPing.value = cachedStatus.ping
serverStatusOnline.value = true
} else {
serverPing.value = undefined
}
fetchCachedServerStatus(queryClient, serverAddress)
.then((status) => {
if (projectV3.value?.minecraft_java_server?.address !== serverAddress) return
serverPing.value = status.ping
serverStatusOnline.value = true
serverPing.value = undefined
getServerLatency(serverAddress)
.then((latency) => {
serverPing.value = latency
})
.catch((error) => {
console.error(`Failed to ping server ${serverAddress}:`, error)
+426 -162
View File
@@ -1,152 +1,259 @@
<template>
<div class="flex flex-col">
<router-link
class="mb-4 flex w-fit items-center gap-2 rounded-lg px-2 py-0.5 pl-0 text-link"
:to="buildProjectHref(`/project/${route.params.id}/versions`)"
>
<ChevronLeftIcon class="shrink-0" /> {{ formatMessage(messages.allVersions) }}
</router-link>
<VersionPage
v-if="version"
:version="version"
:enrichment="enrichment"
:enrichment-loading="enrichmentLoading"
:members="members"
:dependency-link-creator="createDependencyLink"
>
<template #headerActions>
<div>
<Card>
<Breadcrumbs
:current-title="version.name"
:link-stack="[
{
href: buildProjectHref(`/project/${route.params.id}/versions`),
label: 'Versions',
},
]"
/>
<div class="version-title">
<h2>{{ version.name }}</h2>
</div>
<div class="button-group">
<ButtonStyled color="brand">
<button
:disabled="installing || (installed && installedVersion === version.id)"
@click="() => version && install(version.id)"
@click="() => install(version.id)"
>
<DownloadIcon v-if="!installed" />
<SwapIcon v-else-if="installedVersion !== version.id" />
<CheckIcon v-else />
{{
installing
? formatMessage(messages.installing)
? 'Installing...'
: installed && installedVersion === version.id
? formatMessage(commonMessages.installedLabel)
: installed
? formatMessage(commonMessages.switchToVersionButton)
: formatMessage(commonMessages.installButton)
? 'Installed'
: 'Install'
}}
</button>
</ButtonStyled>
<ButtonStyled type="outlined" circular>
<OverflowMenu
v-tooltip="formatMessage(commonMessages.moreOptionsButton)"
:options="[
{
id: 'open-in-browser',
link: `https://modrinth.com/${project.project_type}/${project.slug}/version/${version.id}`,
external: true,
},
{
id: 'report',
color: 'red',
hoverFilled: true,
link: `https://modrinth.com/report?item=version&itemID=${version.id}`,
external: true,
},
]"
aria-label="More options"
>
<MoreVerticalIcon aria-hidden="true" />
<template #open-in-browser>
<ExternalIcon aria-hidden="true" />
{{ formatMessage(commonMessages.openInBrowserButton) }}
</template>
<template #report>
<ReportIcon aria-hidden="true" /> {{ formatMessage(commonMessages.reportButton) }}
</template>
</OverflowMenu>
</ButtonStyled>
</template>
<template #supplementaryResourceActions="{ file }">
<ButtonStyled>
<a :href="file.url" :download="file.filename" target="_blank">
<DownloadIcon aria-hidden="true" />
{{ formatMessage(messages.downloadInBrowser) }}
<button>
<ReportIcon />
Report
</button>
</ButtonStyled>
<ButtonStyled>
<a
:href="`https://modrinth.com/mod/${route.params.id}/version/${route.params.version}`"
rel="external"
>
Modrinth website
<ExternalIcon />
</a>
</ButtonStyled>
</template>
</VersionPage>
</div>
</Card>
<div class="version-container">
<div class="description-cards">
<Card>
<h3 class="card-title">Changelog</h3>
<div class="markdown-body" v-html="renderString(version.changelog ?? '')" />
</Card>
<Card>
<h3 class="card-title">Files</h3>
<Card
v-for="file in version.files"
:key="file.id"
:class="{ primary: file.primary }"
class="file"
>
<span class="label">
<FileIcon />
<span>
<span class="title">
{{ file.filename }}
</span>
({{ formatBytes(file.size) }})
<span v-if="file.primary" class="primary-label"> Primary </span>
</span>
</span>
<ButtonStyled v-if="project.project_type !== 'modpack' || file.primary" color="brand">
<button class="download" :disabled="installed" @click="() => install(version.id)">
<DownloadIcon v-if="!installed" />
<CheckIcon v-else />
{{ installed ? 'Installed' : 'Install' }}
</button>
</ButtonStyled>
</Card>
</Card>
<Card v-if="displayDependencies.length > 0">
<h2>Dependencies</h2>
<div v-for="dependency in displayDependencies" :key="dependency.title">
<router-link v-if="dependency.link" class="btn dependency" :to="dependency.link">
<Avatar size="sm" :src="dependency.icon" />
<div>
<span class="title"> {{ dependency.title }} </span> <br />
<span> {{ dependency.subtitle }} </span>
</div>
</router-link>
<div v-else class="dependency disabled" disabled="">
<Avatar size="sm" :src="dependency.icon" />
<div class="text">
<div class="title">{{ dependency.title }}</div>
<div>{{ dependency.subtitle }}</div>
</div>
</div>
</div>
</Card>
</div>
<Card class="metadata-card">
<h3 class="card-title">Metadata</h3>
<div class="metadata">
<div class="metadata-item">
<span class="metadata-label">Release Channel</span>
<span class="metadata-value"
><Badge
:color="releaseColor(version.version_type)"
:type="
version.version_type.charAt(0).toUpperCase() + version.version_type.slice(1)
"
/></span>
</div>
<div class="metadata-item">
<span class="metadata-label">Version Number</span>
<span class="metadata-value">{{ version.version_number }}</span>
</div>
<div class="metadata-item">
<span class="metadata-label">Loaders</span>
<span class="metadata-value">{{
version.loaders
.map((loader) => loader.charAt(0).toUpperCase() + loader.slice(1))
.join(', ')
}}</span>
</div>
<div class="metadata-item">
<span class="metadata-label">Game Versions</span>
<span class="metadata-value"> {{ version.game_versions.join(', ') }} </span>
</div>
<div class="metadata-item">
<span class="metadata-label">Downloads</span>
<span class="metadata-value">{{ version.downloads }}</span>
</div>
<div class="metadata-item">
<span class="metadata-label">Publication Date</span>
<span class="metadata-value">
{{ formatDateTime(version.date_published) }}
</span>
</div>
<div v-if="author" class="metadata-item">
<span class="metadata-label">Author</span>
<a
:href="`https://modrinth.com/user/${author.user.username}`"
rel="external"
class="metadata-value btn author"
>
<Avatar size="sm" :src="author.user.avatar_url" circle />
<span>
<strong>
{{ author.user.username }}
</strong>
<br />
{{ author.role }}
</span>
</a>
</div>
<div class="metadata-item">
<span class="metadata-label">Version ID</span>
<span class="metadata-value"><CopyCode class="copycode" :text="version.id" /></span>
</div>
</div>
</Card>
</div>
</div>
</template>
<script setup lang="ts">
import type { Labrinth } from '@modrinth/api-client'
import {
CheckIcon,
ChevronLeftIcon,
DownloadIcon,
ExternalIcon,
MoreVerticalIcon,
ReportIcon,
} from '@modrinth/assets'
<script setup>
import { CheckIcon, DownloadIcon, ExternalIcon, FileIcon, ReportIcon } from '@modrinth/assets'
import {
Avatar,
Badge,
Breadcrumbs,
ButtonStyled,
commonMessages,
defineMessages,
type DependencyContext,
OverflowMenu,
useVIntl,
VersionPage,
Card,
CopyCode,
useFormatBytes,
useFormatDateTime,
} from '@modrinth/ui'
import { ref, watch } from 'vue'
import { renderString } from '@modrinth/utils'
import { computed, ref, watch } from 'vue'
import { useRoute } from 'vue-router'
import { SwapIcon } from '@/assets/icons'
import { get_project_many, get_version_many } from '@/helpers/cache.js'
import { releaseColor } from '@/helpers/utils'
import { useBreadcrumbs } from '@/store/breadcrumbs'
const { formatMessage } = useVIntl()
const formatDateTime = useFormatDateTime({
timeStyle: 'short',
dateStyle: 'long',
})
const formatBytes = useFormatBytes()
const messages = defineMessages({
allVersions: {
id: 'app.project.version.all-versions',
defaultMessage: 'All versions',
const breadcrumbs = useBreadcrumbs()
const route = useRoute()
const props = defineProps({
project: {
type: Object,
required: true,
},
versions: {
type: Array,
required: true,
},
members: {
type: Array,
required: true,
},
install: {
type: Function,
required: true,
},
installed: {
type: Boolean,
required: true,
},
installing: {
id: 'app.project.version.installing',
defaultMessage: 'Installing',
type: Boolean,
required: true,
},
downloadInBrowser: {
id: 'app.project.version.download-in-browser',
defaultMessage: 'Download in browser',
installedVersion: {
type: String,
required: true,
},
})
const breadcrumbs = useBreadcrumbs()
const route = useRoute()
const props = defineProps<{
project: Labrinth.Projects.v2.Project
versions: Labrinth.Versions.v3.Version[]
members: Labrinth.Projects.v3.TeamMember[]
install: (version: string | null) => void
installed: boolean
installing: boolean
installedVersion: string
}>()
const version = ref(props.versions.find((version) => version.id === route.params.version))
if (version.value) {
breadcrumbs.setName('Version', version.value.name)
}
breadcrumbs.setName('Version', version.value.name)
const enrichment = ref<Labrinth.Projects.v2.DependencyInfo | undefined>(undefined)
const enrichmentLoading = ref(false)
watch(
() => props.versions,
async () => {
if (route.params.version) {
version.value = props.versions.find((version) => version.id === route.params.version)
await refreshDisplayDependencies()
breadcrumbs.setName('Version', version.value.name)
}
},
)
function buildProjectHref(path: string): string {
const author = computed(() =>
props.members ? props.members.find((member) => member.user.id === version.value.author_id) : null,
)
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) {
if (v != null) params.append(key, v)
}
for (const v of val) params.append(key, v)
} else if (val) {
params.append(key, String(val))
}
@@ -155,65 +262,222 @@ function buildProjectHref(path: string): string {
return qs ? `${path}?${qs}` : path
}
function createDependencyLink(context: DependencyContext): string | undefined {
if (context.version) {
return buildProjectHref(`/project/${context.version.project_id}/version/${context.version.id}`)
}
if (context.project) {
return buildProjectHref(`/project/${context.project.id}`)
}
return undefined
}
async function refreshEnrichment() {
if (!version.value) return
const projectIds = new Set<string>()
const versionIds = new Set<string>()
for (const dependency of version.value.dependencies ?? []) {
if (dependency.project_id) {
projectIds.add(dependency.project_id)
}
if (dependency.version_id) {
versionIds.add(dependency.version_id)
}
}
if (projectIds.size === 0 && versionIds.size === 0) {
enrichment.value = { projects: [], versions: [] }
return
}
enrichmentLoading.value = true
try {
const versionResults = versionIds.size > 0 ? await get_version_many([...versionIds]) : []
for (const dependencyVersion of versionResults ?? []) {
if (dependencyVersion.project_id) {
projectIds.add(dependencyVersion.project_id)
async function refreshDisplayDependencies() {
const projectIds = new Set()
const versionIds = new Set()
if (version.value.dependencies) {
for (const dependency of version.value.dependencies) {
if (dependency.project_id) {
projectIds.add(dependency.project_id)
}
if (dependency.version_id) {
versionIds.add(dependency.version_id)
}
}
const projectResults = projectIds.size > 0 ? await get_project_many([...projectIds]) : []
enrichment.value = {
projects: projectResults ?? [],
versions: versionResults ?? [],
}
} finally {
enrichmentLoading.value = false
}
}
const [projectDeps, versionDeps] = await Promise.all([
get_project_many([...projectIds]),
get_version_many([...versionIds]),
])
watch(
() => props.versions,
async () => {
if (route.params.version) {
version.value = props.versions.find((v) => v.id === route.params.version)
if (version.value) {
breadcrumbs.setName('Version', version.value.name)
const dependencies = {
projects: projectDeps,
versions: versionDeps,
}
displayDependencies.value = version.value.dependencies.map((dependency) => {
const version = dependencies.versions.find((obj) => obj.id === dependency.version_id)
if (version) {
const project = dependencies.projects.find(
(obj) => obj.id === version.project_id || obj.id === dependency.project_id,
)
return {
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}`),
}
await refreshEnrichment()
}
},
)
} else {
const project = dependencies.projects.find((obj) => obj.id === dependency.project_id)
await refreshEnrichment()
if (project) {
return {
icon: project?.icon_url,
title: project?.title || project?.name,
subtitle: `${dependency.dependency_type}`,
link: buildProjectHref(`/project/${project.slug}`),
}
} else {
return {
icon: null,
title: dependency.file_name,
subtitle: `Added via overrides`,
link: null,
}
}
}
})
}
await refreshDisplayDependencies()
</script>
<style scoped lang="scss">
.version-container {
display: flex;
flex-direction: row;
gap: 1rem;
}
.version-title {
margin-bottom: 1rem;
h2 {
font-size: var(--font-size-2xl);
font-weight: 700;
color: var(--color-contrast);
margin: 0;
}
}
.dependency {
display: flex;
padding: 0.5rem 1rem 0.5rem 0.5rem;
gap: 0.5rem;
background: var(--color-raised-bg);
color: var(--color-base);
width: 100%;
.title {
font-weight: bolder;
}
:deep(svg) {
margin-right: 0 !important;
}
}
.file {
display: flex;
flex-direction: row;
gap: 0.5rem;
background: var(--color-button-bg);
color: var(--color-base);
padding: 0.5rem 1rem;
.download {
margin-left: auto;
background-color: var(--color-raised-bg);
}
.label {
display: flex;
margin: auto 0 auto;
gap: 0.5rem;
.title {
font-weight: bolder;
word-break: break-all;
}
svg {
min-width: 1.1rem;
min-height: 1.1rem;
width: 1.1rem;
height: 1.1rem;
margin: auto 0;
}
.primary-label {
font-style: italic;
}
}
}
.primary {
background: var(--color-brand-highlight);
color: var(--color-contrast);
}
.button-group {
display: flex;
flex-wrap: wrap;
flex-direction: row;
gap: 0.5rem;
}
.card-title {
font-size: var(--font-size-lg);
color: var(--color-contrast);
margin: 0 0 0.5rem;
}
.description-cards {
width: 100%;
}
.metadata-card {
width: 20rem;
height: min-content;
}
.metadata {
display: flex;
flex-direction: column;
flex-wrap: wrap;
gap: 1rem;
.metadata-item {
display: flex;
flex-direction: column;
gap: 0.5rem;
.metadata-label {
font-weight: bold;
}
}
}
.author {
display: flex;
flex-direction: row;
gap: 0.5rem;
align-items: center;
text-decoration: none;
color: var(--color-base);
background: var(--color-raised-bg);
padding: 0.5rem;
width: 100%;
box-shadow: none;
}
.markdown-body {
:deep(hr),
:deep(h1),
:deep(h2),
img {
max-width: max(60rem, 90%) !important;
}
:deep(ul),
:deep(ol) {
margin-left: 2rem;
}
}
.copycode {
border: 0;
color: var(--color-contrast);
}
.disabled {
display: flex;
flex-direction: row;
vertical-align: center;
align-items: center;
cursor: not-allowed;
border-radius: var(--radius-lg);
.text {
filter: brightness(0.5);
}
}
</style>
@@ -5,23 +5,16 @@
:game-versions="gameVersions"
:versions="versions"
:project="project"
:show-environment-column="themeStore.featureFlags.show_version_environment_column"
:version-link="(version) => buildProjectHref(`/project/${project.id}/version/${version.id}`)"
>
<template #actions="{ version }">
<ButtonStyled
circular
type="transparent"
:color="installed && version.id === installedVersion ? 'standard' : 'green'"
>
<ButtonStyled circular type="transparent">
<button
v-tooltip="
!installed
? formatMessage(commonMessages.installButton)
: version.id !== installedVersion
? formatMessage(commonMessages.switchToVersionButton)
: formatMessage(messages.alreadyInstalled)
"
v-tooltip="`Install`"
:class="{
'group-hover:!bg-brand group-hover:[&>svg]:!text-brand-inverted':
!installed || version.id !== installedVersion,
}"
:disabled="installing || (installed && version.id === installedVersion)"
@click.stop="() => install(version.id)"
>
@@ -33,6 +26,7 @@
<ButtonStyled circular type="transparent">
<OverflowMenu
v-if="false"
class="group-hover:!bg-button-bg"
:options="[
{
id: 'install-elsewhere',
@@ -53,13 +47,12 @@
<DownloadIcon aria-hidden="true" />
Add to another instance
</template>
<template #open-in-browser>
<ExternalIcon /> {{ formatMessage(commonMessages.openInBrowserButton) }}
</template>
<template #open-in-browser> <ExternalIcon /> Open in browser </template>
</OverflowMenu>
<a
v-else
v-tooltip="formatMessage(commonMessages.openInBrowserButton)"
v-tooltip="`Open in browser`"
class="group-hover:!bg-button-bg"
:href="`https://modrinth.com/${project.project_type}/${project.slug}/version/${version.id}`"
target="_blank"
>
@@ -75,29 +68,15 @@
import { CheckIcon, DownloadIcon, ExternalIcon, MoreVerticalIcon } from '@modrinth/assets'
import {
ButtonStyled,
commonMessages,
defineMessages,
injectNotificationManager,
OverflowMenu,
ProjectPageVersions,
useVIntl,
} 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'
import { useTheming } from '@/store/theme.ts'
const { formatMessage } = useVIntl()
const themeStore = useTheming()
const messages = defineMessages({
alreadyInstalled: {
id: 'app.project.versions.already-installed',
defaultMessage: 'Already installed',
},
})
defineProps({
project: {
@@ -807,11 +807,6 @@ export function createContentInstall(opts: {
) {
const project: Labrinth.Projects.v2.Project = await get_project(projectId, 'must_revalidate')
if (!project) {
opts.handleError(`Project not found: '${projectId}'`)
return
}
if (project.project_type === 'modpack') {
const version = versionId ?? project.versions[project.versions.length - 1]
const packs = await list()
@@ -110,7 +110,7 @@ export function setupCreationModal(notificationManager: AbstractWebNotificationM
}
const preview = await install_get_modpack_preview(location)
if (preview.unknownFile || preview.externalFilesInModpack.length > 0) {
if (preview.unknownFile) {
const splitPath = config.modpackFilePath.value.split(/[\\/]/)
const fileName = splitPath
? splitPath[splitPath.length - 1]
@@ -119,7 +119,6 @@ export function setupCreationModal(notificationManager: AbstractWebNotificationM
unknownPackWarningModal.value?.show(
() => install_create_modpack_instance(location).then(() => undefined),
fileName,
preview.externalFilesInModpack,
)
} else {
await install_create_modpack_instance(location)
-1
View File
@@ -247,7 +247,6 @@ export default new createRouter({
name: 'Logs',
component: Instance.Logs,
meta: {
renderMode: 'fixed',
useRootContext: true,
breadcrumb: [{ name: '?Instance', link: '/instance/{id}/' }, { name: 'Logs' }],
},
-13
View File
@@ -6,7 +6,6 @@ export const useError = defineStore('errorsStore', {
state: () => ({
errorModal: null,
minecraftAuthErrorModal: null,
minecraftRequiredModal: null,
}),
actions: {
setErrorModal(ref) {
@@ -15,19 +14,7 @@ export const useError = defineStore('errorsStore', {
setMinecraftAuthErrorModal(ref) {
this.minecraftAuthErrorModal = ref
},
setMinecraftRequiredModal(ref) {
this.minecraftRequiredModal = ref
},
showError(error, context, closable = true, source = null) {
const errorMessage = error.message?.toLowerCase()
if (
(errorMessage?.includes('user is not logged in') ||
errorMessage?.includes('cannot play instance since minecraft is required')) &&
this.minecraftRequiredModal
) {
this.minecraftRequiredModal.show()
return
}
if (
error.message &&
(error.message.includes('Minecraft authentication error:') ||
-2
View File
@@ -8,7 +8,6 @@ export const DEFAULT_FEATURE_FLAGS = {
worlds_tab: false,
worlds_in_home: true,
server_project_qa: false,
show_version_environment_column: false,
server_ram_as_bytes_always_on: false,
always_show_app_controls: false,
skip_non_essential_warnings: false,
@@ -16,7 +15,6 @@ export const DEFAULT_FEATURE_FLAGS = {
pride_fundraiser: true,
i18n_debug: false,
show_instance_play_time: true,
advanced_filters_collapsed: true,
}
export const THEME_OPTIONS = ['dark', 'light', 'oled', 'system'] as const
-9
View File
@@ -157,7 +157,6 @@ fn main() {
"install_job_retry",
"install_job_cancel",
"install_job_dismiss",
"install_job_support_details",
])
.default_permission(
DefaultPermissionRule::AllowAllCommands,
@@ -202,7 +201,6 @@ fn main() {
"instance_install_project_with_dependencies",
"instance_switch_project_version_with_dependencies",
"instance_add_project_from_path",
"instance_is_file_on_modrinth",
"instance_toggle_disable_project",
"instance_remove_project",
"instance_update_managed_modrinth_version",
@@ -278,13 +276,6 @@ fn main() {
"hide_ads_window",
"scroll_ads_window",
"show_ads_window",
"show_ads_consent_overlay",
"show_ads_consent_preferences",
"open_ads_consent_preferences",
"hide_ads_consent_preferences",
"hide_ads_consent_overlay",
"get_ads_consent_required",
"perform_ads_consent_action",
"record_ads_click",
"open_link",
"get_ads_personalization",
+4 -865
View File
@@ -1,163 +1,14 @@
const MODRINTH_ORIGIN = 'https://modrinth.com'
function installAdsConsentThemeStyle() {
if (document.getElementById('modrinth-ads-consent-theme-style')) return
const style = document.createElement('style')
style.id = 'modrinth-ads-consent-theme-style'
style.textContent = `
:root {
--modrinth-usp-bg: #27292e;
--modrinth-usp-surface: #34363c;
--modrinth-usp-divider: #34363c;
--modrinth-usp-text: #b0bac5;
--modrinth-usp-contrast: #ffffff;
--modrinth-usp-brand: #1bd96a;
--modrinth-usp-link: #4f9cff;
--modrinth-usp-accent-contrast: #000000;
--modrinth-usp-shadow: rgba(0, 0, 0, 0.1) 0 4px 6px -1px,
rgba(0, 0, 0, 0.06) 0 2px 4px -1px;
color-scheme: dark;
}
#qc-cmp2-usp {
background: var(--modrinth-usp-bg) !important;
border: 1px solid var(--modrinth-usp-divider) !important;
border-radius: 1rem !important;
box-shadow: var(--modrinth-usp-shadow) !important;
color: var(--modrinth-usp-text) !important;
font-family: Inter, -apple-system, BlinkMacSystemFont, 'Segoe UI', Oxygen, Ubuntu, Roboto,
Cantarell, 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif !important;
max-width: 660px;
}
#qc-cmp2-usp .qc-usp-ui-content,
#qc-cmp2-usp .qc-usp-ui-form-content,
#qc-cmp2-usp .qc-usp-container {
background: transparent !important;
}
#qc-cmp2-usp .qc-usp-container{
margin-bottom: 12px;
}
#qc-cmp2-usp p,
#qc-cmp2-usp label,
#qc-cmp2-usp .qc-usp-action-description {
color: var(--modrinth-usp-text) !important;
font-family: inherit !important;
}
#qc-cmp2-usp .qc-usp-title,
#qc-cmp2-usp .qc-cmp2-list-item-title {
color: var(--modrinth-usp-contrast) !important;
font-family: inherit !important;
font-weight: 700 !important;
}
#qc-cmp2-usp .qc-usp-title {
font-size: 1.25rem !important;
}
#qc-cmp2-usp a,
#qc-cmp2-usp .qc-usp-alt-action {
color: var(--modrinth-usp-link) !important;
}
#qc-cmp2-usp .qc-cmp2-list-item {
background: var(--modrinth-usp-surface) !important;
border: 1px solid var(--modrinth-usp-divider) !important;
border-radius: 0.75rem !important;
}
#qc-cmp2-usp .qc-cmp2-list-item-header {
background: transparent !important;
border: 0 !important;
color: var(--modrinth-usp-contrast) !important;
}
#qc-cmp2-usp .qc-cmp2-list-item-header svg {
color: var(--modrinth-usp-text) !important;
}
#qc-cmp2-usp .qc-cmp2-toggle {
background: var(--modrinth-usp-bg) !important;
border-color: var(--modrinth-usp-divider) !important;
}
#qc-cmp2-usp .qc-cmp2-toggle .toggle {
background: var(--modrinth-usp-contrast) !important;
}
#qc-cmp2-usp .qc-cmp2-toggle .text {
color: var(--modrinth-usp-contrast) !important;
}
#qc-cmp2-usp .qc-cmp2-toggle[aria-checked='true'] {
background: var(--modrinth-usp-brand) !important;
border-color: var(--modrinth-usp-brand) !important;
}
#qc-cmp2-usp .qc-cmp2-toggle[aria-checked='true'] .text {
color: var(--modrinth-usp-accent-contrast) !important;
}
#qc-cmp2-usp button[mode='primary'] {
background: var(--modrinth-usp-brand) !important;
border: 0 !important;
border-radius: 0.75rem !important;
color: var(--modrinth-usp-accent-contrast) !important;
font-family: inherit !important;
font-weight: 700 !important;
}
#qc-cmp2-usp .qc-usp-close-icon {
border: 0 !important;
background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M18 6 6 18'/%3E%3Cpath d='m6 6 12 12'/%3E%3C/svg%3E")
center / 1.5rem 1.5rem no-repeat;
}
#qc-cmp2-usp a:focus-visible,
#qc-cmp2-usp button:focus-visible {
outline: 2px solid var(--modrinth-usp-brand) !important;
outline-offset: 2px !important;
}
#qc-cmp2-usp .qc-usp-ui-content {
max-width: 100% !important;
}
#qc-cmp2-usp .qc-usp-ui-content .qc-usp-ui-form-content {
border: 1px solid transparent !important;
padding: 0 !important;
}
`
document.documentElement.appendChild(style)
}
document.addEventListener(
'click',
function (e) {
window.top.postMessage({ modrinthAdClick: true }, MODRINTH_ORIGIN)
window.top.postMessage({ modrinthAdClick: true }, 'https://modrinth.com')
let target = e.target
const uspCloseIcon = target?.closest?.('.qc-usp-close-icon')
if (target?.closest?.('.qc-cmp2-close-icon') || uspCloseIcon) {
if (isAdsConsentReprompt()) {
setTimeout(finishAdsConsentReprompt)
} else if (document.documentElement.classList.contains('modrinth-ads-consent-preferences')) {
setTimeout(() => void restoreAdsConsentNotification())
}
}
if (target?.closest?.('#qc-cmp2-usp .qc-usp-ui-form-content button[mode="primary"]')) {
beginUspConsentCommit()
}
while (target != null) {
if (target.matches('a')) {
e.preventDefault()
if (target.href) {
window.top.postMessage({ modrinthOpenUrl: target.href }, MODRINTH_ORIGIN)
window.top.postMessage({ modrinthOpenUrl: target.href }, 'https://modrinth.com')
}
break
}
@@ -168,706 +19,7 @@ document.addEventListener(
)
window.open = (url, target, features) => {
window.top.postMessage({ modrinthOpenUrl: url }, MODRINTH_ORIGIN)
}
const modrinthAdsConsentState = {
phase: 'idle',
variant: null,
commitTimeout: null,
}
let modrinthTcfListenerInstalled = false
let modrinthTcfListenerAttempts = 0
let modrinthGppListenerInstalled = false
let modrinthGppListenerAttempts = 0
let modrinthAdsConsentActionRequestId = 0
const modrinthAdsConsentActionResolvers = new Map()
function transitionAdsConsent(event) {
const phase = modrinthAdsConsentState.phase
if (event === 'prompt-detected') {
if (phase === 'idle') modrinthAdsConsentState.phase = 'initial'
} else if (event === 'reprompt-started') {
modrinthAdsConsentState.phase = 'reprompt'
} else if (event === 'commit-started') {
modrinthAdsConsentState.phase = isAdsConsentReprompt()
? 'reprompt-committing'
: 'initial-committing'
} else if (event === 'commit-timed-out') {
if (phase === 'reprompt-committing') {
modrinthAdsConsentState.phase = 'reprompt'
} else if (phase === 'initial-committing') {
modrinthAdsConsentState.phase = 'initial'
}
} else if (event === 'completed') {
modrinthAdsConsentState.phase = 'complete'
}
}
function isAdsConsentReprompt() {
return (
modrinthAdsConsentState.phase === 'reprompt' ||
modrinthAdsConsentState.phase === 'reprompt-committing'
)
}
function isUspConsentCommitPending() {
return (
modrinthAdsConsentState.phase === 'initial-committing' ||
modrinthAdsConsentState.phase === 'reprompt-committing'
)
}
function installAdsRailStyle() {
if (document.getElementById('modrinth-ads-rail-style')) {
return
}
const style = document.createElement('style')
style.id = 'modrinth-ads-rail-style'
style.textContent = `
html.modrinth-ads-consent-preferences #modrinth-rail-1 {
display: none !important;
}
`
document.documentElement.appendChild(style)
}
function installAdsConsentOverlayStyle() {
if (document.getElementById('modrinth-ads-consent-overlay-style')) {
return
}
const style = document.createElement('style')
style.id = 'modrinth-ads-consent-overlay-style'
style.textContent = `
html.modrinth-ads-consent-overlay:not(.modrinth-ads-consent-preferences) #qc-cmp2-container,
html.modrinth-ads-consent-preferences:not(.modrinth-ads-consent-preferences-visible) #qc-cmp2-container {
display: none !important;
}
#qc-cmp2-usp .qc-usp-close-icon {
display: none !important;
}
`
document.documentElement.appendChild(style)
}
function getTauriInvoke() {
return window.__TAURI__?.core?.invoke ?? window.__TAURI_INTERNALS__?.invoke
}
function invokeAdsConsentOverlayCommand(shown) {
const invoke = getTauriInvoke()
if (typeof invoke !== 'function') {
return
}
const command = shown ? 'show_ads_consent_overlay' : 'hide_ads_consent_overlay'
const args = shown ? {} : { dpr: window.devicePixelRatio }
invoke(`plugin:ads|${command}`, args).catch(() => {})
}
function prepareAdsConsentPreferences() {
installAdsRailStyle()
installAdsConsentOverlayStyle()
document.documentElement.classList.add('modrinth-ads-consent-preferences')
document.documentElement.classList.remove('modrinth-ads-consent-preferences-visible')
}
function revealAdsConsentPreferences() {
document.documentElement.classList.add('modrinth-ads-consent-preferences-visible')
}
function concealAdsConsentPreferences() {
document.documentElement.classList.remove('modrinth-ads-consent-preferences')
document.documentElement.classList.remove('modrinth-ads-consent-preferences-visible')
installAdsConsentOverlayStyle()
}
async function expandAdsConsentWebview() {
const invoke = getTauriInvoke()
if (typeof invoke !== 'function') {
throw new Error('Tauri invoke is unavailable in the ads webview')
}
await invoke('plugin:ads|show_ads_consent_preferences')
}
function sendAdsConsentCommandToChildFrames(command) {
document.querySelectorAll('iframe').forEach((frame) => {
frame.contentWindow?.postMessage({ modrinthAdsConsentCommand: command }, '*')
})
}
function isDirectChildFrame(source) {
return Array.from(document.querySelectorAll('iframe')).some(
(frame) => frame.contentWindow === source,
)
}
function displayUspConsentUi() {
if (typeof window.__uspapi !== 'function') return false
try {
window.__uspapi('displayUspUi', 1, () => {})
return true
} catch {
return false
}
}
function detectAdsConsentVariant() {
let variant = null
if (document.getElementById('qc-cmp2-usp')) {
variant = 'usp'
} else if (document.getElementById('qc-cmp2-ui')) {
variant = 'tcf'
}
if (variant) {
modrinthAdsConsentState.variant = variant
}
return variant
}
function findTcfConsentButton(action) {
const dialog = document.getElementById('qc-cmp2-ui')
if (!dialog) return null
const buttonIds = {
accept: 'accept-btn',
reject: 'disagree-btn',
manage: 'more-options-btn',
}
const buttonId = buttonIds[action]
if (!buttonId) return null
const button = dialog.querySelector(`#${buttonId}`)
return button && !button.disabled ? button : null
}
function getUspConsentControls() {
const dialog = document.getElementById('qc-cmp2-usp')
if (!dialog) return null
const toggles = Array.from(
dialog.querySelectorAll('.qc-usp-container button.qc-cmp2-toggle[role="switch"]'),
)
const confirmButton = dialog.querySelector('.qc-usp-ui-form-content button[mode="primary"]')
if (
toggles.length === 0 ||
!confirmButton ||
confirmButton.disabled ||
toggles.some(
(toggle) =>
toggle.disabled || !['true', 'false'].includes(toggle.getAttribute('aria-checked')),
)
) {
return null
}
return { toggles, confirmButton }
}
function waitForUspToggleState(index, checked, expectedCount, deadline) {
return new Promise((resolve) => {
function checkState() {
const controls = getUspConsentControls()
if (
controls &&
controls.toggles.length === expectedCount &&
controls.toggles[index]?.getAttribute('aria-checked') === String(checked)
) {
resolve(controls)
} else if (Date.now() >= deadline) {
resolve(null)
} else {
setTimeout(checkState, 50)
}
}
checkState()
})
}
async function setUspToggleStates(checked, controls, timeoutMs) {
const expectedCount = controls.toggles.length
const deadline = Date.now() + timeoutMs
for (let index = 0; index < expectedCount; index += 1) {
controls = getUspConsentControls()
if (!controls || controls.toggles.length !== expectedCount) return null
const toggle = controls.toggles[index]
if (toggle.getAttribute('aria-checked') !== String(checked)) {
toggle.click()
controls = await waitForUspToggleState(index, checked, expectedCount, deadline)
if (!controls) return null
}
}
return controls
}
async function performAdsConsentActionInDocument(action, onHandled) {
const variant = detectAdsConsentVariant()
const unknownDialog = document.querySelector('#qc-cmp2-container [role="dialog"]')
if (action === 'show') {
if (variant || unknownDialog) {
onHandled?.()
return 'handled'
}
return 'not-ready'
}
if (variant === 'usp') {
if (action === 'manage') {
onHandled?.()
return 'handled'
}
if (!['accept', 'reject'].includes(action)) return 'failed'
const controls = getUspConsentControls()
if (!controls) return 'not-ready'
const shouldOptOut = action === 'reject'
const settledControls = await setUspToggleStates(shouldOptOut, controls, 2000)
if (!settledControls) return 'failed'
// CMP navigation can replace this document during the click, so acknowledge it first.
onHandled?.()
beginUspConsentCommit()
settledControls.confirmButton.click()
return 'handled'
}
if (variant === 'tcf') {
const button = findTcfConsentButton(action)
if (!button) return 'not-ready'
// CMP navigation can replace this document during the click, so acknowledge it first.
onHandled?.()
button.click()
return 'handled'
}
if (action === 'manage' && unknownDialog) {
onHandled?.()
return 'handled'
}
return 'not-ready'
}
function performAdsConsentActionWhenReady(action, timeoutMs, onHandled) {
const deadline = Date.now() + timeoutMs
return new Promise((resolve) => {
async function tryAction() {
const result = await performAdsConsentActionInDocument(action, onHandled)
if (result === 'handled') {
resolve(true)
} else if (result === 'failed' || Date.now() >= deadline) {
resolve(false)
} else {
setTimeout(tryAction, 50)
}
}
void tryAction()
})
}
function performAdsConsentActionAcrossFrames(action, timeoutMs) {
const requestId = `${Date.now()}-${++modrinthAdsConsentActionRequestId}`
return new Promise((resolve) => {
let settled = false
const settle = (clicked) => {
if (settled) return
settled = true
clearTimeout(timeout)
modrinthAdsConsentActionResolvers.delete(requestId)
resolve(clicked)
}
const timeout = setTimeout(() => settle(false), timeoutMs)
modrinthAdsConsentActionResolvers.set(requestId, () => settle(true))
sendAdsConsentCommandToChildFrames({ type: 'perform', action, requestId, timeoutMs })
void performAdsConsentActionWhenReady(action, timeoutMs, () => settle(true))
})
}
function waitForAdsConsentLayout() {
return new Promise((resolve) => setTimeout(resolve, 100))
}
async function restoreAdsConsentNotification() {
concealAdsConsentPreferences()
sendAdsConsentCommandToChildFrames({ type: 'conceal' })
const invoke = getTauriInvoke()
if (typeof invoke === 'function') {
await invoke('plugin:ads|hide_ads_consent_preferences')
}
}
async function showNativeAdsConsentUi() {
prepareAdsConsentPreferences()
await waitForAdsConsentLayout()
sendAdsConsentCommandToChildFrames({ type: 'prepare' })
await expandAdsConsentWebview()
await waitForAdsConsentLayout()
revealAdsConsentPreferences()
sendAdsConsentCommandToChildFrames({ type: 'reveal' })
window.dispatchEvent(new Event('resize'))
sendAdsConsentCommandToChildFrames({ type: 'resize' })
const shown = await performAdsConsentActionAcrossFrames('show', 2500)
if (!shown) {
await restoreAdsConsentNotification()
}
return shown
}
function finishAdsConsentReprompt() {
transitionAdsConsent('completed')
clearTimeout(modrinthAdsConsentState.commitTimeout)
modrinthAdsConsentState.commitTimeout = null
document.documentElement.classList.remove('modrinth-ads-consent-overlay')
concealAdsConsentPreferences()
sendAdsConsentCommandToChildFrames({ type: 'conceal' })
invokeAdsConsentOverlayCommand(false)
}
async function openAdsConsentPreferences() {
if (!(await showNativeAdsConsentUi())) return
await performAdsConsentActionAcrossFrames('manage', 2500)
}
async function performAdsConsentAction(action) {
if (!['accept', 'reject', 'manage'].includes(action)) return
if (action === 'manage') {
try {
await openAdsConsentPreferences()
} catch {
await restoreAdsConsentNotification()
}
return
}
const handled = await performAdsConsentActionAcrossFrames(action, 2500)
if (!handled) {
try {
await showNativeAdsConsentUi()
} catch {
await restoreAdsConsentNotification()
}
}
}
window.modrinthAdsConsentAction = (action) => {
void performAdsConsentAction(action)
}
function isUspConsentApplicable() {
if (detectAdsConsentVariant() === 'usp') return Promise.resolve(true)
if (typeof window.__uspapi !== 'function') return Promise.resolve(false)
return new Promise((resolve) => {
let settled = false
const settle = (applicable) => {
if (settled) return
settled = true
clearTimeout(timeout)
resolve(applicable)
}
const timeout = setTimeout(() => settle(false), 500)
try {
window.__uspapi('uspPing', 1, (data, success) => {
if (!success || !data) {
settle(false)
return
}
const modes = Array.isArray(data.mode) ? data.mode : [data.mode]
const jurisdictions = Array.isArray(data.jurisdiction)
? data.jurisdiction
: [data.jurisdiction]
const location = String(data.location ?? '').toUpperCase()
const hasUspMode = modes.some((mode) =>
String(mode ?? '')
.toUpperCase()
.includes('USP'),
)
const locationApplies =
!location ||
jurisdictions.some((jurisdiction) =>
String(jurisdiction ?? '')
.toUpperCase()
.includes(location),
)
settle(hasUspMode && locationApplies)
})
} catch {
settle(false)
}
})
}
async function displayAdsConsentReprompt() {
if (
(modrinthAdsConsentState.variant === 'usp' || (await isUspConsentApplicable())) &&
typeof window.__uspapi === 'function'
) {
modrinthAdsConsentState.variant = 'usp'
return displayUspConsentUi()
}
if (typeof window.__tcfapi === 'function') {
modrinthAdsConsentState.variant = 'tcf'
window.__tcfapi('displayConsentUi', 2, () => {})
return true
}
return false
}
window.modrinthAdsReopenConsentPreferences = async () => {
if (document.documentElement.classList.contains('modrinth-ads-consent-overlay')) {
try {
await openAdsConsentPreferences()
} catch {
await restoreAdsConsentNotification()
}
return
}
transitionAdsConsent('reprompt-started')
prepareAdsConsentPreferences()
sendAdsConsentCommandToChildFrames({ type: 'prepare' })
try {
await waitForAdsConsentLayout()
await expandAdsConsentWebview()
await waitForAdsConsentLayout()
revealAdsConsentPreferences()
sendAdsConsentCommandToChildFrames({ type: 'reveal' })
window.dispatchEvent(new Event('resize'))
sendAdsConsentCommandToChildFrames({ type: 'resize' })
if (!(await displayAdsConsentReprompt())) {
finishAdsConsentReprompt()
return
}
if (!(await performAdsConsentActionAcrossFrames('show', 2500))) {
finishAdsConsentReprompt()
return
}
await performAdsConsentActionAcrossFrames('manage', 2500)
} catch {
finishAdsConsentReprompt()
}
}
window.addEventListener('message', (event) => {
const resultRequestId = event.data?.modrinthAdsConsentResult
if (typeof resultRequestId === 'string' && isDirectChildFrame(event.source)) {
if (window.top === window) {
modrinthAdsConsentActionResolvers.get(resultRequestId)?.()
} else {
window.parent.postMessage({ modrinthAdsConsentResult: resultRequestId }, '*')
}
return
}
if (window.top === window || event.source !== window.parent) return
const command = event.data?.modrinthAdsConsentCommand
if (!command || typeof command !== 'object') return
if (command.type === 'prepare') {
prepareAdsConsentPreferences()
sendAdsConsentCommandToChildFrames(command)
} else if (command.type === 'reveal') {
revealAdsConsentPreferences()
sendAdsConsentCommandToChildFrames(command)
} else if (command.type === 'conceal') {
concealAdsConsentPreferences()
sendAdsConsentCommandToChildFrames(command)
} else if (command.type === 'resize') {
window.dispatchEvent(new Event('resize'))
sendAdsConsentCommandToChildFrames(command)
} else if (
command.type === 'perform' &&
typeof command.action === 'string' &&
typeof command.requestId === 'string' &&
typeof command.timeoutMs === 'number'
) {
sendAdsConsentCommandToChildFrames(command)
performAdsConsentActionWhenReady(command.action, command.timeoutMs, () => {
window.parent.postMessage({ modrinthAdsConsentResult: command.requestId }, '*')
})
}
})
function setAdsConsentOverlay(shown) {
if (document.documentElement.classList.contains('modrinth-ads-consent-overlay') === shown) return
installAdsConsentOverlayStyle()
document.documentElement.classList.toggle('modrinth-ads-consent-overlay', shown)
if (!shown) {
document.documentElement.classList.remove('modrinth-ads-consent-preferences')
document.documentElement.classList.remove('modrinth-ads-consent-preferences-visible')
}
if (window.top === window) {
invokeAdsConsentOverlayCommand(shown)
} else {
window.top.postMessage({ modrinthAdsConsentOverlay: shown }, MODRINTH_ORIGIN)
}
}
if (window.top === window) {
window.addEventListener('message', (event) => {
if (event.origin !== MODRINTH_ORIGIN) return
if (typeof event.data?.modrinthAdsConsentOverlay === 'boolean') {
setAdsConsentOverlay(event.data.modrinthAdsConsentOverlay)
}
})
}
function finishUspConsentCommit() {
if (!isUspConsentCommitPending()) return
const wasReprompt = isAdsConsentReprompt()
transitionAdsConsent('completed')
clearTimeout(modrinthAdsConsentState.commitTimeout)
modrinthAdsConsentState.commitTimeout = null
if (wasReprompt) {
finishAdsConsentReprompt()
} else {
setAdsConsentOverlay(false)
}
}
function beginUspConsentCommit() {
if (!document.getElementById('qc-cmp2-usp')) return
modrinthAdsConsentState.variant = 'usp'
transitionAdsConsent('commit-started')
clearTimeout(modrinthAdsConsentState.commitTimeout)
const deadline = Date.now() + 2500
function checkForDialogClosure() {
if (!isUspConsentCommitPending()) return
if (!document.getElementById('qc-cmp2-usp')) {
finishUspConsentCommit()
} else if (Date.now() >= deadline) {
transitionAdsConsent('commit-timed-out')
modrinthAdsConsentState.commitTimeout = null
} else {
modrinthAdsConsentState.commitTimeout = setTimeout(checkForDialogClosure, 50)
}
}
modrinthAdsConsentState.commitTimeout = setTimeout(checkForDialogClosure, 50)
}
function syncAdsConsentUi() {
const variant = detectAdsConsentVariant()
if (variant && !isAdsConsentReprompt() && modrinthAdsConsentState.phase !== 'complete') {
transitionAdsConsent('prompt-detected')
setAdsConsentOverlay(true)
}
if (isUspConsentCommitPending() && !document.getElementById('qc-cmp2-usp')) {
finishUspConsentCommit()
}
}
function handleGppConsentEvent(gppData, success) {
if (
success &&
gppData?.eventName === 'sectionChange' &&
modrinthAdsConsentState.variant === 'usp' &&
isUspConsentCommitPending()
) {
finishUspConsentCommit()
}
}
function installGppConsentListener() {
if (modrinthGppListenerInstalled) return
if (typeof window.__gpp === 'function') {
modrinthGppListenerInstalled = true
window.__gpp('addEventListener', handleGppConsentEvent)
return
}
if (modrinthGppListenerAttempts < 60) {
modrinthGppListenerAttempts += 1
setTimeout(installGppConsentListener, 500)
}
}
function handleTcfConsentEvent(tcData, success) {
if (!success || !tcData) return
detectAdsConsentVariant()
if (tcData.eventStatus === 'cmpuishown') {
if (isAdsConsentReprompt()) return
transitionAdsConsent('prompt-detected')
setAdsConsentOverlay(true)
} else if (
tcData.eventStatus === 'useractioncomplete' &&
modrinthAdsConsentState.variant === 'tcf'
) {
if (isAdsConsentReprompt()) {
finishAdsConsentReprompt()
return
}
transitionAdsConsent('completed')
setAdsConsentOverlay(false)
}
}
// polling to install listener on tcf api
function installTcfConsentListener() {
if (modrinthTcfListenerInstalled) return
if (typeof window.__tcfapi === 'function') {
modrinthTcfListenerInstalled = true
window.__tcfapi('addEventListener', 2, handleTcfConsentEvent)
return
}
if (modrinthTcfListenerAttempts < 60) {
modrinthTcfListenerAttempts += 1
setTimeout(installTcfConsentListener, 500)
}
window.top.postMessage({ modrinthOpenUrl: url }, 'https://modrinth.com')
}
function muteAudioContext() {
@@ -946,22 +98,9 @@ function muteVideos() {
}
document.addEventListener('DOMContentLoaded', () => {
installAdsConsentThemeStyle()
installAdsRailStyle()
installAdsConsentOverlayStyle()
muteVideos()
muteAudioContext()
syncAdsConsentUi()
installTcfConsentListener()
installGppConsentListener()
const observer = new MutationObserver(() => {
muteVideos()
syncAdsConsentUi()
})
const observer = new MutationObserver(muteVideos)
observer.observe(document.body, { childList: true, subtree: true })
})
syncAdsConsentUi()
installTcfConsentListener()
installGppConsentListener()
+25 -331
View File
@@ -3,7 +3,7 @@ use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};
use std::time::{Duration, Instant};
use tauri::plugin::TauriPlugin;
use tauri::{Emitter, Manager, PhysicalPosition, PhysicalSize, Rect, Runtime};
use tauri::{Manager, PhysicalPosition, PhysicalSize, Runtime};
use tauri_plugin_opener::OpenerExt;
use theseus::settings;
use tokio::sync::RwLock;
@@ -11,16 +11,12 @@ use tokio::sync::RwLock;
pub struct AdsState {
pub shown: bool,
pub modal_shown: bool,
pub consent_required: bool,
pub consent_overlay_shown: bool,
pub occluded: bool,
pub last_click: Option<Instant>,
pub malicious_origins: HashSet<String>,
}
const AD_LINK: &str = "https://modrinth.com/wrapper/app-ads-cookie";
const ADS_CONSENT_REQUIRED_EVENT: &str = "ads-consent-required";
const APP_TITLE_BAR_HEIGHT: f32 = 48.0;
#[cfg(any(windows, target_os = "macos"))]
pub(super) const OCCLUDED_AREA_THRESHOLD: f64 = 0.5;
#[cfg(not(target_os = "linux"))]
@@ -135,16 +131,13 @@ fn set_webview_visible_for_window<R: Runtime>(
.and_then(|window| window.is_minimized().ok())
.unwrap_or(false);
let (is_occluded, consent_overlay_shown) = app
let is_occluded = app
.state::<RwLock<AdsState>>()
.try_read()
.map(|state| (state.occluded, state.consent_overlay_shown))
.unwrap_or((false, false));
.map(|state| state.occluded)
.unwrap_or(false);
set_webview_visible(
webview,
visible && !is_minimized && (!is_occluded || consent_overlay_shown),
);
set_webview_visible(webview, visible && !is_minimized && !is_occluded);
}
#[cfg(any(windows, target_os = "macos"))]
@@ -202,8 +195,7 @@ async fn sync_ads_occlusion<R: Runtime>(app: &tauri::AppHandle<R>) {
}
state.occluded = occluded;
let visible =
state.shown && (!state.modal_shown || state.consent_overlay_shown);
let visible = state.shown && !state.modal_shown;
drop(state);
if let Some(webview) = app.webviews().get("ads-window") {
@@ -219,36 +211,21 @@ fn sync_webview_visibility_for_main_window<R: Runtime>(
let is_minimized = main_window.is_minimized().unwrap_or(false);
let was = was_minimized.load(Ordering::SeqCst);
let ads_state = if is_minimized {
None
} else {
match app.state::<RwLock<AdsState>>().try_read() {
Ok(state) => Some((
state.shown
&& (!state.modal_shown || state.consent_overlay_shown)
&& (!state.occluded || state.consent_overlay_shown),
state.consent_overlay_shown,
)),
Err(_) => None,
}
};
let ads_visible = ads_state.map(|state| state.0).unwrap_or(false);
if ads_state.map(|state| state.1).unwrap_or(false)
&& let Some(webview) = app.webviews().get("ads-window")
&& let Ok((position, size)) =
get_overlay_webview_position_for_window(main_window)
{
webview.set_position(position).ok();
webview.set_size(size).ok();
}
if is_minimized == was {
return;
}
was_minimized.store(is_minimized, Ordering::SeqCst);
let ads_visible = if is_minimized {
false
} else {
match app.state::<RwLock<AdsState>>().try_read() {
Ok(state) => state.shown && !state.modal_shown && !state.occluded,
Err(_) => false,
}
};
let mut webviews = Vec::new();
let mut seen_webviews = HashSet::new();
@@ -277,8 +254,6 @@ pub fn init<R: Runtime>() -> TauriPlugin<R> {
app.manage(RwLock::new(AdsState {
shown: true,
modal_shown: false,
consent_required: false,
consent_overlay_shown: false,
occluded: false,
last_click: None,
malicious_origins: HashSet::new(),
@@ -294,11 +269,7 @@ pub fn init<R: Runtime>() -> TauriPlugin<R> {
.state::<RwLock<AdsState>>()
.try_read()
.map(|state| {
state.shown
&& !state.modal_shown
&& !state.consent_required
&& !state.consent_overlay_shown
&& !state.occluded
state.shown && !state.modal_shown && !state.occluded
})
.unwrap_or(false);
@@ -361,13 +332,6 @@ pub fn init<R: Runtime>() -> TauriPlugin<R> {
init_ads_window,
hide_ads_window,
show_ads_window,
show_ads_consent_overlay,
show_ads_consent_preferences,
open_ads_consent_preferences,
hide_ads_consent_preferences,
hide_ads_consent_overlay,
get_ads_consent_required,
perform_ads_consent_action,
record_ads_click,
open_link,
get_ads_personalization,
@@ -394,42 +358,6 @@ fn get_webview_position<R: Runtime>(
))
}
fn get_overlay_webview_position_for_window<R: Runtime>(
main_window: &tauri::Window<R>,
) -> crate::api::Result<(PhysicalPosition<f32>, PhysicalSize<f32>)> {
let main_window_size = main_window.outer_size()?;
let title_bar_height =
APP_TITLE_BAR_HEIGHT * main_window.scale_factor()? as f32;
Ok((
PhysicalPosition::new(0.0, title_bar_height),
PhysicalSize::new(
main_window_size.width as f32,
(main_window_size.height as f32 - title_bar_height).max(0.0),
),
))
}
fn get_overlay_webview_position<R: Runtime>(
app: &tauri::AppHandle<R>,
) -> crate::api::Result<(PhysicalPosition<f32>, PhysicalSize<f32>)> {
let main_window = app.get_window("main").unwrap();
get_overlay_webview_position_for_window(&main_window)
}
fn get_device_pixel_ratio<R: Runtime>(
app: &tauri::AppHandle<R>,
dpr: Option<f32>,
) -> f32 {
dpr.unwrap_or_else(|| {
app.get_window("main")
.and_then(|window| window.scale_factor().ok())
.map(|scale_factor| scale_factor as f32)
.unwrap_or(1.0)
})
}
#[tauri::command]
#[cfg(not(target_os = "linux"))]
pub async fn init_ads_window<R: Runtime>(
@@ -446,17 +374,11 @@ pub async fn init_ads_window<R: Runtime>(
state.shown = true;
}
if state.modal_shown && !state.consent_overlay_shown {
if state.modal_shown {
return Ok(());
}
let layout = if state.consent_overlay_shown {
get_overlay_webview_position(&app)
} else {
get_webview_position(&app, dpr)
};
if let Ok((position, size)) = layout {
if let Ok((position, size)) = get_webview_position(&app, dpr) {
let webview = if let Some(webview) = app.webviews().get("ads-window") {
// set both the `hide`/`show` state and `position`,
// to ensure that the webview is actually shown/hidden
@@ -644,10 +566,6 @@ pub async fn init_ads_window<R: Runtime>(
// });
}
if state.shown && state.consent_required {
app.emit_to("main", ADS_CONSENT_REQUIRED_EVENT, true).ok();
}
Ok(())
}
@@ -661,8 +579,6 @@ pub async fn show_ads_window<R: Runtime>(
app: tauri::AppHandle<R>,
dpr: f32,
) -> crate::api::Result<()> {
let mut consent_required = false;
if let Some(webview) = app.webviews().get("ads-window") {
let state = app.state::<RwLock<AdsState>>();
let mut state = state.write().await;
@@ -670,11 +586,7 @@ pub async fn show_ads_window<R: Runtime>(
state.modal_shown = false;
if state.shown {
let (position, size) = if state.consent_overlay_shown {
get_overlay_webview_position(&app)?
} else {
get_webview_position(&app, dpr)?
};
let (position, size) = get_webview_position(&app, dpr)?;
// set both the `hide`/`show` state and `position`,
// to ensure that the webview is actually shown/hidden
webview.set_size(size).ok();
@@ -682,12 +594,6 @@ pub async fn show_ads_window<R: Runtime>(
webview.show().ok();
set_webview_visible_for_window(&app, webview, true);
}
consent_required = state.shown && state.consent_required;
}
if consent_required {
app.emit_to("main", ADS_CONSENT_REQUIRED_EVENT, true).ok();
}
Ok(())
@@ -698,28 +604,14 @@ pub async fn hide_ads_window<R: Runtime>(
app: tauri::AppHandle<R>,
reset: Option<bool>,
) -> crate::api::Result<()> {
let reset = reset.unwrap_or(false);
let state = app.state::<RwLock<AdsState>>();
let mut state = state.write().await;
if reset {
state.shown = false;
state.consent_overlay_shown = false;
}
if let Some(webview) = app.webviews().get("ads-window") {
if !reset {
let state = app.state::<RwLock<AdsState>>();
let mut state = state.write().await;
if reset.unwrap_or(false) {
state.shown = false;
} else {
state.modal_shown = true;
if state.consent_overlay_shown {
let (position, size) = get_overlay_webview_position(&app)?;
webview.set_size(size).ok();
webview.set_position(position).ok();
webview.show().ok();
set_webview_visible_for_window(&app, webview, true);
return Ok(());
}
}
// set both the `hide`/`show` state and `position`,
@@ -730,204 +622,6 @@ pub async fn hide_ads_window<R: Runtime>(
webview.hide().ok();
}
if reset {
app.emit_to("main", ADS_CONSENT_REQUIRED_EVENT, false).ok();
}
Ok(())
}
#[tauri::command]
pub async fn show_ads_consent_overlay<R: Runtime>(
app: tauri::AppHandle<R>,
) -> crate::api::Result<()> {
if let Some(webview) = app.webviews().get("ads-window") {
let state = app.state::<RwLock<AdsState>>();
let mut state = state.write().await;
// dont show for hidden ads so consent events cannot re-enable the webview.
if !state.shown {
return Ok(());
}
state.consent_required = true;
state.consent_overlay_shown = false;
if !state.modal_shown {
let dpr = get_device_pixel_ratio(&app, None);
let (position, size) = get_webview_position(&app, dpr)?;
webview.set_size(size).ok();
webview.set_position(position).ok();
webview.show().ok();
set_webview_visible_for_window(&app, webview, true);
}
}
app.emit_to("main", ADS_CONSENT_REQUIRED_EVENT, true).ok();
Ok(())
}
#[tauri::command]
pub async fn show_ads_consent_preferences<R: Runtime>(
app: tauri::AppHandle<R>,
) -> crate::api::Result<()> {
if let Some(webview) = app.webviews().get("ads-window") {
let state = app.state::<RwLock<AdsState>>();
let mut state = state.write().await;
if !state.consent_required {
return Ok(());
}
state.consent_overlay_shown = true;
let (position, size) = get_overlay_webview_position(&app)?;
webview
.set_bounds(Rect {
position: position.into(),
size: size.into(),
})
.ok();
webview.show().ok();
set_webview_visible_for_window(&app, webview, true);
}
Ok(())
}
#[tauri::command]
pub async fn open_ads_consent_preferences<R: Runtime>(
app: tauri::AppHandle<R>,
) -> crate::api::Result<()> {
let Some(webview) = app.webviews().get("ads-window").cloned() else {
return Ok(());
};
{
let state = app.state::<RwLock<AdsState>>();
let mut state = state.write().await;
state.consent_required = true;
state.consent_overlay_shown = false;
}
webview.eval("window.modrinthAdsReopenConsentPreferences?.()")?;
Ok(())
}
/// Restores the ad inventory bounds without resolving the pending consent request.
#[tauri::command]
pub async fn hide_ads_consent_preferences<R: Runtime>(
app: tauri::AppHandle<R>,
) -> crate::api::Result<()> {
if let Some(webview) = app.webviews().get("ads-window") {
let state = app.state::<RwLock<AdsState>>();
let mut state = state.write().await;
state.consent_overlay_shown = false;
if state.shown && !state.modal_shown {
let dpr = get_device_pixel_ratio(&app, None);
let (position, size) = get_webview_position(&app, dpr)?;
webview
.set_bounds(Rect {
position: position.into(),
size: size.into(),
})
.ok();
webview.show().ok();
set_webview_visible_for_window(&app, webview, true);
} else {
webview
.set_position(PhysicalPosition::new(-1000, -1000))
.ok();
webview.hide().ok();
}
}
app.emit_to("main", ADS_CONSENT_REQUIRED_EVENT, true).ok();
Ok(())
}
#[tauri::command]
pub async fn hide_ads_consent_overlay<R: Runtime>(
app: tauri::AppHandle<R>,
dpr: Option<f32>,
) -> crate::api::Result<()> {
if let Some(webview) = app.webviews().get("ads-window") {
let state = app.state::<RwLock<AdsState>>();
let mut state = state.write().await;
let should_reload_ads = state.consent_required;
state.consent_required = false;
state.consent_overlay_shown = false;
if state.shown && !state.modal_shown {
let dpr = get_device_pixel_ratio(&app, dpr);
let (position, size) = get_webview_position(&app, dpr)?;
webview.set_size(size).ok();
webview.set_position(position).ok();
webview.show().ok();
set_webview_visible_for_window(&app, webview, true);
} else {
webview
.set_position(PhysicalPosition::new(-1000, -1000))
.ok();
webview.hide().ok();
}
drop(state);
if should_reload_ads {
webview.navigate(AD_LINK.parse().unwrap()).ok();
}
}
app.emit_to("main", ADS_CONSENT_REQUIRED_EVENT, false).ok();
Ok(())
}
#[tauri::command]
pub async fn get_ads_consent_required<R: Runtime>(
app: tauri::AppHandle<R>,
) -> crate::api::Result<bool> {
let state = app.state::<RwLock<AdsState>>();
let state = state.read().await;
Ok(state.shown && state.consent_required)
}
#[tauri::command]
pub async fn perform_ads_consent_action<R: Runtime>(
app: tauri::AppHandle<R>,
action: String,
) -> crate::api::Result<()> {
let script = match action.as_str() {
"accept" => "window.modrinthAdsConsentAction?.('accept')",
"reject" => "window.modrinthAdsConsentAction?.('reject')",
"manage" => "window.modrinthAdsConsentAction?.('manage')",
_ => return Ok(()),
};
let state = app.state::<RwLock<AdsState>>();
let should_perform = {
let state = state.read().await;
state.shown && state.consent_required
};
if !should_perform {
return Ok(());
}
if let Some(webview) = app.webviews().get("ads-window") {
webview.eval(script)?;
}
Ok(())
}
-6
View File
@@ -25,7 +25,6 @@ pub fn init<R: tauri::Runtime>() -> tauri::plugin::TauriPlugin<R> {
install_job_retry,
install_job_cancel,
install_job_dismiss,
install_job_support_details,
])
.build()
}
@@ -170,8 +169,3 @@ pub async fn install_job_cancel(job_id: Uuid) -> Result<InstallJobSnapshot> {
pub async fn install_job_dismiss(job_id: Uuid) -> Result<()> {
Ok(theseus::install::dismiss_job(job_id).await?)
}
#[tauri::command]
pub async fn install_job_support_details(job_id: Uuid) -> Result<String> {
Ok(theseus::install::job_support_details(job_id).await?)
}
-6
View File
@@ -41,7 +41,6 @@ pub fn init<R: tauri::Runtime>() -> tauri::plugin::TauriPlugin<R> {
instance_install_project_with_dependencies,
instance_switch_project_version_with_dependencies,
instance_add_project_from_path,
instance_is_file_on_modrinth,
instance_toggle_disable_project,
instance_remove_project,
instance_update_managed_modrinth_version,
@@ -634,11 +633,6 @@ pub async fn instance_add_project_from_path(
.await?)
}
#[tauri::command]
pub async fn instance_is_file_on_modrinth(project_path: &Path) -> Result<bool> {
Ok(theseus::instance::is_file_on_modrinth(project_path).await?)
}
#[tauri::command]
pub async fn instance_toggle_disable_project(
instance_id: &str,
-2
View File
@@ -27,8 +27,6 @@ pub enum ErrorKind {
inner: Box<s3::error::S3Error>,
file: String,
},
#[error("I/O error: {0}")]
Io(#[from] std::io::Error),
#[error("Error acquiring semaphore: {0}")]
Acquire(#[from] tokio::sync::AcquireError),
#[error("Tracing error: {0}")]
+93 -300
View File
@@ -1,19 +1,3 @@
//! Fetches Fabric-compatible loader metadata.
//!
//! Fabric and Quilt both expose loader profiles for a concrete Minecraft
//! version, but Daedalus publishes templated profiles using
//! `${modrinth.gameVersion}`. A group is a set of Minecraft versions whose
//! upstream loader profiles have the same structure after the concrete
//! Minecraft version is replaced with `${modrinth.gameVersion}`. Fabric uses
//! one universal group, so its public profile paths stay as
//! `versions/{loader}.json`. Quilt has more than one group: versions before
//! 26.x include hashed/intermediary libraries, while 26.x versions do not. For
//! Quilt, Daedalus writes one templated profile per group at
//! `version-group/{group}/loader-version/{loader}`.
use crate::metadata_groups::{
UNIVERSAL_METADATA_GROUP, metadata_group_for_game_version, metadata_groups,
};
use crate::util::{download_file, fetch_json, format_url};
use crate::{
Error, FetchResult, MirrorArtifact, UploadFile, insert_mirrored_artifact,
@@ -80,112 +64,7 @@ async fn fetch(
&semaphore,
)
.await?;
let all_loader_versions = fabric_manifest.loader.clone();
let all_game_versions = fabric_manifest.game.clone();
let metadata_groups = metadata_groups(
mod_loader,
all_game_versions.iter().map(|x| x.version.as_str()),
);
if metadata_groups
.iter()
.any(|group| group.id != UNIVERSAL_METADATA_GROUP)
{
let loaders = all_loader_versions
.iter()
.filter(|x| !skip_versions.contains(&&*x.version))
.collect::<Vec<_>>();
let profile_requests = metadata_groups
.iter()
.flat_map(|group| {
loaders.iter().map(move |loader| ProfileRequest {
group: group.id.to_string(),
loader_profile_template_game_version: group
.loader_profile_template_game_version
.clone(),
game_versions: group.game_versions.clone(),
loader_version: loader.version.clone(),
url: format!(
"{}/versions/loader/{}/{}/profile/json",
meta_url,
group.loader_profile_template_game_version,
loader.version
),
})
})
.collect::<Vec<_>>();
fetch_metadata_profiles(
mod_loader,
format_version,
maven_url,
profile_requests,
&upload_files,
&mirror_artifacts,
&semaphore,
)
.await?;
let version_groups = metadata_groups
.iter()
.map(|group| daedalus::modded::VersionGroup {
id: group.id.to_string(),
loaders: loaders
.iter()
.map(|loader| {
let version_path = metadata_version_path(
mod_loader,
format_version,
&loader.version,
group.id,
);
daedalus::modded::LoaderVersion {
id: loader.version.clone(),
url: format_url(&version_path),
stable: loader.stable,
}
})
.collect(),
})
.collect();
let manifest = daedalus::modded::Manifest {
game_versions: all_game_versions
.into_iter()
.map(|game_version| {
let group = metadata_group_for_game_version(
&metadata_groups,
mod_loader,
&game_version.version,
)
.expect("game version should have a metadata group");
daedalus::modded::Version {
id: game_version.version.clone(),
stable: game_version.stable,
version_group: Some(group.id.to_string()),
loaders: Vec::new(),
}
})
.collect(),
version_groups,
};
upload_files.insert(
format!("{mod_loader}/v{format_version}/manifest.json"),
UploadFile {
file: bytes::Bytes::from(serde_json::to_vec(&manifest)?),
content_type: Some("application/json".to_string()),
},
);
return Ok(FetchResult {
upload_files,
mirror_artifacts,
});
}
// We check Modrinth's manifest to find newly added loader versions,
// intermediary/mapping artifacts, and game versions.
let (
@@ -246,6 +125,8 @@ async fn fetch(
)
};
const DUMMY_GAME_VERSION: &str = "1.21";
if !fetch_intermediary_versions.is_empty() {
for x in &fetch_intermediary_versions {
insert_mirrored_artifact(
@@ -259,38 +140,94 @@ async fn fetch(
}
if !fetch_fabric_versions.is_empty() {
let universal_group = metadata_groups
let fabric_version_manifest_urls = fetch_fabric_versions
.iter()
.find(|group| group.id == UNIVERSAL_METADATA_GROUP)
.expect("fabric metadata should have a universal group");
let profile_requests = fetch_fabric_versions
.iter()
.map(|loader| ProfileRequest {
group: universal_group.id.to_string(),
loader_profile_template_game_version: universal_group
.loader_profile_template_game_version
.clone(),
game_versions: universal_group.game_versions.clone(),
loader_version: loader.version.clone(),
url: format!(
.map(|x| {
format!(
"{}/versions/loader/{}/{}/profile/json",
meta_url,
universal_group.loader_profile_template_game_version,
loader.version
),
meta_url, DUMMY_GAME_VERSION, x.version
)
})
.collect::<Vec<_>>();
fetch_metadata_profiles(
mod_loader,
format_version,
maven_url,
profile_requests,
&upload_files,
&mirror_artifacts,
&semaphore,
let fabric_version_manifests = futures::future::try_join_all(
fabric_version_manifest_urls
.iter()
.map(|x| download_file(x, None, &semaphore)),
)
.await?;
.await?
.into_iter()
.map(|x| serde_json::from_slice(&x))
.collect::<Result<Vec<PartialVersionInfo>, serde_json::Error>>()?;
let patched_version_manifests = fabric_version_manifests
.into_iter()
.map(|mut version_info| {
for lib in &mut version_info.libraries {
let new_name = lib
.name
.replace(DUMMY_GAME_VERSION, DUMMY_REPLACE_STRING);
// Hard-code: This library is not present on fabric's maven, so we fetch it from MC libraries
if &*lib.name == "net.minecraft:launchwrapper:1.12" {
lib.url = Some(
"https://libraries.minecraft.net/".to_string(),
);
}
// If a library is not intermediary, we add it to mirror artifacts to be mirrored
if lib.name == new_name {
insert_mirrored_artifact(
&new_name,
None,
vec![
lib.url
.clone()
.unwrap_or_else(|| maven_url.to_string()),
],
false,
&mirror_artifacts,
)?;
} else {
lib.name = new_name;
}
lib.url = Some(format_url("maven/"));
}
version_info.id = version_info
.id
.replace(DUMMY_GAME_VERSION, DUMMY_REPLACE_STRING);
version_info.inherits_from = version_info
.inherits_from
.replace(DUMMY_GAME_VERSION, DUMMY_REPLACE_STRING);
Ok(version_info)
})
.collect::<Result<Vec<_>, Error>>()?;
let serialized_version_manifests = patched_version_manifests
.iter()
.map(|x| serde_json::to_vec(x).map(bytes::Bytes::from))
.collect::<Result<Vec<_>, serde_json::Error>>()?;
serialized_version_manifests
.into_iter()
.enumerate()
.for_each(|(index, bytes)| {
let loader = fetch_fabric_versions[index];
let version_path = format!(
"{mod_loader}/v{format_version}/versions/{}.json",
loader.version
);
upload_files.insert(
version_path,
UploadFile {
file: bytes,
content_type: Some("application/json".to_string()),
},
);
});
}
if !fetch_fabric_versions.is_empty()
@@ -303,20 +240,17 @@ async fn fetch(
let loader_versions = daedalus::modded::Version {
id: DUMMY_REPLACE_STRING.to_string(),
stable: true,
version_group: None,
loaders: all_loader_versions
.iter()
.filter(|x| !skip_versions.contains(&&*x.version))
loaders: fabric_manifest
.loader
.into_iter()
.map(|x| {
let version_path = metadata_version_path(
mod_loader,
format_version,
&x.version,
UNIVERSAL_METADATA_GROUP,
let version_path = format!(
"{mod_loader}/v{format_version}/versions/{}.json",
x.version,
);
daedalus::modded::LoaderVersion {
id: x.version.clone(),
id: x.version,
url: format_url(&version_path),
stable: x.stable,
}
@@ -326,16 +260,14 @@ async fn fetch(
let manifest = daedalus::modded::Manifest {
game_versions: std::iter::once(loader_versions)
.chain(all_game_versions.into_iter().map(|x| {
.chain(fabric_manifest.game.into_iter().map(|x| {
daedalus::modded::Version {
id: x.version,
stable: x.stable,
version_group: None,
loaders: vec![],
}
}))
.collect(),
version_groups: Vec::new(),
};
upload_files.insert(
@@ -353,145 +285,6 @@ async fn fetch(
})
}
struct ProfileRequest {
group: String,
loader_profile_template_game_version: String,
game_versions: Vec<String>,
loader_version: String,
url: String,
}
fn metadata_version_path(
mod_loader: &str,
format_version: usize,
loader_version: &str,
group: &str,
) -> String {
if group == UNIVERSAL_METADATA_GROUP {
format!("{mod_loader}/v{format_version}/versions/{loader_version}.json")
} else {
format!(
"{mod_loader}/v{format_version}/version-group/{group}/loader-version/{loader_version}"
)
}
}
async fn fetch_metadata_profiles(
mod_loader: &str,
format_version: usize,
maven_url: &str,
profile_requests: Vec<ProfileRequest>,
upload_files: &DashMap<String, UploadFile>,
mirror_artifacts: &DashMap<String, MirrorArtifact>,
semaphore: &Arc<Semaphore>,
) -> Result<(), Error> {
let version_manifests = futures::future::try_join_all(
profile_requests
.iter()
.map(|x| download_file(&x.url, None, semaphore)),
)
.await?
.into_iter()
.map(|x| serde_json::from_slice(&x))
.collect::<Result<Vec<PartialVersionInfo>, serde_json::Error>>()?;
let patched_version_manifests = version_manifests
.into_iter()
.zip(profile_requests.iter())
.map(|(mut version_info, request)| {
patch_version_info(
&mut version_info,
&request.loader_profile_template_game_version,
&request.game_versions,
maven_url,
mirror_artifacts,
)?;
Ok(version_info)
})
.collect::<Result<Vec<_>, Error>>()?;
let serialized_version_manifests = patched_version_manifests
.iter()
.map(|x| serde_json::to_vec(x).map(bytes::Bytes::from))
.collect::<Result<Vec<_>, serde_json::Error>>()?;
serialized_version_manifests
.into_iter()
.zip(profile_requests)
.for_each(|(bytes, request)| {
let version_path = metadata_version_path(
mod_loader,
format_version,
&request.loader_version,
&request.group,
);
upload_files.insert(
version_path,
UploadFile {
file: bytes,
content_type: Some("application/json".to_string()),
},
);
});
Ok(())
}
fn patch_version_info(
version_info: &mut PartialVersionInfo,
game_version: &str,
game_versions: &[String],
maven_url: &str,
mirror_artifacts: &DashMap<String, MirrorArtifact>,
) -> Result<(), Error> {
for lib in &mut version_info.libraries {
let new_name = lib.name.replace(game_version, DUMMY_REPLACE_STRING);
// Hard-code: This library is not present on fabric's maven, so we fetch it from MC libraries
if &*lib.name == "net.minecraft:launchwrapper:1.12" {
lib.url = Some("https://libraries.minecraft.net/".to_string());
}
let source_url =
lib.url.clone().unwrap_or_else(|| maven_url.to_string());
if lib.name == new_name {
insert_mirrored_artifact(
&new_name,
None,
vec![source_url],
false,
mirror_artifacts,
)?;
} else {
for concrete_game_version in game_versions {
let concrete_name =
lib.name.replace(game_version, concrete_game_version);
insert_mirrored_artifact(
&concrete_name,
None,
vec![source_url.clone()],
false,
mirror_artifacts,
)?;
}
lib.name = new_name;
}
lib.url = Some(format_url("maven/"));
}
version_info.id =
version_info.id.replace(game_version, DUMMY_REPLACE_STRING);
version_info.inherits_from = version_info
.inherits_from
.replace(game_version, DUMMY_REPLACE_STRING);
Ok(())
}
#[derive(Deserialize, Debug, Clone)]
struct FabricVersions {
pub loader: Vec<FabricLoaderVersion>,
+20 -58
View File
@@ -12,10 +12,7 @@ use itertools::Itertools;
use serde::Deserialize;
use serde::de::DeserializeOwned;
use std::collections::HashMap;
use std::sync::{
Arc,
atomic::{AtomicUsize, Ordering},
};
use std::sync::Arc;
use tokio::sync::Semaphore;
#[tracing::instrument(skip(semaphore))]
@@ -246,45 +243,12 @@ async fn fetch(
};
if !fetch_versions.is_empty() {
let total_installers = fetch_versions.len();
let downloaded_installers = Arc::new(AtomicUsize::new(0));
tracing::info!(
mod_loader,
total_files = total_installers,
"Downloading loader installers"
);
let forge_installers =
futures::future::try_join_all(fetch_versions.iter().map(|x| {
let downloaded_installers = downloaded_installers.clone();
let semaphore = semaphore.clone();
async move {
let installer =
download_file(&x.installer_url, None, &semaphore)
.await?;
let downloaded = downloaded_installers
.fetch_add(1, Ordering::Relaxed)
+ 1;
if downloaded.is_multiple_of(100)
|| downloaded == total_installers
{
tracing::info!(
mod_loader,
downloaded_files = downloaded,
remaining_files =
total_installers.saturating_sub(downloaded),
total_files = total_installers,
"Downloaded loader installers"
);
}
Ok::<_, Error>(installer)
}
}))
.await?;
let forge_installers = futures::future::try_join_all(
fetch_versions
.iter()
.map(|x| download_file(&x.installer_url, None, &semaphore)),
)
.await?;
#[tracing::instrument(skip(raw, upload_files, mirror_artifacts))]
async fn read_forge_installer(
@@ -797,23 +761,21 @@ async fn fetch(
.into_iter()
.map(|(game_version, loaders)| {
daedalus::modded::Version {
id: game_version,
stable: true,
version_group: None,
loaders: loaders
.map(|x| daedalus::modded::LoaderVersion {
url: format_url(&format!(
"{mod_loader}/v{format_version}/versions/{}.json",
x.loader_version
)),
id: x.loader_version,
stable: false,
})
.collect(),
}
id: game_version,
stable: true,
loaders: loaders
.map(|x| daedalus::modded::LoaderVersion {
url: format_url(&format!(
"{mod_loader}/v{format_version}/versions/{}.json",
x.loader_version
)),
id: x.loader_version,
stable: false,
})
.collect(),
}
})
.collect(),
version_groups: Vec::new(),
};
upload_files.insert(
+41 -208
View File
@@ -1,14 +1,10 @@
use crate::util::{
REQWEST_CLIENT, format_url, upload_file_to_bucket,
upload_url_to_bucket_mirrors, write_file_to_local_output,
write_url_to_local_output_mirrors,
upload_url_to_bucket_mirrors,
};
use daedalus::get_path_from_artifact;
use dashmap::{DashMap, DashSet};
use std::sync::{
Arc,
atomic::{AtomicUsize, Ordering},
};
use std::sync::Arc;
use tokio::sync::Semaphore;
use tracing_error::ErrorLayer;
use tracing_subscriber::{EnvFilter, fmt, prelude::*};
@@ -16,7 +12,6 @@ use tracing_subscriber::{EnvFilter, fmt, prelude::*};
mod error;
mod fabric;
mod forge;
mod metadata_groups;
mod minecraft;
pub mod util;
@@ -49,92 +44,51 @@ async fn main() -> Result<()> {
));
let mut fetch_result = FetchResult::default();
let only_loader = dotenvy::var("DAEDALUS_ONLY").ok();
if should_fetch(only_loader.as_deref(), "minecraft") {
match minecraft::fetch(semaphore.clone()).await {
Ok(fetched) => merge_fetch_result(&mut fetch_result, fetched),
Err(err) => tracing::warn!(error = %err, "Minecraft fetch failed"),
}
match minecraft::fetch(semaphore.clone()).await {
Ok(fetched) => merge_fetch_result(&mut fetch_result, fetched),
Err(err) => tracing::warn!(error = %err, "Minecraft fetch failed"),
}
if should_fetch(only_loader.as_deref(), "fabric") {
match fabric::fetch_fabric(semaphore.clone()).await {
Ok(fetched) => merge_fetch_result(&mut fetch_result, fetched),
Err(err) => tracing::warn!(error = %err, "Fabric fetch failed"),
}
match fabric::fetch_fabric(semaphore.clone()).await {
Ok(fetched) => merge_fetch_result(&mut fetch_result, fetched),
Err(err) => tracing::warn!(error = %err, "Fabric fetch failed"),
}
if should_fetch(only_loader.as_deref(), "quilt") {
match fabric::fetch_quilt(semaphore.clone()).await {
Ok(fetched) => merge_fetch_result(&mut fetch_result, fetched),
Err(err) => tracing::warn!(error = %err, "Quilt fetch failed"),
}
match fabric::fetch_quilt(semaphore.clone()).await {
Ok(fetched) => merge_fetch_result(&mut fetch_result, fetched),
Err(err) => tracing::warn!(error = %err, "Quilt fetch failed"),
}
if should_fetch(only_loader.as_deref(), "neo") {
match forge::fetch_neo(semaphore.clone()).await {
Ok(fetched) => merge_fetch_result(&mut fetch_result, fetched),
Err(err) => tracing::warn!(error = %err, "NeoForge fetch failed"),
}
match forge::fetch_neo(semaphore.clone()).await {
Ok(fetched) => merge_fetch_result(&mut fetch_result, fetched),
Err(err) => tracing::warn!(error = %err, "NeoForge fetch failed"),
}
if should_fetch(only_loader.as_deref(), "forge") {
match forge::fetch_forge(semaphore.clone()).await {
Ok(fetched) => merge_fetch_result(&mut fetch_result, fetched),
Err(err) => tracing::warn!(error = %err, "Forge fetch failed"),
}
match forge::fetch_forge(semaphore.clone()).await {
Ok(fetched) => merge_fetch_result(&mut fetch_result, fetched),
Err(err) => tracing::warn!(error = %err, "Forge fetch failed"),
}
let FetchResult {
upload_files,
mirror_artifacts,
} = fetch_result;
let upload_file_total = upload_files.len();
let mirror_file_total = mirror_artifacts.len();
if dotenvy::var("LOCAL_OUTPUT_DIR").is_ok() {
let written_files = Arc::new(AtomicUsize::new(0));
futures::future::try_join_all(upload_files.iter().map(|entry| {
upload_file_to_bucket(
entry.key().clone(),
entry.value().file.clone(),
entry.value().content_type.clone(),
&semaphore,
)
}))
.await?;
tracing::info!(
total_files = upload_file_total,
"Writing local metadata files"
);
futures::future::try_join_all(upload_files.iter().map(|entry| {
let path = entry.key().clone();
let file = entry.value().file.clone();
let written_files = written_files.clone();
async move {
write_file_to_local_output(&path, file).await?;
let written = written_files.fetch_add(1, Ordering::Relaxed) + 1;
if written.is_multiple_of(100) || written == upload_file_total {
tracing::info!(
written_files = written,
remaining_files =
upload_file_total.saturating_sub(written),
total_files = upload_file_total,
"Wrote local metadata files"
);
}
Ok::<_, Error>(())
}
}))
.await?;
let written_mirror_files = Arc::new(AtomicUsize::new(0));
tracing::info!(
total_files = mirror_file_total,
"Writing local mirror files"
);
futures::future::try_join_all(mirror_artifacts.iter().map(|entry| {
let path = format!("maven/{}", entry.key());
let mirrors = entry
futures::future::try_join_all(mirror_artifacts.iter().map(|entry| {
upload_url_to_bucket_mirrors(
format!("maven/{}", entry.key()),
entry
.value()
.mirrors
.iter()
@@ -145,117 +99,12 @@ async fn main() -> Result<()> {
format!("{}{}", mirror.path, entry.key())
}
})
.collect();
let sha1 = entry.value().sha1.clone();
let written_mirror_files = written_mirror_files.clone();
let semaphore = semaphore.clone();
async move {
write_url_to_local_output_mirrors(
path, mirrors, sha1, &semaphore,
)
.await?;
let written =
written_mirror_files.fetch_add(1, Ordering::Relaxed) + 1;
if written.is_multiple_of(100) || written == mirror_file_total {
tracing::info!(
written_files = written,
remaining_files =
mirror_file_total.saturating_sub(written),
total_files = mirror_file_total,
"Wrote local mirror files"
);
}
Ok::<_, Error>(())
}
}))
.await?;
} else {
let uploaded_files = Arc::new(AtomicUsize::new(0));
tracing::info!(
total_files = upload_file_total,
"Uploading metadata files"
);
futures::future::try_join_all(upload_files.iter().map(|entry| {
let path = entry.key().clone();
let file = entry.value().file.clone();
let content_type = entry.value().content_type.clone();
let uploaded_files = uploaded_files.clone();
let semaphore = semaphore.clone();
async move {
upload_file_to_bucket(path, file, content_type, &semaphore)
.await?;
let uploaded =
uploaded_files.fetch_add(1, Ordering::Relaxed) + 1;
if uploaded.is_multiple_of(100) || uploaded == upload_file_total
{
tracing::info!(
uploaded_files = uploaded,
remaining_files =
upload_file_total.saturating_sub(uploaded),
total_files = upload_file_total,
"Uploaded metadata files"
);
}
Ok::<_, Error>(())
}
}))
.await?;
let uploaded_mirror_files = Arc::new(AtomicUsize::new(0));
tracing::info!(
total_files = mirror_file_total,
"Uploading mirror files"
);
futures::future::try_join_all(mirror_artifacts.iter().map(|entry| {
let path = format!("maven/{}", entry.key());
let mirrors = entry
.value()
.mirrors
.iter()
.map(|mirror| {
if mirror.entire_url {
mirror.path.clone()
} else {
format!("{}{}", mirror.path, entry.key())
}
})
.collect();
let sha1 = entry.value().sha1.clone();
let uploaded_mirror_files = uploaded_mirror_files.clone();
let semaphore = semaphore.clone();
async move {
upload_url_to_bucket_mirrors(path, mirrors, sha1, &semaphore)
.await?;
let uploaded =
uploaded_mirror_files.fetch_add(1, Ordering::Relaxed) + 1;
if uploaded.is_multiple_of(100) || uploaded == mirror_file_total
{
tracing::info!(
uploaded_files = uploaded,
remaining_files =
mirror_file_total.saturating_sub(uploaded),
total_files = mirror_file_total,
"Uploaded mirror files"
);
}
Ok::<_, Error>(())
}
}))
.await?;
}
.collect(),
entry.value().sha1.clone(),
&semaphore,
)
}))
.await?;
if dotenvy::var("CLOUDFLARE_INTEGRATION")
.ok()
@@ -302,20 +151,6 @@ async fn main() -> Result<()> {
Ok(())
}
fn should_fetch(only_loader: Option<&str>, loader: &str) -> bool {
let Some(only_loader) = only_loader else {
return true;
};
only_loader.split(',').any(|entry| {
let entry = entry.trim();
entry.eq_ignore_ascii_case("all")
|| entry.eq_ignore_ascii_case(loader)
|| (loader == "neo" && entry.eq_ignore_ascii_case("neoforge"))
})
}
pub struct UploadFile {
file: bytes::Bytes,
content_type: Option<String>,
@@ -413,13 +248,11 @@ fn check_env_vars() -> bool {
failed |= check_var::<String>("BASE_URL");
if dotenvy::var("LOCAL_OUTPUT_DIR").is_err() {
failed |= check_var::<String>("S3_ACCESS_TOKEN");
failed |= check_var::<String>("S3_SECRET");
failed |= check_var::<String>("S3_URL");
failed |= check_var::<String>("S3_REGION");
failed |= check_var::<String>("S3_BUCKET_NAME");
}
failed |= check_var::<String>("S3_ACCESS_TOKEN");
failed |= check_var::<String>("S3_SECRET");
failed |= check_var::<String>("S3_URL");
failed |= check_var::<String>("S3_REGION");
failed |= check_var::<String>("S3_BUCKET_NAME");
if dotenvy::var("CLOUDFLARE_INTEGRATION")
.ok()
-145
View File
@@ -1,145 +0,0 @@
//! Determines how version info is generated for pairs of game and loader
//! versions.
//!
//! When a user installs a version of the game, they install two things: the
//! game (of some specific version), and a loader (of some other specific
//! version). Each combination of game and loader version requires a specific
//! configuration, like a specific set of libraries that must be downloaded and
//! run along with the game. However, some versions of the game or loader may
//! change configuration requirements without the other version being affected.
//! For example, pre-26.x game versions with Quilt require the Quilt `hashed`
//! libraries to also be downloaded. However, 26.x and later don't require the
//! `hashed` libraries, and don't even have a download for them. The problem is
//! that Quilt loader 0.30.0 can be used for both pre-26.x and 26.x - but our v0
//! manifest files can't differentiate the two. The result is that you either
//! break compatibility for 0.30.0 game versions pre-26.x, or break 0.30.0
//! on 26.x and later.
//!
//! To fix this, v1 introduces the concept of *version groups*: game versions
//! before 26.x are version group v1, and 26.x and later are v2. Then, we
//! parameterize our version info on both version group and loader version,
//! letting us specify the right configuration based on both game version and
//! loader version.
//!
//! Why not parameterize on game version and loader version directly? Most game
//! versions have the same configuration as their surrounding game versions, so
//! we'd end up with many duplicate configurations: the number of game versions
//! multiplied by the number of loader versions.
//!
//! This file lets you configure what game versions are grouped together.
//!
//! Each version group is templated from a specific game version - e.g. game
//! version 1.21 is used as the template file for 1.20, 1.19, etc.
pub const UNIVERSAL_METADATA_GROUP: &str = "universal";
pub const QUILT_LEGACY_METADATA_GROUP: &str = "v1";
pub const QUILT_MODERN_METADATA_GROUP: &str = "v2";
pub struct MetadataGroup {
pub id: &'static str,
/// Minecraft version used to fetch and template this group's loader profiles.
pub loader_profile_template_game_version: String,
pub game_versions: Vec<String>,
}
pub fn metadata_groups<'a>(
mod_loader: &str,
game_versions: impl IntoIterator<Item = &'a str>,
) -> Vec<MetadataGroup> {
// Non-Quilt loaders don't need the concept of version groups, so we just
// make one "universal" group, and template it on 1.21.
if mod_loader != "quilt" {
return vec![MetadataGroup {
id: UNIVERSAL_METADATA_GROUP,
loader_profile_template_game_version: "1.21".to_string(),
game_versions: game_versions
.into_iter()
.map(|x| x.to_string())
.collect(),
}];
}
let game_versions = game_versions.into_iter().collect::<Vec<_>>();
let legacy_game_versions = game_versions
.iter()
.copied()
.filter(|game_version| {
metadata_group_id_for_game_version(mod_loader, game_version)
== QUILT_LEGACY_METADATA_GROUP
})
.collect::<Vec<_>>();
let modern_game_versions = game_versions
.iter()
.copied()
.filter(|game_version| {
metadata_group_id_for_game_version(mod_loader, game_version)
== QUILT_MODERN_METADATA_GROUP
})
.collect::<Vec<_>>();
let mut groups = Vec::new();
if !legacy_game_versions.is_empty() {
groups.push(MetadataGroup {
id: QUILT_LEGACY_METADATA_GROUP,
loader_profile_template_game_version: legacy_game_versions
.iter()
.find(|x| **x == "1.21")
.copied()
.unwrap_or(legacy_game_versions[0])
.to_string(),
game_versions: legacy_game_versions
.iter()
.map(|x| x.to_string())
.collect(),
});
}
if !modern_game_versions.is_empty() {
groups.push(MetadataGroup {
id: QUILT_MODERN_METADATA_GROUP,
loader_profile_template_game_version: modern_game_versions[0]
.to_string(),
game_versions: modern_game_versions
.iter()
.map(|x| x.to_string())
.collect(),
});
}
groups
}
pub fn metadata_group_for_game_version<'a>(
groups: &'a [MetadataGroup],
mod_loader: &str,
game_version: &str,
) -> Option<&'a MetadataGroup> {
let group_id = metadata_group_id_for_game_version(mod_loader, game_version);
groups.iter().find(|group| group.id == group_id)
}
fn metadata_group_id_for_game_version(
mod_loader: &str,
game_version: &str,
) -> &'static str {
if mod_loader == "quilt" && is_modern_quilt_game_version(game_version) {
QUILT_MODERN_METADATA_GROUP
} else if mod_loader == "quilt" {
QUILT_LEGACY_METADATA_GROUP
} else {
UNIVERSAL_METADATA_GROUP
}
}
// Update these Quilt group boundaries if upstream loader profiles gain another
// structural incompatibility between Minecraft versions.
fn is_modern_quilt_game_version(game_version: &str) -> bool {
let major = game_version
.split(['.', 'w'])
.next()
.and_then(|x| x.parse::<usize>().ok());
major.is_some_and(|x| x >= 26)
}
+1 -85
View File
@@ -3,11 +3,7 @@ use bytes::Bytes;
use s3::creds::Credentials;
use s3::{Bucket, Region};
use serde::de::DeserializeOwned;
use std::path::PathBuf;
use std::sync::{
Arc, LazyLock,
atomic::{AtomicUsize, Ordering},
};
use std::sync::{Arc, LazyLock};
use tokio::sync::Semaphore;
static BUCKET: LazyLock<Bucket> = LazyLock::new(|| {
@@ -59,8 +55,6 @@ pub static REQWEST_CLIENT: LazyLock<reqwest::Client> = LazyLock::new(|| {
.unwrap()
});
static DOWNLOADED_FILE_COUNT: AtomicUsize = AtomicUsize::new(0);
#[tracing::instrument(skip(bytes, semaphore))]
pub async fn upload_file_to_bucket(
path: String,
@@ -141,74 +135,6 @@ pub async fn upload_url_to_bucket(
Ok(())
}
pub async fn write_file_to_local_output(
path: &str,
bytes: Bytes,
) -> Result<(), Error> {
let output_path = local_output_path(path)?;
if let Some(parent) = output_path.parent() {
tokio::fs::create_dir_all(parent).await?;
}
tokio::fs::write(output_path, bytes).await?;
Ok(())
}
pub async fn write_url_to_local_output_mirrors(
output_path: String,
mirrors: Vec<String>,
sha1: Option<String>,
semaphore: &Arc<Semaphore>,
) -> Result<(), Error> {
if mirrors.is_empty() {
return Err(ErrorKind::InvalidInput(
"No mirrors provided!".to_string(),
)
.into());
}
for (index, mirror) in mirrors.iter().enumerate() {
let result = write_url_to_local_output(
output_path.clone(),
mirror.clone(),
sha1.clone(),
semaphore,
)
.await;
if result.is_ok() || (result.is_err() && index == (mirrors.len() - 1)) {
return result;
}
}
unreachable!()
}
async fn write_url_to_local_output(
path: String,
url: String,
sha1: Option<String>,
semaphore: &Arc<Semaphore>,
) -> Result<(), Error> {
let data = download_file(&url, sha1.as_deref(), semaphore).await?;
write_file_to_local_output(&path, data).await?;
Ok(())
}
fn local_output_path(path: &str) -> Result<PathBuf, Error> {
let output_dir = dotenvy::var("LOCAL_OUTPUT_DIR").map_err(|_| {
ErrorKind::InvalidInput(
"LOCAL_OUTPUT_DIR is required for local output".to_string(),
)
})?;
Ok(PathBuf::from(output_dir).join(path))
}
#[tracing::instrument(skip(bytes))]
pub async fn sha1_async(bytes: Bytes) -> Result<String, Error> {
let hash = tokio::task::spawn_blocking(move || {
@@ -256,16 +182,6 @@ pub async fn download_file(
}
}
let downloaded = DOWNLOADED_FILE_COUNT
.fetch_add(1, Ordering::Relaxed)
+ 1;
if downloaded.is_multiple_of(100) {
tracing::info!(
downloaded_files = downloaded,
"Downloaded metadata files"
);
}
return Ok(bytes);
} else if attempt <= RETRIES {
continue;
+4 -14
View File
@@ -414,7 +414,7 @@ components:
- unknown
- signature
example: required-resource-pack
# https://github.com/modrinth/code/blob/main/apps/labrinth/src/routes/v2/version_creation.rs#L32-L76
# https://github.com/modrinth/labrinth/blob/master/src/routes/version_creation.rs#L27-L57
CreatableVersion:
allOf:
- $ref: '#/components/schemas/BaseVersion'
@@ -446,11 +446,6 @@ components:
- client_or_server_prefers_both
- unknown
description: The environment that this version is for.
file_types:
type: object
additionalProperties:
$ref: '#/components/schemas/FileTypeEnum'
description: A map of file parts to their associated file type, a file type is used for additional files such as sources jars.
required:
- file_parts
- project_id
@@ -692,12 +687,6 @@ components:
type: string
description: The ID of the project
example: AABBCCDD
all_project_types:
type: array
items:
type: string
description: All project types across every version of the project, unlike `project_type` which only reflects a version-specific type
example: [mod, plugin, datapack]
author:
type: string
description: The username of the project's author
@@ -754,7 +743,6 @@ components:
- client_side
- server_side
- project_id
- all_project_types
- author
- versions
- follows
@@ -1955,7 +1943,6 @@ paths:
These are the most commonly used facet types:
- `project_type`
- `all_project_types` (matches against every project type across all of the project's versions, not just the primary/version-specific type)
- `categories` (loaders are lumped in with categories in search)
- `versions`
- `client_side`
@@ -1969,8 +1956,11 @@ paths:
- `project_id`
- `license`
- `downloads`
- `color`
- `created_timestamp` (uses Unix timestamp)
- `modified_timestamp` (uses Unix timestamp)
- `date_created` (uses ISO-8601 timestamp)
- `date_modified` (uses ISO-8601 timestamp)
In order to then use these facets, you need a value to filter by, as well as an operation to perform on this value.
The most common operation is `:` (same as `=`), though you can also use `!=`, `>=`, `>`, `<=`, and `<`.
@@ -97,7 +97,7 @@ If you're prepared to contribute by submitting a pull request, ensure you have m
- `cargo fmt --all` has been run.
- `cargo clippy --all-targets` has been run.
- `cargo sqlx prepare -- --tests` has been run.
- `cargo sqlx prepare` has been run.
> Note: If you encounter issues with `sqlx` saying 'no queries found' after running `cargo sqlx prepare`, you may need to ensure the installed version of `sqlx-cli` matches the current version of `sqlx` used [in labrinth](https://github.com/modrinth/labrinth/blob/master/Cargo.toml).
+5 -1
View File
@@ -364,7 +364,11 @@ function getFeatureFlagOverrides() {
function getDomain() {
if (process.env.NODE_ENV === 'production') {
if (process.env.HEROKU_APP_NAME) {
// @ts-ignore
if (process.env.CF_PAGES_URL || globalThis.CF_PAGES_URL) {
// @ts-ignore
return process.env.CF_PAGES_URL ?? globalThis.CF_PAGES_URL
} else if (process.env.HEROKU_APP_NAME) {
return `https://${process.env.HEROKU_APP_NAME}.herokuapp.com`
} else if (process.env.VERCEL_URL) {
return `https://${process.env.VERCEL_URL}`
-2
View File
@@ -5,7 +5,6 @@
<LoadingBar />
</ClientOnly>
<NotificationPanel />
<AdsConsentNotification />
<I18nDebugPanel />
<NuxtPage />
</NuxtLayout>
@@ -13,7 +12,6 @@
<script setup lang="ts">
import { I18nDebugPanel, LoadingBar, NotificationPanel } from '@modrinth/ui'
import AdsConsentNotification from '~/components/ui/AdsConsentNotification.vue'
import { setupProviders } from '~/providers/setup.ts'
import { useAuth } from './composables/auth'

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