mirror of
https://github.com/modrinth/code.git
synced 2026-08-02 14:15:53 +00:00
Compare commits
50
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5d1ddeda24 | ||
|
|
a0228dc2b1 | ||
|
|
07f9e3aedc | ||
|
|
a79b8e0777 | ||
|
|
671f6d264a | ||
|
|
e231df1f97 | ||
|
|
3052a14d95 | ||
|
|
e8665f43ca | ||
|
|
cba4550be4 | ||
|
|
384556a810 | ||
|
|
a082e8597c | ||
|
|
7048a35e9f | ||
|
|
c166ce52b3 | ||
|
|
9c99518497 | ||
|
|
758ed818c8 | ||
|
|
83e45d7a5c | ||
|
|
77b30b27fe | ||
|
|
3d7aea5a45 | ||
|
|
fd5d2797b3 | ||
|
|
ec85d9de1c | ||
|
|
22415a4cc6 | ||
|
|
3c6fadf5e8 | ||
|
|
871672d8bf | ||
|
|
e8dc3c3150 | ||
|
|
56dae8f104 | ||
|
|
8af43e59b9 | ||
|
|
ae9ca4db18 | ||
|
|
4eeb53c429 | ||
|
|
c69f24f94d | ||
|
|
de07bcff7d | ||
|
|
beff44767e | ||
|
|
5875e4332f | ||
|
|
f9c078d29d | ||
|
|
418e4f281d | ||
|
|
99ac6b87b1 | ||
|
|
159c6205ef | ||
|
|
bb4862daa6 | ||
|
|
6ed56a4756 | ||
|
|
93b21bb107 | ||
|
|
abc6cf59d8 | ||
|
|
849fe4e1ba | ||
|
|
b442fa4cca | ||
|
|
118046d690 | ||
|
|
b6bca2aaeb | ||
|
|
dcab665455 | ||
|
|
2f311643a0 | ||
|
|
e13a89dd72 | ||
|
|
565ac2cb53 | ||
|
|
7d6f77bebf | ||
|
|
b53887997c |
@@ -0,0 +1,171 @@
|
||||
# MIT License
|
||||
#
|
||||
# Copyright (c) 2024 CARIAD SE
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in all
|
||||
# copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
|
||||
name: 'Merge Queue CI Check Skipper'
|
||||
description: 'Outputs `skip-check` as `true` if this is running as part of merge queue checks and the same checks have already been executed in the PR itself.'
|
||||
inputs:
|
||||
secret:
|
||||
description: 'Optional GitHub Secret that can access branch protection rules using the administration:read permission'
|
||||
required: false
|
||||
outputs:
|
||||
skip-check:
|
||||
description: 'Skip Check (boolean)'
|
||||
value: ${{ steps.passed-checks.outputs.can-skip-checks }}
|
||||
runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Extract PR Number and Commit ID
|
||||
id: extract-pr-info
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
const githubRef = process.env.GITHUB_REF;
|
||||
const regex = /^refs\/heads\/gh-readonly-queue\/([a-zA-Z0-9.\-_\/]+)\/pr-(\d+)-([a-f0-9]+)$/;
|
||||
|
||||
if (regex.test(githubRef)) {
|
||||
const [, targetBranchName, prNumber, commitId] = githubRef.match(regex);
|
||||
core.setOutput('targetBranchName', targetBranchName);
|
||||
core.setOutput('prNumber', prNumber);
|
||||
core.setOutput('commitId', commitId);
|
||||
} else {
|
||||
console.log(`GITHUB_REF is not a merge queue ref, setting CAN_SKIP_CHECKS to false: ${githubRef}`);
|
||||
core.exportVariable('CAN_SKIP_CHECKS', 'false');
|
||||
}
|
||||
|
||||
- name: Print PR Number and Target Commit ID
|
||||
if: env.CAN_SKIP_CHECKS != 'false'
|
||||
shell: bash
|
||||
run: |
|
||||
echo "Target Branch Name: ${{ steps.extract-pr-info.outputs.targetBranchName }}"
|
||||
echo "PR Number: ${{ steps.extract-pr-info.outputs.prNumber }}"
|
||||
echo "Target Commit ID: ${{ steps.extract-pr-info.outputs.commitId }}"
|
||||
|
||||
- name: Check if merge queue entry was enqueued as head of the queue
|
||||
if: env.CAN_SKIP_CHECKS != 'false'
|
||||
shell: bash
|
||||
run: |
|
||||
targetBranchHead=$(git rev-parse origin/${{ steps.extract-pr-info.outputs.targetBranchName }})
|
||||
if [[ "$targetBranchHead" != "${{ steps.extract-pr-info.outputs.commitId }}" ]]; then
|
||||
echo "'${{ steps.extract-pr-info.outputs.targetBranchName }}' branch commit ID does not match PR commit ID. This Merge Queue run was not head of the queue when it was enqueued. Setting CAN_SKIP_CHECKS to false."
|
||||
echo "CAN_SKIP_CHECKS=false" >> "$GITHUB_ENV"
|
||||
else
|
||||
echo "This merge queue entry is targeting '${{ steps.extract-pr-info.outputs.targetBranchName }}' directly."
|
||||
fi
|
||||
|
||||
- name: Get PR Branch
|
||||
id: get-pr-branch
|
||||
if: env.CAN_SKIP_CHECKS != 'false'
|
||||
shell: bash
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
prNumber=${{ steps.extract-pr-info.outputs.prNumber }}
|
||||
branchName=$(gh pr view ${prNumber} --json headRefName -q '.headRefName')
|
||||
echo "prBranch=$branchName" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Print PR Branch
|
||||
if: env.CAN_SKIP_CHECKS != 'false'
|
||||
shell: bash
|
||||
run: |
|
||||
echo "PR Branch: ${{ steps.get-pr-branch.outputs.prBranch }}"
|
||||
|
||||
- name: Check if PR branch contains the Merge Queue target commit ID
|
||||
if: env.CAN_SKIP_CHECKS != 'false'
|
||||
shell: bash
|
||||
run: |
|
||||
# Get the branch name from previous steps
|
||||
branch_name="origin/${{ steps.get-pr-branch.outputs.prBranch }}"
|
||||
commit_id="${{ steps.extract-pr-info.outputs.commitId }}"
|
||||
|
||||
# Check if the branch history contains the commit
|
||||
if git branch -r --contains "$commit_id" | grep -q "$branch_name"; then
|
||||
echo "Branch '$branch_name' contains commit '$commit_id'. It is up to date with ${{ steps.extract-pr-info.outputs.targetBranchName }}."
|
||||
else
|
||||
echo "Branch '$branch_name' does not contain commit '$commit_id'. It is outdated. Setting CAN_SKIP_CHECKS to false."
|
||||
echo "CAN_SKIP_CHECKS=false" >> "$GITHUB_ENV"
|
||||
fi
|
||||
|
||||
- name: Compare PR Branch with Current Branch
|
||||
if: env.CAN_SKIP_CHECKS != 'false'
|
||||
shell: bash
|
||||
run: |
|
||||
if git diff --quiet "origin/${{ steps.get-pr-branch.outputs.prBranch }}"; then
|
||||
echo "No differences found. PR branch is identical with this merge queue branch."
|
||||
else
|
||||
echo "Differences detected. PR branch has been updated after PR was added to merge queue. Setting CAN_SKIP_CHECKS to false."
|
||||
echo "CAN_SKIP_CHECKS=false" >> "$GITHUB_ENV"
|
||||
fi
|
||||
|
||||
- name: Compute/publish skip result
|
||||
id: passed-checks
|
||||
uses: actions/github-script@v7
|
||||
env:
|
||||
SECRET: ${{ inputs.secret }}
|
||||
with:
|
||||
github-token: ${{ inputs.secret != '' && inputs.secret || github.token }}
|
||||
script: |
|
||||
if (process.env.CAN_SKIP_CHECKS == "false") {
|
||||
console.log("Setting CAN_SKIP_CHECKS to false");
|
||||
core.setOutput("can-skip-checks", false);
|
||||
return;
|
||||
}
|
||||
|
||||
const secretProvided = !!process.env.SECRET;
|
||||
if (!secretProvided) {
|
||||
console.log("secret input not set, assuming all checks have passed. Ensure 'Require status checks to pass before merging' is enabled, or provide a secret with administration:read.");
|
||||
core.setOutput("can-skip-checks", true);
|
||||
return;
|
||||
}
|
||||
|
||||
const { data: branchProtection } = await github.rest.repos.getBranchProtection({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
branch: "${{ steps.extract-pr-info.outputs.targetBranchName }}",
|
||||
});
|
||||
const requiredCheckNames = branchProtection.required_status_checks.contexts;
|
||||
console.log(`requiredCheckNames = ${requiredCheckNames}`);
|
||||
|
||||
const { data: checks } = await github.rest.checks.listForRef({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
ref: "refs/heads/${{ steps.get-pr-branch.outputs.prBranch }}",
|
||||
});
|
||||
|
||||
console.log(`checks.check_runs = ${checks.check_runs.map(check => `${check.status},${check.conclusion},${check.name};`)}`);
|
||||
const nonSuccessfulChecks = checks.check_runs.filter(check => check.status !== "completed" || check.conclusion !== "success");
|
||||
const nonSuccessfulCheckNames = nonSuccessfulChecks.map(check => check.name);
|
||||
console.log(`nonSuccessfulCheckNames = ${nonSuccessfulCheckNames}`);
|
||||
|
||||
const missingChecks = requiredCheckNames.filter(checkName => nonSuccessfulCheckNames.includes(checkName));
|
||||
|
||||
if (missingChecks.length > 0) {
|
||||
console.log(`Required checks not passed, cannot skip merge queue checks. Setting CAN_SKIP_CHECKS to false. Missing checks: ${missingChecks.join(', ')}`);
|
||||
core.setOutput("can-skip-checks", false);
|
||||
} else {
|
||||
console.log("No missing checks. Setting CAN_SKIP_CHECKS to true.");
|
||||
core.setOutput("can-skip-checks", true);
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
name: API client release
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
paths:
|
||||
- .github/workflows/api-client-release.yml
|
||||
- packages/api-client/**
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: false
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
id-token: write
|
||||
|
||||
jobs:
|
||||
release:
|
||||
if: github.repository_owner == 'modrinth' && github.ref == 'refs/heads/main'
|
||||
# npm Trusted Publishing requires a GitHub-hosted runner.
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
FORCE_COLOR: 3
|
||||
PACKAGE_DIR: packages/api-client
|
||||
PACKAGE_NAME: '@modrinth/api-client'
|
||||
BUMP_TYPE: minor
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Check for api-client changes
|
||||
id: changes
|
||||
run: |
|
||||
if [ "${{ github.event.before }}" = "0000000000000000000000000000000000000000" ]; then
|
||||
echo "changed=true" >> "$GITHUB_OUTPUT"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if git diff --quiet "${{ github.event.before }}" "$GITHUB_SHA" -- "$PACKAGE_DIR"; then
|
||||
echo "changed=false" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "changed=true" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
- name: Setup Node
|
||||
if: steps.changes.outputs.changed == 'true'
|
||||
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
||||
with:
|
||||
node-version-file: .nvmrc
|
||||
registry-url: https://registry.npmjs.org
|
||||
|
||||
- name: Enable Corepack
|
||||
if: steps.changes.outputs.changed == 'true'
|
||||
run: corepack enable
|
||||
|
||||
- name: Get pnpm store path
|
||||
if: steps.changes.outputs.changed == 'true'
|
||||
id: pnpm-store
|
||||
run: echo "store-path=$(pnpm store path --silent)" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Restore pnpm cache
|
||||
if: steps.changes.outputs.changed == 'true'
|
||||
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
|
||||
with:
|
||||
path: ${{ steps.pnpm-store.outputs.store-path }}
|
||||
key: pnpm-cache-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||
restore-keys: |
|
||||
pnpm-cache-
|
||||
|
||||
- name: Install dependencies
|
||||
if: steps.changes.outputs.changed == 'true'
|
||||
run: pnpm install --frozen-lockfile --filter @modrinth/api-client...
|
||||
|
||||
- name: Resolve release version
|
||||
if: steps.changes.outputs.changed == 'true'
|
||||
id: version
|
||||
run: |
|
||||
CURRENT_VERSION_JSON="$(npm view "${PACKAGE_NAME}" version --json)"
|
||||
CURRENT_VERSION="$(
|
||||
jq -nr \
|
||||
--argjson version "$CURRENT_VERSION_JSON" \
|
||||
'if ($version | type) == "array" then $version[-1] else $version end'
|
||||
)"
|
||||
|
||||
NEXT_VERSION="$(
|
||||
jq -nr \
|
||||
--arg version "$CURRENT_VERSION" \
|
||||
--arg bump "$BUMP_TYPE" '
|
||||
def semver:
|
||||
capture("^(?<major>[0-9]+)\\.(?<minor>[0-9]+)\\.(?<patch>[0-9]+)$")
|
||||
| with_entries(.value |= tonumber);
|
||||
|
||||
($version | semver) as $current
|
||||
| if $bump == "major" then "\($current.major + 1).0.0"
|
||||
elif $bump == "minor" then "\($current.major).\($current.minor + 1).0"
|
||||
elif $bump == "patch" then "\($current.major).\($current.minor).\($current.patch + 1)"
|
||||
else error("Unsupported bump type: \($bump)")
|
||||
end
|
||||
'
|
||||
)"
|
||||
|
||||
PACKAGE_JSON="$(mktemp)"
|
||||
jq --tab --arg version "$NEXT_VERSION" '.version = $version' "$PACKAGE_DIR/package.json" > "$PACKAGE_JSON"
|
||||
mv "$PACKAGE_JSON" "$PACKAGE_DIR/package.json"
|
||||
|
||||
echo "current_version=$CURRENT_VERSION" >> "$GITHUB_OUTPUT"
|
||||
echo "published_version=$CURRENT_VERSION" >> "$GITHUB_OUTPUT"
|
||||
echo "version=$NEXT_VERSION" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Build api-client
|
||||
if: steps.changes.outputs.changed == 'true'
|
||||
run: pnpm --filter @modrinth/api-client build
|
||||
|
||||
- name: Check package contents
|
||||
if: steps.changes.outputs.changed == 'true'
|
||||
working-directory: packages/api-client
|
||||
run: pnpm pack --dry-run
|
||||
|
||||
- name: Publish api-client
|
||||
if: steps.changes.outputs.changed == 'true'
|
||||
working-directory: packages/api-client
|
||||
run: pnpm publish --access public --provenance --no-git-checks
|
||||
@@ -0,0 +1,22 @@
|
||||
name: Cancel PR Workflows on Merge
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
types:
|
||||
- closed
|
||||
|
||||
permissions:
|
||||
actions: write
|
||||
|
||||
jobs:
|
||||
cancel:
|
||||
if: github.event.pull_request.merged == true
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Cancel Previous Runs
|
||||
uses: styfle/cancel-workflow-action@85880fa0301c86cca9da44039ee3bb12d3bedbfa # 0.12.1
|
||||
with:
|
||||
workflow_id: all
|
||||
access_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
ignore_sha: true
|
||||
pr_number: ${{ github.event.pull_request.number }}
|
||||
@@ -2,7 +2,7 @@ on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- main
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
|
||||
@@ -2,7 +2,7 @@ on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- main
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
|
||||
@@ -3,7 +3,7 @@ name: daedalus-docker-build
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- '**'
|
||||
- 'main'
|
||||
paths:
|
||||
- .github/workflows/daedalus-docker.yml
|
||||
- 'apps/daedalus_client/**'
|
||||
@@ -26,19 +26,66 @@ concurrency:
|
||||
cancel-in-progress: ${{ github.ref != 'refs/heads/main' && github.ref != 'refs/heads/prod' }}
|
||||
|
||||
jobs:
|
||||
skip-if-clean:
|
||||
name: Skip if merge_queue produces no diff
|
||||
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
|
||||
with:
|
||||
secret: ${{ secrets.GH_ACCESS_TOKEN }}
|
||||
|
||||
- name: Check merge_group synthetic commit
|
||||
id: check
|
||||
run: |
|
||||
# PR mode: never skip
|
||||
if [ "${{ github.event_name }}" != "merge_group" ]; then
|
||||
echo "skip=false" >> $GITHUB_OUTPUT
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ "${{ steps.merge-queue-ci-skipper.outputs.skip-check }}" = "true" ]; then
|
||||
echo "skip=true" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "skip=false" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
docker:
|
||||
runs-on: blacksmith-4vcpu-ubuntu-2404
|
||||
runs-on: ${{ needs.skip-if-clean.outputs.internal == 'true' && 'blacksmith-4vcpu-ubuntu-2404' || 'ubuntu-latest' }}
|
||||
env:
|
||||
SCCACHE_DIR: '/mnt/sccache'
|
||||
SCCACHE_CACHE_SIZE: '10G'
|
||||
SCCACHE_MULTILEVEL_CHAIN: 'disk,s3'
|
||||
SCCACHE_S3_KEY_PREFIX: '${{ github.repository }}/'
|
||||
SCCACHE_DIR: ${{ needs.skip-if-clean.outputs.internal == 'true' && '/mnt/sccache' || '' }}
|
||||
SCCACHE_CACHE_SIZE: ${{ needs.skip-if-clean.outputs.internal == 'true' && '10G' || '' }}
|
||||
SCCACHE_MULTILEVEL_CHAIN: ${{ needs.skip-if-clean.outputs.internal == 'true' && 'disk,s3' || '' }}
|
||||
SCCACHE_S3_KEY_PREFIX: ${{ needs.skip-if-clean.outputs.internal == 'true' && format('{0}/', 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'
|
||||
RUSTC_WRAPPER: ${{ needs.skip-if-clean.outputs.internal == 'true' && 'sccache' || '' }}
|
||||
needs: [skip-if-clean]
|
||||
if: ${{ needs.skip-if-clean.outputs.skip != 'true' }}
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
@@ -59,12 +106,14 @@ jobs:
|
||||
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 }}-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
|
||||
|
||||
@@ -3,7 +3,7 @@ name: docker-build
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- '**'
|
||||
- 'main'
|
||||
paths:
|
||||
- .github/workflows/labrinth-docker.yml
|
||||
- 'apps/labrinth/**'
|
||||
@@ -24,21 +24,68 @@ concurrency:
|
||||
cancel-in-progress: ${{ github.ref != 'refs/heads/main' && github.ref != 'refs/heads/prod' }}
|
||||
|
||||
jobs:
|
||||
skip-if-clean:
|
||||
name: Skip if merge_queue produces no diff
|
||||
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
|
||||
with:
|
||||
secret: ${{ secrets.GH_ACCESS_TOKEN }}
|
||||
|
||||
- name: Check merge_group synthetic commit
|
||||
id: check
|
||||
run: |
|
||||
# PR mode: never skip
|
||||
if [ "${{ github.event_name }}" != "merge_group" ]; then
|
||||
echo "skip=false" >> $GITHUB_OUTPUT
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ "${{ steps.merge-queue-ci-skipper.outputs.skip-check }}" = "true" ]; then
|
||||
echo "skip=true" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "skip=false" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
docker:
|
||||
runs-on: blacksmith-4vcpu-ubuntu-2404
|
||||
runs-on: ${{ needs.skip-if-clean.outputs.internal == 'true' && 'blacksmith-4vcpu-ubuntu-2404' || 'ubuntu-latest' }}
|
||||
needs: [skip-if-clean]
|
||||
if: ${{ needs.skip-if-clean.outputs.skip != 'true' }}
|
||||
env:
|
||||
SQLX_OFFLINE: 'true'
|
||||
GIT_HASH: ${{ github.sha }}
|
||||
SCCACHE_DIR: '/mnt/sccache'
|
||||
SCCACHE_CACHE_SIZE: '10G'
|
||||
SCCACHE_MULTILEVEL_CHAIN: 'disk,s3'
|
||||
SCCACHE_S3_KEY_PREFIX: '${{ github.repository }}/'
|
||||
SCCACHE_DIR: ${{ needs.skip-if-clean.outputs.internal == 'true' && '/mnt/sccache' || '' }}
|
||||
SCCACHE_CACHE_SIZE: ${{ needs.skip-if-clean.outputs.internal == 'true' && '10G' || '' }}
|
||||
SCCACHE_MULTILEVEL_CHAIN: ${{ needs.skip-if-clean.outputs.internal == 'true' && 'disk,s3' || '' }}
|
||||
SCCACHE_S3_KEY_PREFIX: ${{ needs.skip-if-clean.outputs.internal == 'true' && format('{0}/', 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'
|
||||
RUSTC_WRAPPER: ${{ needs.skip-if-clean.outputs.internal == 'true' && 'sccache' || '' }}
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
@@ -62,12 +109,14 @@ jobs:
|
||||
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 }}-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
|
||||
|
||||
@@ -13,6 +13,7 @@ concurrency:
|
||||
|
||||
jobs:
|
||||
prepare:
|
||||
if: github.repository_owner == 'modrinth'
|
||||
runs-on: blacksmith-2vcpu-ubuntu-2404
|
||||
steps:
|
||||
- name: Checkout
|
||||
|
||||
@@ -156,6 +156,7 @@ jobs:
|
||||
run: ${{ (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v')) && 'pnpm --filter=@modrinth/app run tauri build --target universal-apple-darwin --config tauri-release.conf.json' || 'pnpm --filter=@modrinth/app run tauri build --target universal-apple-darwin --config tauri-dev.conf.json' }}
|
||||
if: contains(matrix.platform, 'macos')
|
||||
env:
|
||||
TAURI_BUNDLER_DMG_IGNORE_CI: 'true'
|
||||
ENABLE_CODE_SIGNING: ${{ secrets.APPLE_CERTIFICATE }}
|
||||
APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
|
||||
APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
|
||||
|
||||
@@ -13,9 +13,58 @@ concurrency:
|
||||
cancel-in-progress: ${{ github.ref != 'refs/heads/main' && github.ref != 'refs/heads/prod' }}
|
||||
|
||||
jobs:
|
||||
skip-if-clean:
|
||||
name: Skip if merge_queue produces no diff
|
||||
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
|
||||
with:
|
||||
secret: ${{ secrets.GH_ACCESS_TOKEN }}
|
||||
|
||||
- name: Check merge_group synthetic commit
|
||||
id: check
|
||||
env:
|
||||
EVENT_NAME: ${{ github.event_name }}
|
||||
SKIP_CHECK: ${{ steps.merge-queue-ci-skipper.outputs.skip-check }}
|
||||
run: |
|
||||
if [ "$EVENT_NAME" != "merge_group" ]; then
|
||||
echo "skip=false" >> $GITHUB_OUTPUT
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ "$SKIP_CHECK" = "true" ]; then
|
||||
echo "skip=true" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "skip=false" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
build:
|
||||
name: Lint and Test
|
||||
runs-on: blacksmith-4vcpu-ubuntu-2404
|
||||
runs-on: ${{ needs.skip-if-clean.outputs.internal == 'true' && 'blacksmith-4vcpu-ubuntu-2404' || 'ubuntu-latest' }}
|
||||
needs: [skip-if-clean]
|
||||
if: ${{ needs.skip-if-clean.outputs.skip != 'true' }}
|
||||
|
||||
env:
|
||||
# Ensure pnpm output is colored in GitHub Actions logs
|
||||
@@ -27,17 +76,19 @@ jobs:
|
||||
# since we don't want warnings to become errors
|
||||
# while developing)
|
||||
RUSTFLAGS: -Dwarnings
|
||||
# sccache config
|
||||
SCCACHE_DIR: '/mnt/sccache'
|
||||
SCCACHE_CACHE_SIZE: '10G'
|
||||
SCCACHE_MULTILEVEL_CHAIN: 'disk,s3'
|
||||
SCCACHE_S3_KEY_PREFIX: '${{ github.repository }}/'
|
||||
# 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' || '' }}
|
||||
SCCACHE_MULTILEVEL_CHAIN: ${{ needs.skip-if-clean.outputs.internal == 'true' && 'disk,s3' || '' }}
|
||||
SCCACHE_S3_KEY_PREFIX: ${{ needs.skip-if-clean.outputs.internal == 'true' && format('{0}/', 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'
|
||||
RUSTC_WRAPPER: ${{ needs.skip-if-clean.outputs.internal == 'true' && 'sccache' || '' }}
|
||||
|
||||
steps:
|
||||
- name: Check out code
|
||||
@@ -88,12 +139,14 @@ jobs:
|
||||
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
|
||||
|
||||
@@ -51,9 +51,10 @@ import {
|
||||
providePageContext,
|
||||
providePopupNotificationManager,
|
||||
useDebugLogger,
|
||||
useFormatBytes,
|
||||
useVIntl,
|
||||
} from '@modrinth/ui'
|
||||
import { formatBytes, renderString } from '@modrinth/utils'
|
||||
import { renderString } from '@modrinth/utils'
|
||||
import { useQuery, useQueryClient } from '@tanstack/vue-query'
|
||||
import { getVersion } from '@tauri-apps/api/app'
|
||||
import { invoke } from '@tauri-apps/api/core'
|
||||
@@ -146,8 +147,9 @@ const popupNotificationManager = new AppPopupNotificationManager()
|
||||
providePopupNotificationManager(popupNotificationManager)
|
||||
const { addPopupNotification } = popupNotificationManager
|
||||
|
||||
const appVersion = getVersion()
|
||||
const tauriApiClient = new TauriModrinthClient({
|
||||
userAgent: `modrinth/theseus/${getVersion()} (support@modrinth.com)`,
|
||||
userAgent: async () => `modrinth/theseus/${await appVersion} (support@modrinth.com)`,
|
||||
labrinthBaseUrl: config.labrinthBaseUrl,
|
||||
archonBaseUrl: config.archonBaseUrl,
|
||||
features: [
|
||||
@@ -261,6 +263,8 @@ onUnmounted(async () => {
|
||||
})
|
||||
|
||||
const { formatMessage } = useVIntl()
|
||||
const formatBytes = useFormatBytes()
|
||||
|
||||
const messages = defineMessages({
|
||||
updateInstalledToastTitle: {
|
||||
id: 'app.update.complete-toast.title',
|
||||
@@ -1567,11 +1571,15 @@ provideAppUpdateDownloadProgress(appUpdateDownload)
|
||||
|
||||
.app-grid-navbar {
|
||||
grid-area: nav;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.app-grid-statusbar {
|
||||
grid-area: status;
|
||||
padding-right: var(--window-controls-width, 0px);
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
[data-tauri-drag-region-exclude] {
|
||||
@@ -1657,10 +1665,11 @@ provideAppUpdateDownloadProgress(appUpdateDownload)
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
overflow-x: hidden;
|
||||
scrollbar-gutter: stable;
|
||||
}
|
||||
|
||||
.app-contents::before {
|
||||
z-index: 1;
|
||||
z-index: 30;
|
||||
content: '';
|
||||
position: fixed;
|
||||
left: var(--left-bar-width);
|
||||
|
||||
@@ -149,7 +149,7 @@ const handleOptionsClick = async (args) => {
|
||||
break
|
||||
case 'edit':
|
||||
await router.push({
|
||||
path: `/instance/${encodeURIComponent(args.item.path)}/`,
|
||||
path: `/instance/${encodeURIComponent(args.item.path)}`,
|
||||
})
|
||||
break
|
||||
case 'duplicate':
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<script setup>
|
||||
import { PlusIcon, XIcon } from '@modrinth/assets'
|
||||
import { WrenchIcon, XIcon } from '@modrinth/assets'
|
||||
import {
|
||||
Accordion,
|
||||
ButtonStyled,
|
||||
Checkbox,
|
||||
commonMessages,
|
||||
@@ -9,7 +10,7 @@ import {
|
||||
StyledInput,
|
||||
useVIntl,
|
||||
} from '@modrinth/ui'
|
||||
import { open } from '@tauri-apps/plugin-dialog'
|
||||
import { save } from '@tauri-apps/plugin-dialog'
|
||||
import { ref } from 'vue'
|
||||
|
||||
import { PackageIcon, VersionIcon } from '@/assets/icons'
|
||||
@@ -40,9 +41,13 @@ const messages = defineMessages({
|
||||
},
|
||||
selectFilesLabel: {
|
||||
id: 'app.export-modal.select-files-label',
|
||||
defaultMessage: 'Select files and folders to include in pack',
|
||||
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({
|
||||
@@ -65,7 +70,6 @@ const exportDescription = ref('')
|
||||
const versionInput = ref('1.0.0')
|
||||
const files = ref([])
|
||||
const folders = ref([])
|
||||
const showingFiles = ref(false)
|
||||
|
||||
const initFiles = async () => {
|
||||
const newFolders = new Map()
|
||||
@@ -87,7 +91,12 @@ const initFiles = async () => {
|
||||
folder.startsWith('modrinth_logs') ||
|
||||
folder.startsWith('.fabric'),
|
||||
}))
|
||||
.filter((pathData) => !pathData.path.includes('.DS_Store'))
|
||||
.filter(
|
||||
(pathData) =>
|
||||
!pathData.path.includes('.DS_Store') &&
|
||||
pathData.path !== 'mods/.connector' &&
|
||||
!pathData.path.startsWith('mods/.connector/'),
|
||||
)
|
||||
.forEach((pathData) => {
|
||||
const parent = pathData.path.split(sep).slice(0, -1).join(sep)
|
||||
if (parent !== '') {
|
||||
@@ -121,15 +130,20 @@ const exportPack = async () => {
|
||||
}
|
||||
})
|
||||
})
|
||||
const outputPath = await open({
|
||||
directory: true,
|
||||
multiple: false,
|
||||
const outputPath = await save({
|
||||
defaultPath: `${nameInput.value} ${versionInput.value}.mrpack`,
|
||||
filters: [
|
||||
{
|
||||
name: 'Modrinth Modpack',
|
||||
extensions: ['mrpack'],
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
if (outputPath) {
|
||||
export_profile_mrpack(
|
||||
props.instance.path,
|
||||
outputPath + `/${nameInput.value} ${versionInput.value}.mrpack`,
|
||||
outputPath,
|
||||
filesToExport,
|
||||
versionInput.value,
|
||||
exportDescription.value,
|
||||
@@ -142,97 +156,91 @@ const exportPack = async () => {
|
||||
|
||||
<template>
|
||||
<ModalWrapper ref="exportModal" :header="formatMessage(messages.header)">
|
||||
<div class="modal-body">
|
||||
<div class="labeled_input">
|
||||
<p>{{ formatMessage(messages.modpackNameLabel) }}</p>
|
||||
<StyledInput
|
||||
v-model="nameInput"
|
||||
:icon="PackageIcon"
|
||||
type="text"
|
||||
:placeholder="formatMessage(messages.modpackNamePlaceholder)"
|
||||
clearable
|
||||
/>
|
||||
</div>
|
||||
<div class="labeled_input">
|
||||
<p>{{ formatMessage(messages.versionNumberLabel) }}</p>
|
||||
<StyledInput
|
||||
v-model="versionInput"
|
||||
:icon="VersionIcon"
|
||||
type="text"
|
||||
:placeholder="formatMessage(messages.versionNumberPlaceholder)"
|
||||
clearable
|
||||
/>
|
||||
</div>
|
||||
<div class="adjacent-input">
|
||||
<div class="flex flex-col gap-4 w-[40rem]">
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<div class="labeled_input">
|
||||
<p>{{ formatMessage(commonMessages.descriptionLabel) }}</p>
|
||||
|
||||
<p>{{ formatMessage(messages.modpackNameLabel) }}</p>
|
||||
<StyledInput
|
||||
v-model="exportDescription"
|
||||
multiline
|
||||
:placeholder="formatMessage(messages.descriptionPlaceholder)"
|
||||
v-model="nameInput"
|
||||
:icon="PackageIcon"
|
||||
type="text"
|
||||
:placeholder="formatMessage(messages.modpackNamePlaceholder)"
|
||||
clearable
|
||||
/>
|
||||
</div>
|
||||
<div class="labeled_input">
|
||||
<p>{{ formatMessage(messages.versionNumberLabel) }}</p>
|
||||
<StyledInput
|
||||
v-model="versionInput"
|
||||
:icon="VersionIcon"
|
||||
type="text"
|
||||
:placeholder="formatMessage(messages.versionNumberPlaceholder)"
|
||||
clearable
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="table">
|
||||
<div class="table-head">
|
||||
<div class="table-cell row-wise">
|
||||
{{ formatMessage(messages.selectFilesLabel) }}
|
||||
<ButtonStyled circular>
|
||||
<button @click="() => (showingFiles = !showingFiles)">
|
||||
<PlusIcon v-if="!showingFiles" />
|
||||
<XIcon v-else />
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="showingFiles" class="table-content">
|
||||
<div v-for="[path, children] in folders" :key="path.name" class="table-row">
|
||||
<div class="table-cell file-entry">
|
||||
<div class="file-primary">
|
||||
<div class="flex flex-col gap-2">
|
||||
<p class="m-0">{{ formatMessage(commonMessages.descriptionLabel) }}</p>
|
||||
<StyledInput
|
||||
v-model="exportDescription"
|
||||
multiline
|
||||
:placeholder="formatMessage(messages.descriptionPlaceholder)"
|
||||
/>
|
||||
</div>
|
||||
<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)"
|
||||
:label="path.name"
|
||||
class="select-checkbox"
|
||||
: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="path.showingMore"
|
||||
class="select-checkbox dropdown"
|
||||
collapsing-toggle-style
|
||||
v-model="child.selected"
|
||||
:label="child.name"
|
||||
class="w-full px-8 py-2 hover:bg-surface-4 text-primary"
|
||||
:disabled="child.disabled"
|
||||
/>
|
||||
</div>
|
||||
<div v-if="path.showingMore" class="file-secondary">
|
||||
<div v-for="child in children" :key="child.path" class="file-secondary-row">
|
||||
<Checkbox
|
||||
v-model="child.selected"
|
||||
:label="child.name"
|
||||
class="select-checkbox"
|
||||
:disabled="child.disabled"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-for="file in files" :key="file.path" class="table-row">
|
||||
<div class="table-cell file-entry">
|
||||
<div class="file-primary">
|
||||
<Checkbox
|
||||
v-model="file.selected"
|
||||
:label="file.name"
|
||||
:disabled="file.disabled"
|
||||
class="select-checkbox"
|
||||
/>
|
||||
</div>
|
||||
</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>
|
||||
</div>
|
||||
<div class="button-row push-right">
|
||||
</Accordion>
|
||||
<div class="flex items-center justify-end gap-2">
|
||||
<ButtonStyled type="outlined">
|
||||
<button @click="exportModal.hide">
|
||||
<XIcon />
|
||||
@@ -249,83 +257,3 @@ const exportPack = async () => {
|
||||
</div>
|
||||
</ModalWrapper>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.modal-body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--gap-md);
|
||||
}
|
||||
|
||||
.labeled_input {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--gap-sm);
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.select-checkbox {
|
||||
gap: var(--gap-sm);
|
||||
|
||||
button.checkbox {
|
||||
border: none;
|
||||
}
|
||||
|
||||
&.dropdown {
|
||||
margin-left: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.table-content {
|
||||
max-height: 18rem;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.table {
|
||||
border: 1px solid var(--color-bg);
|
||||
}
|
||||
|
||||
.file-entry {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--gap-sm);
|
||||
}
|
||||
|
||||
.file-primary {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--gap-sm);
|
||||
}
|
||||
|
||||
.file-secondary {
|
||||
margin-left: var(--gap-xl);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--gap-sm);
|
||||
height: 100%;
|
||||
vertical-align: center;
|
||||
}
|
||||
|
||||
.file-secondary-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--gap-sm);
|
||||
}
|
||||
|
||||
.button-row {
|
||||
display: flex;
|
||||
gap: var(--gap-sm);
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.row-wise {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -0,0 +1,332 @@
|
||||
import type { Labrinth } from '@modrinth/api-client'
|
||||
import { CheckIcon, PlayIcon, PlusIcon, StopCircleIcon } from '@modrinth/assets'
|
||||
import type { CardAction } from '@modrinth/ui'
|
||||
import { commonMessages, defineMessages, useDebugLogger, useVIntl } from '@modrinth/ui'
|
||||
import { openUrl } from '@tauri-apps/plugin-opener'
|
||||
import type { ComputedRef, Ref } from 'vue'
|
||||
import { onUnmounted, ref, shallowRef } from 'vue'
|
||||
import type { Router } from 'vue-router'
|
||||
|
||||
import { process_listener } from '@/helpers/events'
|
||||
import { get_by_profile_path } from '@/helpers/process'
|
||||
import { kill, list as listInstances } from '@/helpers/profile.js'
|
||||
import type { GameInstance } from '@/helpers/types'
|
||||
import { add_server_to_profile, getServerLatency } from '@/helpers/worlds'
|
||||
import { getServerAddress } from '@/store/install.js'
|
||||
|
||||
interface BrowseServerInstance {
|
||||
name: string
|
||||
path: string
|
||||
}
|
||||
|
||||
interface ContextMenuHandle {
|
||||
showMenu: (
|
||||
event: MouseEvent,
|
||||
result: Labrinth.Search.v2.ResultSearchProject | Labrinth.Search.v3.ResultSearchProject,
|
||||
options: { name: string }[],
|
||||
) => void
|
||||
}
|
||||
|
||||
interface ContextMenuOptionClick {
|
||||
option: 'open_link' | 'copy_link'
|
||||
item: Labrinth.Search.v2.ResultSearchProject | Labrinth.Search.v3.ResultSearchProject
|
||||
}
|
||||
|
||||
export interface UseAppServerBrowseOptions {
|
||||
instance: Ref<BrowseServerInstance | null>
|
||||
isFromWorlds: ComputedRef<boolean>
|
||||
allInstalledIds: ComputedRef<Set<string>>
|
||||
newlyInstalled: Ref<string[]>
|
||||
installingServerProjects: Ref<string[]>
|
||||
playServerProject: (projectId: string) => Promise<void>
|
||||
showAddServerToInstanceModal: (serverName: string, serverAddress: string) => void
|
||||
handleError: (error: unknown) => void
|
||||
router: Router
|
||||
}
|
||||
|
||||
const messages = defineMessages({
|
||||
addToInstance: {
|
||||
id: 'app.browse.add-to-instance',
|
||||
defaultMessage: 'Add to instance',
|
||||
},
|
||||
addToInstanceName: {
|
||||
id: 'app.browse.add-to-instance-name',
|
||||
defaultMessage: 'Add to {instanceName}',
|
||||
},
|
||||
added: {
|
||||
id: 'app.browse.added',
|
||||
defaultMessage: 'Added',
|
||||
},
|
||||
alreadyAdded: {
|
||||
id: 'app.browse.already-added',
|
||||
defaultMessage: 'Already added',
|
||||
},
|
||||
})
|
||||
|
||||
export function useAppServerBrowse(options: UseAppServerBrowseOptions) {
|
||||
const { formatMessage } = useVIntl()
|
||||
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 serverPingCacheActive = true
|
||||
let unlistenProcesses: (() => void) | null = null
|
||||
|
||||
async function checkServerRunningStates(hits: Labrinth.Search.v3.ResultSearchProject[]) {
|
||||
debugLog('checkServerRunningStates', { hitCount: hits.length })
|
||||
const packs = await listInstances().catch((error) => {
|
||||
options.handleError(error)
|
||||
return []
|
||||
})
|
||||
const newRunning: Record<string, string> = {}
|
||||
for (const hit of hits) {
|
||||
const inst = packs.find(
|
||||
(pack: GameInstance) => pack.linked_data?.project_id === hit.project_id,
|
||||
)
|
||||
if (inst) {
|
||||
const processes = await get_by_profile_path(inst.path).catch(() => [])
|
||||
if (Array.isArray(processes) && processes.length > 0) {
|
||||
newRunning[hit.project_id] = inst.path
|
||||
}
|
||||
}
|
||||
}
|
||||
debugLog('runningServerProjects updated', newRunning)
|
||||
runningServerProjects.value = newRunning
|
||||
}
|
||||
|
||||
async function handleStopServerProject(projectId: string) {
|
||||
debugLog('handleStopServerProject', projectId)
|
||||
const instancePath = runningServerProjects.value[projectId]
|
||||
if (!instancePath) return
|
||||
await kill(instancePath).catch(() => {})
|
||||
const { [projectId]: _, ...rest } = runningServerProjects.value
|
||||
runningServerProjects.value = rest
|
||||
}
|
||||
|
||||
async function handlePlayServerProject(projectId: string) {
|
||||
debugLog('handlePlayServerProject', projectId)
|
||||
await options.playServerProject(projectId)
|
||||
checkServerRunningStates(lastServerHits.value)
|
||||
}
|
||||
|
||||
async function handleAddServerToInstance(project: Labrinth.Search.v3.ResultSearchProject) {
|
||||
debugLog('handleAddServerToInstance', { projectId: project.project_id, name: project.name })
|
||||
const address = getServerAddress(project.minecraft_java_server)
|
||||
if (!address) return
|
||||
|
||||
if (options.instance.value) {
|
||||
try {
|
||||
await add_server_to_profile(
|
||||
options.instance.value.path,
|
||||
project.name,
|
||||
address,
|
||||
'prompt',
|
||||
project.project_id,
|
||||
project.minecraft_java_server?.content?.kind,
|
||||
)
|
||||
options.newlyInstalled.value.push(project.project_id)
|
||||
} catch (error) {
|
||||
options.handleError(error)
|
||||
}
|
||||
} else {
|
||||
options.showAddServerToInstanceModal(project.name, address)
|
||||
}
|
||||
}
|
||||
|
||||
async function pingServerHits(hits: Labrinth.Search.v3.ResultSearchProject[]) {
|
||||
debugLog('pingServerHits', { hitCount: hits.length })
|
||||
const pingsToFetch = hits.flatMap((hit) => {
|
||||
const address = hit.minecraft_java_server?.address
|
||||
if (!address) return []
|
||||
return [{ hit, address }]
|
||||
})
|
||||
const nextPings = { ...serverPings.value }
|
||||
for (const { hit, address } of pingsToFetch) {
|
||||
if (serverPingCache.has(address)) {
|
||||
nextPings[hit.project_id] = serverPingCache.get(address)
|
||||
}
|
||||
}
|
||||
serverPings.value = nextPings
|
||||
|
||||
await Promise.all(
|
||||
pingsToFetch.map(async ({ hit, address }) => {
|
||||
if (serverPingCache.has(address)) return
|
||||
|
||||
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 }
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
function updateServerHits(hits: Labrinth.Search.v3.ResultSearchProject[]) {
|
||||
lastServerHits.value = hits
|
||||
pingServerHits(hits)
|
||||
checkServerRunningStates(hits)
|
||||
}
|
||||
|
||||
function getServerModpackContent(project: Labrinth.Search.v3.ResultSearchProject) {
|
||||
const content = project.minecraft_java_server?.content
|
||||
if (content?.kind === 'modpack') {
|
||||
const { project_name, project_icon, project_id } = content
|
||||
if (!project_name) return undefined
|
||||
return {
|
||||
name: project_name,
|
||||
icon: project_icon ?? undefined,
|
||||
onclick:
|
||||
project_id !== project.project_id
|
||||
? () => {
|
||||
options.router.push(`/project/${project_id}`)
|
||||
}
|
||||
: undefined,
|
||||
showCustomModpackTooltip: project_id === project.project_id,
|
||||
}
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
|
||||
function getServerCardActions(
|
||||
serverResult: Labrinth.Search.v3.ResultSearchProject,
|
||||
): CardAction[] {
|
||||
const isInstalled = options.allInstalledIds.value.has(serverResult.project_id)
|
||||
|
||||
if (options.isFromWorlds.value && options.instance.value) {
|
||||
return [
|
||||
{
|
||||
key: 'add-to-instance',
|
||||
label: formatMessage(isInstalled ? messages.added : messages.addToInstance),
|
||||
icon: isInstalled ? CheckIcon : PlusIcon,
|
||||
disabled: isInstalled,
|
||||
color: 'brand',
|
||||
type: 'outlined',
|
||||
onClick: () => handleAddServerToInstance(serverResult),
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
const actions: CardAction[] = []
|
||||
|
||||
actions.push({
|
||||
key: 'add',
|
||||
label: '',
|
||||
icon: isInstalled ? CheckIcon : PlusIcon,
|
||||
disabled: isInstalled,
|
||||
circular: true,
|
||||
tooltip: isInstalled
|
||||
? formatMessage(messages.alreadyAdded)
|
||||
: options.instance.value
|
||||
? formatMessage(messages.addToInstanceName, {
|
||||
instanceName: options.instance.value.name,
|
||||
})
|
||||
: formatMessage(commonMessages.addServerToInstanceButton),
|
||||
onClick: () => handleAddServerToInstance(serverResult),
|
||||
})
|
||||
|
||||
if (runningServerProjects.value[serverResult.project_id]) {
|
||||
actions.push({
|
||||
key: 'stop',
|
||||
label: formatMessage(commonMessages.stopButton),
|
||||
icon: StopCircleIcon,
|
||||
color: 'red',
|
||||
type: 'outlined',
|
||||
onClick: () => handleStopServerProject(serverResult.project_id),
|
||||
})
|
||||
} else {
|
||||
const isInstalling = options.installingServerProjects.value.includes(serverResult.project_id)
|
||||
actions.push({
|
||||
key: 'play',
|
||||
label: formatMessage(
|
||||
isInstalling ? commonMessages.installingLabel : commonMessages.playButton,
|
||||
),
|
||||
icon: PlayIcon,
|
||||
disabled: isInstalling,
|
||||
color: 'brand',
|
||||
type: 'outlined',
|
||||
onClick: () => handlePlayServerProject(serverResult.project_id),
|
||||
})
|
||||
}
|
||||
|
||||
return actions
|
||||
}
|
||||
|
||||
function handleRightClick(
|
||||
event: MouseEvent,
|
||||
result: Labrinth.Search.v2.ResultSearchProject | Labrinth.Search.v3.ResultSearchProject,
|
||||
) {
|
||||
contextMenuRef.value?.showMenu(event, result, [{ name: 'open_link' }, { name: 'copy_link' }])
|
||||
}
|
||||
|
||||
function handleOptionsClick(args: ContextMenuOptionClick) {
|
||||
const url = getProjectUrl(args.item)
|
||||
switch (args.option) {
|
||||
case 'open_link':
|
||||
openUrl(url)
|
||||
break
|
||||
case 'copy_link':
|
||||
navigator.clipboard.writeText(url)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
process_listener((event: { event: string; profile_path_id: string }) => {
|
||||
debugLog('process event', event)
|
||||
if (event.event === 'finished') {
|
||||
const projectId = Object.entries(runningServerProjects.value).find(
|
||||
([, path]) => path === event.profile_path_id,
|
||||
)?.[0]
|
||||
if (projectId) {
|
||||
const { [projectId]: _, ...rest } = runningServerProjects.value
|
||||
runningServerProjects.value = rest
|
||||
}
|
||||
}
|
||||
})
|
||||
.then((unlisten) => {
|
||||
unlistenProcesses = unlisten
|
||||
})
|
||||
.catch(options.handleError)
|
||||
|
||||
onUnmounted(() => {
|
||||
serverPingCacheActive = false
|
||||
unlistenProcesses?.()
|
||||
serverPingCache.clear()
|
||||
pendingServerPings.clear()
|
||||
})
|
||||
|
||||
return {
|
||||
serverPings,
|
||||
contextMenuRef,
|
||||
updateServerHits,
|
||||
getServerModpackContent,
|
||||
getServerCardActions,
|
||||
handleRightClick,
|
||||
handleOptionsClick,
|
||||
}
|
||||
}
|
||||
|
||||
function getProjectUrl(
|
||||
item: Labrinth.Search.v2.ResultSearchProject | Labrinth.Search.v3.ResultSearchProject,
|
||||
) {
|
||||
const projectType = 'project_types' in item ? item.project_types?.[0] : item.project_type
|
||||
return `https://modrinth.com/${projectType ?? 'project'}/${item.slug ?? item.project_id}`
|
||||
}
|
||||
@@ -36,18 +36,38 @@ export function useIntercomPositioning({
|
||||
() => route.path.startsWith('/browse') || route.path.startsWith('/project'),
|
||||
)
|
||||
const sidebarVisible = computed(() => sidebarToggled.value || forceSidebar.value)
|
||||
const intercomBubbleHorizontalPadding = computed(() =>
|
||||
const defaultIntercomBubbleHorizontalPadding = computed(() =>
|
||||
sidebarVisible.value
|
||||
? APP_SIDEBAR_WIDTH + INTERCOM_BUBBLE_DEFAULT_PADDING
|
||||
: INTERCOM_BUBBLE_DEFAULT_PADDING,
|
||||
)
|
||||
const intercomBubbleRequestedHorizontalPadding = ref<number | null>(null)
|
||||
const intercomBubbleHorizontalPadding = computed(
|
||||
() =>
|
||||
intercomBubbleRequestedHorizontalPadding.value ??
|
||||
defaultIntercomBubbleHorizontalPadding.value,
|
||||
)
|
||||
const intercomBubbleVerticalClearance = ref<number | null>(null)
|
||||
const intercomBubblePosition = computed(() => ({
|
||||
horizontalPadding: intercomBubbleHorizontalPadding.value,
|
||||
verticalPadding: intercomBubbleVerticalClearance.value ?? INTERCOM_BUBBLE_DEFAULT_PADDING,
|
||||
}))
|
||||
const intercomBubbleHorizontalPaddingRequests = new Map<symbol, number>()
|
||||
const intercomBubbleClearanceRequests = new Map<symbol, number>()
|
||||
|
||||
function requestIntercomBubbleHorizontalPadding(id: symbol, padding: number | null) {
|
||||
if (padding === null) {
|
||||
intercomBubbleHorizontalPaddingRequests.delete(id)
|
||||
} else {
|
||||
intercomBubbleHorizontalPaddingRequests.set(id, padding)
|
||||
}
|
||||
|
||||
intercomBubbleRequestedHorizontalPadding.value =
|
||||
intercomBubbleHorizontalPaddingRequests.size > 0
|
||||
? Math.max(...intercomBubbleHorizontalPaddingRequests.values())
|
||||
: null
|
||||
}
|
||||
|
||||
function requestIntercomBubbleVerticalClearance(id: symbol, clearance: number | null) {
|
||||
if (clearance === null) {
|
||||
intercomBubbleClearanceRequests.delete(id)
|
||||
@@ -93,6 +113,7 @@ export function useIntercomPositioning({
|
||||
intercomBubble: {
|
||||
width: ref(INTERCOM_BUBBLE_WIDTH),
|
||||
horizontalPadding: intercomBubbleHorizontalPadding,
|
||||
requestHorizontalPadding: requestIntercomBubbleHorizontalPadding,
|
||||
requestVerticalClearance: requestIntercomBubbleVerticalClearance,
|
||||
},
|
||||
},
|
||||
|
||||
@@ -184,7 +184,7 @@ export async function update_project(path: string, projectPath: string): Promise
|
||||
|
||||
// Add a project to a profile from a version
|
||||
// Returns a path to the new project file
|
||||
export type DownloadReason = 'standalone' | 'dependency' | 'modpack'
|
||||
export type DownloadReason = 'standalone' | 'dependency' | 'modpack' | 'update'
|
||||
|
||||
export async function add_project_from_version(
|
||||
path: string,
|
||||
|
||||
@@ -29,15 +29,12 @@
|
||||
"app.browse.discover-servers": {
|
||||
"message": "استكشف خوادم"
|
||||
},
|
||||
"app.browse.hide-added-servers": {
|
||||
"message": "إخفاء الخوادم المضافة"
|
||||
},
|
||||
"app.browse.hide-installed-content": {
|
||||
"message": "إخفاء المحتوى المضاف"
|
||||
},
|
||||
"app.browse.install-content-to-instance": {
|
||||
"message": "تثبيت محتوى لنموذجك"
|
||||
},
|
||||
"app.browse.server.installing": {
|
||||
"message": "جاري التثبيت"
|
||||
},
|
||||
"app.export-modal.description-placeholder": {
|
||||
"message": "أدخل وصف التعديل..."
|
||||
},
|
||||
|
||||
@@ -29,12 +29,6 @@
|
||||
"app.browse.discover-servers": {
|
||||
"message": "Prozkoumat servery"
|
||||
},
|
||||
"app.browse.hide-added-servers": {
|
||||
"message": "Skrýt přidané servery"
|
||||
},
|
||||
"app.browse.hide-installed-content": {
|
||||
"message": "Skrýt nainstalovaný obsah"
|
||||
},
|
||||
"app.browse.install-content-to-instance": {
|
||||
"message": "Nainstalovat obsah do instnce"
|
||||
},
|
||||
|
||||
@@ -20,12 +20,6 @@
|
||||
"app.browse.discover-servers": {
|
||||
"message": "Opdag servere"
|
||||
},
|
||||
"app.browse.hide-added-servers": {
|
||||
"message": "Gem tilføjet servere"
|
||||
},
|
||||
"app.browse.hide-installed-content": {
|
||||
"message": "Gem installeret indhold"
|
||||
},
|
||||
"app.export-modal.description-placeholder": {
|
||||
"message": "Indtast modpack beskrivelse..."
|
||||
},
|
||||
|
||||
@@ -1,4 +1,103 @@
|
||||
{
|
||||
"app.action-bar.downloading-java": {
|
||||
"message": "Java {version} wird heruntergeladen"
|
||||
},
|
||||
"app.action-bar.downloads": {
|
||||
"message": "Downloads"
|
||||
},
|
||||
"app.action-bar.hide-more-running-instances": {
|
||||
"message": "Weitere laufende Instanzen ausblenden"
|
||||
},
|
||||
"app.action-bar.make-primary-instance": {
|
||||
"message": "Zur primären Instanz machen"
|
||||
},
|
||||
"app.action-bar.no-instances-running": {
|
||||
"message": "Keine Instanzen laufen"
|
||||
},
|
||||
"app.action-bar.offline": {
|
||||
"message": "Offline"
|
||||
},
|
||||
"app.action-bar.primary-instance": {
|
||||
"message": "Primäre Instanz"
|
||||
},
|
||||
"app.action-bar.show-more-running-instances": {
|
||||
"message": "Weitere laufende Instanzen anzeigen"
|
||||
},
|
||||
"app.action-bar.stop-instance": {
|
||||
"message": "Instanz stoppen"
|
||||
},
|
||||
"app.action-bar.view-active-downloads": {
|
||||
"message": "Aktive Downloads anzeigen"
|
||||
},
|
||||
"app.action-bar.view-instance": {
|
||||
"message": "Instanz anzeigen"
|
||||
},
|
||||
"app.action-bar.view-logs": {
|
||||
"message": "Protokolle anzeigen"
|
||||
},
|
||||
"app.appearance-settings.advanced-rendering.description": {
|
||||
"message": "Aktiviert erweiterte Rendering-Funktionen wie Unschärfeeffekte, welche ohne hardwarebeschleunigtes Rendering zu Leistungsproblemen führen können."
|
||||
},
|
||||
"app.appearance-settings.advanced-rendering.title": {
|
||||
"message": "Erweitertes Rendering"
|
||||
},
|
||||
"app.appearance-settings.color-theme.description": {
|
||||
"message": "Wähle dein bevorzugtes Farbschema für die Modrinth App."
|
||||
},
|
||||
"app.appearance-settings.color-theme.title": {
|
||||
"message": "Farbschema"
|
||||
},
|
||||
"app.appearance-settings.default-landing-page.description": {
|
||||
"message": "Ändere die Seite, die beim Öffnen des Launchers angezeigt wird."
|
||||
},
|
||||
"app.appearance-settings.default-landing-page.home": {
|
||||
"message": "Start"
|
||||
},
|
||||
"app.appearance-settings.default-landing-page.library": {
|
||||
"message": "Bibliothek"
|
||||
},
|
||||
"app.appearance-settings.default-landing-page.title": {
|
||||
"message": "Standard-Startseite"
|
||||
},
|
||||
"app.appearance-settings.hide-nametag.description": {
|
||||
"message": "Deaktiviert das Namensschild über deinem Spieler auf der Skin-Seite."
|
||||
},
|
||||
"app.appearance-settings.hide-nametag.title": {
|
||||
"message": "Namensschild ausblenden"
|
||||
},
|
||||
"app.appearance-settings.jump-back-into-worlds.description": {
|
||||
"message": "Enthält zuletzt gespielte Welten im Abschnitt „Direkt weiterspielen“ auf der Startseite."
|
||||
},
|
||||
"app.appearance-settings.jump-back-into-worlds.title": {
|
||||
"message": "Springe zurück in Welten"
|
||||
},
|
||||
"app.appearance-settings.minimize-launcher.description": {
|
||||
"message": "Den Launcher minimieren, wenn ein Minecraft-Prozess gestartet wird."
|
||||
},
|
||||
"app.appearance-settings.minimize-launcher.title": {
|
||||
"message": "Launcher minimieren"
|
||||
},
|
||||
"app.appearance-settings.native-decorations.description": {
|
||||
"message": "Systemfensterrahmen verwenden (Neustart der App erforderlich)."
|
||||
},
|
||||
"app.appearance-settings.native-decorations.title": {
|
||||
"message": "Native Dekorationen"
|
||||
},
|
||||
"app.appearance-settings.select-option": {
|
||||
"message": "Option wählen"
|
||||
},
|
||||
"app.appearance-settings.toggle-sidebar.description": {
|
||||
"message": "Ermöglicht das Umschalten der Seitenleiste."
|
||||
},
|
||||
"app.appearance-settings.toggle-sidebar.title": {
|
||||
"message": "Seitenleiste umschalten"
|
||||
},
|
||||
"app.appearance-settings.unknown-pack-warning.description": {
|
||||
"message": "Falls du versuchst, eine Modrinth-Pack-Datei (.mrpack) zu installieren, die nicht auf Modrinth gehostet wird, werden wir sicherstellen, dass du die Risiken vor der Installation verstehst."
|
||||
},
|
||||
"app.appearance-settings.unknown-pack-warning.title": {
|
||||
"message": "Warne mich, bevor unbekannte Modpacks installiert werden"
|
||||
},
|
||||
"app.auth-servers.unreachable.body": {
|
||||
"message": "Die Authentifizierungsserver von Minecraft sind eventuell momentan nicht erreichbar. Überprüfe deine Internetverbindung und versuche es später erneut."
|
||||
},
|
||||
@@ -30,14 +129,32 @@
|
||||
"message": "Server entdecken"
|
||||
},
|
||||
"app.browse.hide-added-servers": {
|
||||
"message": "Hinzugefügte Server ausblenden"
|
||||
"message": "Bereits hinzugefügte Server ausblenden"
|
||||
},
|
||||
"app.browse.hide-installed-content": {
|
||||
"message": "Installierte Inhalte ausblenden"
|
||||
"message": "Bereits installierte Inhalte ausblenden"
|
||||
},
|
||||
"app.browse.install-content-to-instance": {
|
||||
"message": "Inhalt in Instanz installieren"
|
||||
},
|
||||
"app.browse.project-type.modpacks": {
|
||||
"message": "Modpacks"
|
||||
},
|
||||
"app.browse.server.install": {
|
||||
"message": "Installieren"
|
||||
},
|
||||
"app.browse.server.installed": {
|
||||
"message": "Installiert"
|
||||
},
|
||||
"app.browse.server.installing": {
|
||||
"message": "Wird installiert"
|
||||
},
|
||||
"app.creation-modal.installing-modpack.description": {
|
||||
"message": "{fileName}"
|
||||
},
|
||||
"app.creation-modal.installing-modpack.title": {
|
||||
"message": "Modpack wird installiert..."
|
||||
},
|
||||
"app.export-modal.description-placeholder": {
|
||||
"message": "Modpaketbeschreibung eingeben..."
|
||||
},
|
||||
@@ -575,6 +692,24 @@
|
||||
"instance.worlds.world_in_use": {
|
||||
"message": "Welt bereits in Benutzung"
|
||||
},
|
||||
"minecraft-account.add-account": {
|
||||
"message": "Konto hinzufügen"
|
||||
},
|
||||
"minecraft-account.label": {
|
||||
"message": "Minecraft-Konto"
|
||||
},
|
||||
"minecraft-account.not-signed-in": {
|
||||
"message": "Nicht angemeldet"
|
||||
},
|
||||
"minecraft-account.remove-account": {
|
||||
"message": "Konto entfernen"
|
||||
},
|
||||
"minecraft-account.select-account": {
|
||||
"message": "Konto auswählen"
|
||||
},
|
||||
"minecraft-account.sign-in": {
|
||||
"message": "In Minecraft anmelden"
|
||||
},
|
||||
"search.filter.locked.instance": {
|
||||
"message": "Von der Instanz vorgegeben"
|
||||
},
|
||||
@@ -598,5 +733,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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,103 @@
|
||||
{
|
||||
"app.action-bar.downloading-java": {
|
||||
"message": "Java {version} wird heruntergeladen"
|
||||
},
|
||||
"app.action-bar.downloads": {
|
||||
"message": "Downloads"
|
||||
},
|
||||
"app.action-bar.hide-more-running-instances": {
|
||||
"message": "Weitere laufende Instanzen ausblenden"
|
||||
},
|
||||
"app.action-bar.make-primary-instance": {
|
||||
"message": "Zur primären Instanz machen"
|
||||
},
|
||||
"app.action-bar.no-instances-running": {
|
||||
"message": "Keine Instanzen laufen"
|
||||
},
|
||||
"app.action-bar.offline": {
|
||||
"message": "Offline"
|
||||
},
|
||||
"app.action-bar.primary-instance": {
|
||||
"message": "Primäre Instanz"
|
||||
},
|
||||
"app.action-bar.show-more-running-instances": {
|
||||
"message": "Weitere laufende Instanzen anzeigen"
|
||||
},
|
||||
"app.action-bar.stop-instance": {
|
||||
"message": "Instanz stoppen"
|
||||
},
|
||||
"app.action-bar.view-active-downloads": {
|
||||
"message": "Aktive Downloads anzeigen"
|
||||
},
|
||||
"app.action-bar.view-instance": {
|
||||
"message": "Instanz anzeigen"
|
||||
},
|
||||
"app.action-bar.view-logs": {
|
||||
"message": "Protokolle anzeigen"
|
||||
},
|
||||
"app.appearance-settings.advanced-rendering.description": {
|
||||
"message": "Aktiviert erweiterte Rendering-Funktionen wie Unschärfe-Effekte, welche ohne Hardware-beschleunigtes Rendering zu Leistungsproblemen führen können."
|
||||
},
|
||||
"app.appearance-settings.advanced-rendering.title": {
|
||||
"message": "Erweitertes Rendering"
|
||||
},
|
||||
"app.appearance-settings.color-theme.description": {
|
||||
"message": "Wähle dein bevorzugtes Farbschema für die Modrinth App."
|
||||
},
|
||||
"app.appearance-settings.color-theme.title": {
|
||||
"message": "Farbschema"
|
||||
},
|
||||
"app.appearance-settings.default-landing-page.description": {
|
||||
"message": "Ändere die Seite, die beim Öffnen des Launchers angezeigt wird."
|
||||
},
|
||||
"app.appearance-settings.default-landing-page.home": {
|
||||
"message": "Start"
|
||||
},
|
||||
"app.appearance-settings.default-landing-page.library": {
|
||||
"message": "Bibliothek"
|
||||
},
|
||||
"app.appearance-settings.default-landing-page.title": {
|
||||
"message": "Standard-Startseite"
|
||||
},
|
||||
"app.appearance-settings.hide-nametag.description": {
|
||||
"message": "Deaktiviert das Namensschild über deinem Spieler auf der Skin-Seite."
|
||||
},
|
||||
"app.appearance-settings.hide-nametag.title": {
|
||||
"message": "Namensschild ausblenden"
|
||||
},
|
||||
"app.appearance-settings.jump-back-into-worlds.description": {
|
||||
"message": "Enthält zuletzt gespielte Welten im Abschnitt „Direkt weiterspielen“ auf der Startseite."
|
||||
},
|
||||
"app.appearance-settings.jump-back-into-worlds.title": {
|
||||
"message": "Direkt in Welten weiterspielen"
|
||||
},
|
||||
"app.appearance-settings.minimize-launcher.description": {
|
||||
"message": "Den Launcher minimieren, wenn ein Minecraft-Prozess gestartet wird."
|
||||
},
|
||||
"app.appearance-settings.minimize-launcher.title": {
|
||||
"message": "Launcher minimieren"
|
||||
},
|
||||
"app.appearance-settings.native-decorations.description": {
|
||||
"message": "Fensterdekorationen des Systems verwenden (Neustart der App erforderlich)."
|
||||
},
|
||||
"app.appearance-settings.native-decorations.title": {
|
||||
"message": "Native Dekorationen"
|
||||
},
|
||||
"app.appearance-settings.select-option": {
|
||||
"message": "Wähle eine Option"
|
||||
},
|
||||
"app.appearance-settings.toggle-sidebar.description": {
|
||||
"message": "Ermöglicht das Umschalten der Seitenleiste."
|
||||
},
|
||||
"app.appearance-settings.toggle-sidebar.title": {
|
||||
"message": "Seitenleiste umschalten"
|
||||
},
|
||||
"app.appearance-settings.unknown-pack-warning.description": {
|
||||
"message": "Falls du versuchst, eine Modrinth-Pack-Datei (.mrpack) zu installieren, die nicht auf Modrinth gehostet wird, werden die Risiken vor der Installation bekannt gegeben."
|
||||
},
|
||||
"app.appearance-settings.unknown-pack-warning.title": {
|
||||
"message": "Warne mich, bevor unbekannte Modpacks installiert werden"
|
||||
},
|
||||
"app.auth-servers.unreachable.body": {
|
||||
"message": "Die Authentifizierungsserver von Minecraft sind eventuell momentan nicht erreichbar. Überprüfe deine Internetverbindung und versuche es später erneut."
|
||||
},
|
||||
@@ -6,7 +105,7 @@
|
||||
"message": "Authentifizierungsserver sind nicht erreichbar"
|
||||
},
|
||||
"app.browse.add-server-to-instance": {
|
||||
"message": "Server zu Instanz hinzufügen"
|
||||
"message": "Server zur Instanz hinzufügen"
|
||||
},
|
||||
"app.browse.add-servers-to-instance": {
|
||||
"message": "Server zu deiner Instanz hinzufügen"
|
||||
@@ -30,14 +129,32 @@
|
||||
"message": "Server entdecken"
|
||||
},
|
||||
"app.browse.hide-added-servers": {
|
||||
"message": "Hinzugefügte Server ausblenden"
|
||||
"message": "Bereits hinzugefügte Server ausblenden"
|
||||
},
|
||||
"app.browse.hide-installed-content": {
|
||||
"message": "Installierte Inhalte ausblenden"
|
||||
"message": "Bereits installierte Inhalte ausblenden"
|
||||
},
|
||||
"app.browse.install-content-to-instance": {
|
||||
"message": "Inhalt in Instanz installieren"
|
||||
},
|
||||
"app.browse.project-type.modpacks": {
|
||||
"message": "Modpacks"
|
||||
},
|
||||
"app.browse.server.install": {
|
||||
"message": "Installieren"
|
||||
},
|
||||
"app.browse.server.installed": {
|
||||
"message": "Installiert"
|
||||
},
|
||||
"app.browse.server.installing": {
|
||||
"message": "Wird installiert"
|
||||
},
|
||||
"app.creation-modal.installing-modpack.description": {
|
||||
"message": "{fileName}"
|
||||
},
|
||||
"app.creation-modal.installing-modpack.title": {
|
||||
"message": "Modpack wird installiert..."
|
||||
},
|
||||
"app.export-modal.description-placeholder": {
|
||||
"message": "Beschreibung des Modpacks eingeben..."
|
||||
},
|
||||
@@ -575,6 +692,24 @@
|
||||
"instance.worlds.world_in_use": {
|
||||
"message": "Welt wird aktuell benutzt"
|
||||
},
|
||||
"minecraft-account.add-account": {
|
||||
"message": "Konto hinzufügen"
|
||||
},
|
||||
"minecraft-account.label": {
|
||||
"message": "Minecraft-Konto"
|
||||
},
|
||||
"minecraft-account.not-signed-in": {
|
||||
"message": "Nicht angemeldet"
|
||||
},
|
||||
"minecraft-account.remove-account": {
|
||||
"message": "Konto entfernen"
|
||||
},
|
||||
"minecraft-account.select-account": {
|
||||
"message": "Konto auswählen"
|
||||
},
|
||||
"minecraft-account.sign-in": {
|
||||
"message": "Bei Minecraft anmelden"
|
||||
},
|
||||
"search.filter.locked.instance": {
|
||||
"message": "Von der Instanz vorgegeben"
|
||||
},
|
||||
@@ -598,5 +733,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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,11 +104,11 @@
|
||||
"app.auth-servers.unreachable.header": {
|
||||
"message": "Cannot reach authentication servers"
|
||||
},
|
||||
"app.browse.add-server-to-instance": {
|
||||
"message": "Add server to instance"
|
||||
},
|
||||
"app.browse.add-servers-to-instance": {
|
||||
"message": "Add servers to your instance"
|
||||
"message": "Adding server to instance"
|
||||
},
|
||||
"app.browse.add-to-an-instance": {
|
||||
"message": "Add to an instance"
|
||||
},
|
||||
"app.browse.add-to-instance": {
|
||||
"message": "Add to instance"
|
||||
@@ -122,6 +122,9 @@
|
||||
"app.browse.already-added": {
|
||||
"message": "Already added"
|
||||
},
|
||||
"app.browse.back-to-instance": {
|
||||
"message": "Back to instance"
|
||||
},
|
||||
"app.browse.discover-content": {
|
||||
"message": "Discover content"
|
||||
},
|
||||
@@ -131,20 +134,11 @@
|
||||
"app.browse.hide-added-servers": {
|
||||
"message": "Hide already added servers"
|
||||
},
|
||||
"app.browse.hide-installed-content": {
|
||||
"message": "Hide already installed content"
|
||||
},
|
||||
"app.browse.install-content-to-instance": {
|
||||
"message": "Install content to instance"
|
||||
},
|
||||
"app.browse.project-type.modpacks": {
|
||||
"message": "Modpacks"
|
||||
},
|
||||
"app.browse.server.install": {
|
||||
"message": "Install"
|
||||
},
|
||||
"app.browse.server.installed": {
|
||||
"message": "Installed"
|
||||
"app.browse.server-instance-content-warning": {
|
||||
"message": "Adding content can break compatibility when joining the server. Any added content will also be lost when you update the server instance content."
|
||||
},
|
||||
"app.browse.server.installing": {
|
||||
"message": "Installing"
|
||||
@@ -164,6 +158,9 @@
|
||||
"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"
|
||||
},
|
||||
@@ -171,7 +168,7 @@
|
||||
"message": "Modpack name"
|
||||
},
|
||||
"app.export-modal.select-files-label": {
|
||||
"message": "Select files and folders to include in pack"
|
||||
"message": "Configure which files are included in this export"
|
||||
},
|
||||
"app.export-modal.version-number-label": {
|
||||
"message": "Version number"
|
||||
@@ -302,6 +299,15 @@
|
||||
"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."
|
||||
},
|
||||
"app.project.install-button.already-installed": {
|
||||
"message": "This project is already installed"
|
||||
},
|
||||
"app.project.install-context.back-to-browse": {
|
||||
"message": "Back to browse"
|
||||
},
|
||||
"app.project.install-context.install-content-to-instance": {
|
||||
"message": "Install content to instance"
|
||||
},
|
||||
"app.settings.developer-mode-enabled": {
|
||||
"message": "Developer mode enabled."
|
||||
},
|
||||
|
||||
@@ -1,4 +1,103 @@
|
||||
{
|
||||
"app.action-bar.downloading-java": {
|
||||
"message": "Descargando Java {version}"
|
||||
},
|
||||
"app.action-bar.downloads": {
|
||||
"message": "Descargas"
|
||||
},
|
||||
"app.action-bar.hide-more-running-instances": {
|
||||
"message": "Ocultar más instancias en ejecución"
|
||||
},
|
||||
"app.action-bar.make-primary-instance": {
|
||||
"message": "Hacer instancia principal"
|
||||
},
|
||||
"app.action-bar.no-instances-running": {
|
||||
"message": "Ninguna instancia en ejecución"
|
||||
},
|
||||
"app.action-bar.offline": {
|
||||
"message": "Sin conexión"
|
||||
},
|
||||
"app.action-bar.primary-instance": {
|
||||
"message": "Instancia principal"
|
||||
},
|
||||
"app.action-bar.show-more-running-instances": {
|
||||
"message": "Mostrar mas instancias en ejecución"
|
||||
},
|
||||
"app.action-bar.stop-instance": {
|
||||
"message": "Finalizar instancia"
|
||||
},
|
||||
"app.action-bar.view-active-downloads": {
|
||||
"message": "Mostrar descargas activas"
|
||||
},
|
||||
"app.action-bar.view-instance": {
|
||||
"message": "Mostrar instancia"
|
||||
},
|
||||
"app.action-bar.view-logs": {
|
||||
"message": "Mostrar registros"
|
||||
},
|
||||
"app.appearance-settings.advanced-rendering.description": {
|
||||
"message": "Habilita el renderizado avanzado, como los efectos de desenfoque, que pueden causar problemas de rendimiento sin renderizado acelerado por hardware."
|
||||
},
|
||||
"app.appearance-settings.advanced-rendering.title": {
|
||||
"message": "Renderizado avanzado"
|
||||
},
|
||||
"app.appearance-settings.color-theme.description": {
|
||||
"message": "Seleccione su tema de color preferido para Modrinth en este dispositivo."
|
||||
},
|
||||
"app.appearance-settings.color-theme.title": {
|
||||
"message": "Tema de color"
|
||||
},
|
||||
"app.appearance-settings.default-landing-page.description": {
|
||||
"message": "Cambia la página en la que el launcher se abre en."
|
||||
},
|
||||
"app.appearance-settings.default-landing-page.home": {
|
||||
"message": "Inicio"
|
||||
},
|
||||
"app.appearance-settings.default-landing-page.library": {
|
||||
"message": "Librería"
|
||||
},
|
||||
"app.appearance-settings.default-landing-page.title": {
|
||||
"message": "Página predeterminada"
|
||||
},
|
||||
"app.appearance-settings.hide-nametag.description": {
|
||||
"message": "Desactiva la etiqueta arriba de tu personaje en la página de skins."
|
||||
},
|
||||
"app.appearance-settings.hide-nametag.title": {
|
||||
"message": "Ocultar etiqueta"
|
||||
},
|
||||
"app.appearance-settings.jump-back-into-worlds.description": {
|
||||
"message": "Incluye los mundos más recientes en la sección \"Vuelve a jugar tus mundos\" en la página de inicio."
|
||||
},
|
||||
"app.appearance-settings.jump-back-into-worlds.title": {
|
||||
"message": "Vuelve a jugar tus mundos"
|
||||
},
|
||||
"app.appearance-settings.minimize-launcher.description": {
|
||||
"message": "Minimiza el launcher cuando un proceso de Minecraft empieza."
|
||||
},
|
||||
"app.appearance-settings.minimize-launcher.title": {
|
||||
"message": "Minimiza el launcher"
|
||||
},
|
||||
"app.appearance-settings.native-decorations.description": {
|
||||
"message": "Usa los sistemas de frame de Windows (se requiere resetear la aplicación)."
|
||||
},
|
||||
"app.appearance-settings.native-decorations.title": {
|
||||
"message": "Decoraciones nativas"
|
||||
},
|
||||
"app.appearance-settings.select-option": {
|
||||
"message": "Selecciona una opción"
|
||||
},
|
||||
"app.appearance-settings.toggle-sidebar.description": {
|
||||
"message": "Activa la posibilidad de alternar la barra lateral."
|
||||
},
|
||||
"app.appearance-settings.toggle-sidebar.title": {
|
||||
"message": "Alternar barra lateral"
|
||||
},
|
||||
"app.appearance-settings.unknown-pack-warning.description": {
|
||||
"message": "Si intentas instalar un archivo de paquete de Modrinth (.mrpack) que no esté alojado en Modrinth, te advertiremos de los riesgos antes de instalarlo."
|
||||
},
|
||||
"app.appearance-settings.unknown-pack-warning.title": {
|
||||
"message": "Adviérteme antes de instalarme modpacks desconocidos"
|
||||
},
|
||||
"app.auth-servers.unreachable.body": {
|
||||
"message": "Los servidores de autenticación de Minecraft pueden no estar funcionando en este momento. Verifica tu conexión a internet e inténtalo de nuevo más tarde."
|
||||
},
|
||||
@@ -30,7 +129,7 @@
|
||||
"message": "Descubrir servidores"
|
||||
},
|
||||
"app.browse.hide-added-servers": {
|
||||
"message": "Ocultar servidores añadidos"
|
||||
"message": "Ocultar servidores ya añadidos"
|
||||
},
|
||||
"app.browse.hide-installed-content": {
|
||||
"message": "Ocultar contenido instalado"
|
||||
@@ -38,6 +137,24 @@
|
||||
"app.browse.install-content-to-instance": {
|
||||
"message": "Instalar contenido a la instancia"
|
||||
},
|
||||
"app.browse.project-type.modpacks": {
|
||||
"message": "Modpacks"
|
||||
},
|
||||
"app.browse.server.install": {
|
||||
"message": "Instalar"
|
||||
},
|
||||
"app.browse.server.installed": {
|
||||
"message": "Instalado"
|
||||
},
|
||||
"app.browse.server.installing": {
|
||||
"message": "Instalando"
|
||||
},
|
||||
"app.creation-modal.installing-modpack.description": {
|
||||
"message": "{fileName}"
|
||||
},
|
||||
"app.creation-modal.installing-modpack.title": {
|
||||
"message": "Instalando el modpack..."
|
||||
},
|
||||
"app.export-modal.description-placeholder": {
|
||||
"message": "Introduce la descripción del modpack..."
|
||||
},
|
||||
@@ -96,7 +213,7 @@
|
||||
"message": "Se añadieron {count} proyectos"
|
||||
},
|
||||
"app.instance.mods.share-text": {
|
||||
"message": "¡Mira a los proyectos que estoy usando en mi modpack!"
|
||||
"message": "¡Mira los proyectos que estoy usando en mi modpack!"
|
||||
},
|
||||
"app.instance.mods.share-title": {
|
||||
"message": "Compartiendo contenido del modpack"
|
||||
@@ -598,5 +715,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 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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,103 @@
|
||||
{
|
||||
"app.action-bar.downloading-java": {
|
||||
"message": "Descargando Java {version}"
|
||||
},
|
||||
"app.action-bar.downloads": {
|
||||
"message": "Descargas"
|
||||
},
|
||||
"app.action-bar.hide-more-running-instances": {
|
||||
"message": "Ocultar más instancias en ejecución"
|
||||
},
|
||||
"app.action-bar.make-primary-instance": {
|
||||
"message": "Hacer instancia principal"
|
||||
},
|
||||
"app.action-bar.no-instances-running": {
|
||||
"message": "Ninguna instancia en ejecución"
|
||||
},
|
||||
"app.action-bar.offline": {
|
||||
"message": "Sin conexión"
|
||||
},
|
||||
"app.action-bar.primary-instance": {
|
||||
"message": "Instancia principal"
|
||||
},
|
||||
"app.action-bar.show-more-running-instances": {
|
||||
"message": "Mostrar mas instancias en ejecución"
|
||||
},
|
||||
"app.action-bar.stop-instance": {
|
||||
"message": "Finalizar instancia"
|
||||
},
|
||||
"app.action-bar.view-active-downloads": {
|
||||
"message": "Mostrar descargas activas"
|
||||
},
|
||||
"app.action-bar.view-instance": {
|
||||
"message": "Mostrar instancia"
|
||||
},
|
||||
"app.action-bar.view-logs": {
|
||||
"message": "Mostrar registros"
|
||||
},
|
||||
"app.appearance-settings.advanced-rendering.description": {
|
||||
"message": "Habilita el renderizado avanzado, como los efectos de desenfoque, que pueden causar problemas de rendimiento sin renderizado acelerado por hardware."
|
||||
},
|
||||
"app.appearance-settings.advanced-rendering.title": {
|
||||
"message": "Renderizado avanzado"
|
||||
},
|
||||
"app.appearance-settings.color-theme.description": {
|
||||
"message": "Seleccione su tema de color preferido para Modrinth en este dispositivo."
|
||||
},
|
||||
"app.appearance-settings.color-theme.title": {
|
||||
"message": "Tema de color"
|
||||
},
|
||||
"app.appearance-settings.default-landing-page.description": {
|
||||
"message": "Cambia la página en la que el launcher se abre en."
|
||||
},
|
||||
"app.appearance-settings.default-landing-page.home": {
|
||||
"message": "Inicio"
|
||||
},
|
||||
"app.appearance-settings.default-landing-page.library": {
|
||||
"message": "Librería"
|
||||
},
|
||||
"app.appearance-settings.default-landing-page.title": {
|
||||
"message": "Página predeterminada"
|
||||
},
|
||||
"app.appearance-settings.hide-nametag.description": {
|
||||
"message": "Desactiva la etiqueta arriba de tu personaje en la página de skins."
|
||||
},
|
||||
"app.appearance-settings.hide-nametag.title": {
|
||||
"message": "Ocultar etiqueta"
|
||||
},
|
||||
"app.appearance-settings.jump-back-into-worlds.description": {
|
||||
"message": "Incluye los mundos más recientes en la sección \"Vuelve a jugar tus mundos\" en la página de inicio."
|
||||
},
|
||||
"app.appearance-settings.jump-back-into-worlds.title": {
|
||||
"message": "Vuelve a jugar tus mundos"
|
||||
},
|
||||
"app.appearance-settings.minimize-launcher.description": {
|
||||
"message": "Minimiza el launcher cuando un proceso de Minecraft empieza."
|
||||
},
|
||||
"app.appearance-settings.minimize-launcher.title": {
|
||||
"message": "Minimiza el launcher"
|
||||
},
|
||||
"app.appearance-settings.native-decorations.description": {
|
||||
"message": "Usa los sistemas de frame de Windows (se requiere resetear la aplicación)."
|
||||
},
|
||||
"app.appearance-settings.native-decorations.title": {
|
||||
"message": "Decoraciones nativas"
|
||||
},
|
||||
"app.appearance-settings.select-option": {
|
||||
"message": "Selecciona una opción"
|
||||
},
|
||||
"app.appearance-settings.toggle-sidebar.description": {
|
||||
"message": "Activa la posibilidad de alternar la barra lateral."
|
||||
},
|
||||
"app.appearance-settings.toggle-sidebar.title": {
|
||||
"message": "Alternar barra lateral"
|
||||
},
|
||||
"app.appearance-settings.unknown-pack-warning.description": {
|
||||
"message": "Si intentas instalar un archivo de paquete de Modrinth (.mrpack) que no esté alojado en Modrinth, te advertiremos de los riesgos antes de instalarlo."
|
||||
},
|
||||
"app.appearance-settings.unknown-pack-warning.title": {
|
||||
"message": "Adviérteme antes de instalarme modpacks desconocidos"
|
||||
},
|
||||
"app.auth-servers.unreachable.body": {
|
||||
"message": "Los servidores de autenticación de Minecraft pueden no estar funcionando en este momento. Verifica tu conexión a internet e inténtalo de nuevo más tarde."
|
||||
},
|
||||
@@ -30,7 +129,7 @@
|
||||
"message": "Descubrir servidores"
|
||||
},
|
||||
"app.browse.hide-added-servers": {
|
||||
"message": "Ocultar servidores añadidos"
|
||||
"message": "Ocultar servidores ya añadidos"
|
||||
},
|
||||
"app.browse.hide-installed-content": {
|
||||
"message": "Ocultar contenido instalado"
|
||||
@@ -38,6 +137,24 @@
|
||||
"app.browse.install-content-to-instance": {
|
||||
"message": "Instalar contenido a una instancia"
|
||||
},
|
||||
"app.browse.project-type.modpacks": {
|
||||
"message": "Modpacks"
|
||||
},
|
||||
"app.browse.server.install": {
|
||||
"message": "Instalar"
|
||||
},
|
||||
"app.browse.server.installed": {
|
||||
"message": "Instalado"
|
||||
},
|
||||
"app.browse.server.installing": {
|
||||
"message": "Instalando"
|
||||
},
|
||||
"app.creation-modal.installing-modpack.description": {
|
||||
"message": "{fileName}"
|
||||
},
|
||||
"app.creation-modal.installing-modpack.title": {
|
||||
"message": "Instalando el modpack..."
|
||||
},
|
||||
"app.export-modal.description-placeholder": {
|
||||
"message": "Escribe la descripción del modpack..."
|
||||
},
|
||||
@@ -165,7 +282,7 @@
|
||||
"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."
|
||||
"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"
|
||||
@@ -598,5 +715,26 @@
|
||||
},
|
||||
"search.filter.locked.server-loader.title": {
|
||||
"message": "El cargador lo proporciona el servidor"
|
||||
},
|
||||
"unknown-pack-warning-modal.body": {
|
||||
"message": "Un archivo solo es revisado si es subido a Modrinth, independientemente de su formato de archivo (incluyendo .mrpack)."
|
||||
},
|
||||
"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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,12 +29,6 @@
|
||||
"app.browse.discover-servers": {
|
||||
"message": "Löydä palvelimia"
|
||||
},
|
||||
"app.browse.hide-added-servers": {
|
||||
"message": "Piilota lisätyt palvelimet"
|
||||
},
|
||||
"app.browse.hide-installed-content": {
|
||||
"message": "Piilota asennettu sisältö"
|
||||
},
|
||||
"app.browse.install-content-to-instance": {
|
||||
"message": "Lataa sisältöä instanssiin"
|
||||
},
|
||||
|
||||
@@ -29,12 +29,6 @@
|
||||
"app.browse.discover-servers": {
|
||||
"message": "Tumuklas ng mga server"
|
||||
},
|
||||
"app.browse.hide-added-servers": {
|
||||
"message": "Taguin ang mga nadagdag na server"
|
||||
},
|
||||
"app.browse.hide-installed-content": {
|
||||
"message": "Taguin ang mga na-install na kontento"
|
||||
},
|
||||
"app.browse.install-content-to-instance": {
|
||||
"message": "I-install ang kontento sa instansiya"
|
||||
},
|
||||
|
||||
@@ -1,4 +1,103 @@
|
||||
{
|
||||
"app.action-bar.downloading-java": {
|
||||
"message": "Java version {version} en cours de téléchargement"
|
||||
},
|
||||
"app.action-bar.downloads": {
|
||||
"message": "Téléchargements"
|
||||
},
|
||||
"app.action-bar.hide-more-running-instances": {
|
||||
"message": "Masquer plus d'instances en exécution"
|
||||
},
|
||||
"app.action-bar.make-primary-instance": {
|
||||
"message": "Définir en tante qu'instance première"
|
||||
},
|
||||
"app.action-bar.no-instances-running": {
|
||||
"message": "Aucune instance en exécution"
|
||||
},
|
||||
"app.action-bar.offline": {
|
||||
"message": "Hors ligne"
|
||||
},
|
||||
"app.action-bar.primary-instance": {
|
||||
"message": "Instance première"
|
||||
},
|
||||
"app.action-bar.show-more-running-instances": {
|
||||
"message": "Montrer plus d'instances en exécution"
|
||||
},
|
||||
"app.action-bar.stop-instance": {
|
||||
"message": "Arrêter l'instance"
|
||||
},
|
||||
"app.action-bar.view-active-downloads": {
|
||||
"message": "Voir les téléchargements actifs"
|
||||
},
|
||||
"app.action-bar.view-instance": {
|
||||
"message": "Voir l'instance"
|
||||
},
|
||||
"app.action-bar.view-logs": {
|
||||
"message": "Voir les journaux"
|
||||
},
|
||||
"app.appearance-settings.advanced-rendering.description": {
|
||||
"message": "Permet un rendu avancé tel que des effets de flou qui peuvent causer des problèmes de performance sans rendu accéléré par le matériel."
|
||||
},
|
||||
"app.appearance-settings.advanced-rendering.title": {
|
||||
"message": "Rendu avancé"
|
||||
},
|
||||
"app.appearance-settings.color-theme.description": {
|
||||
"message": "Sélectionnez votre couleur préférée pour le thème de Modrinth App."
|
||||
},
|
||||
"app.appearance-settings.color-theme.title": {
|
||||
"message": "Thème couleur"
|
||||
},
|
||||
"app.appearance-settings.default-landing-page.description": {
|
||||
"message": "Modifier la page sur laquelle le lancheur s'ouvre."
|
||||
},
|
||||
"app.appearance-settings.default-landing-page.home": {
|
||||
"message": "Acceuil"
|
||||
},
|
||||
"app.appearance-settings.default-landing-page.library": {
|
||||
"message": "Bibliothèque"
|
||||
},
|
||||
"app.appearance-settings.default-landing-page.title": {
|
||||
"message": "Page d'ouverture"
|
||||
},
|
||||
"app.appearance-settings.hide-nametag.description": {
|
||||
"message": "Désactive le nom au-dessus du joueur sur la page skins."
|
||||
},
|
||||
"app.appearance-settings.hide-nametag.title": {
|
||||
"message": "Cacher le pseudo"
|
||||
},
|
||||
"app.appearance-settings.jump-back-into-worlds.description": {
|
||||
"message": "Inclut les mondes récents dans la section « Partie rapide » sur la page d'accueil."
|
||||
},
|
||||
"app.appearance-settings.jump-back-into-worlds.title": {
|
||||
"message": "Mondes sur partie rapide"
|
||||
},
|
||||
"app.appearance-settings.minimize-launcher.description": {
|
||||
"message": "Minimiser le launcher quand Minecraft démarre."
|
||||
},
|
||||
"app.appearance-settings.minimize-launcher.title": {
|
||||
"message": "Minimiser le launcher"
|
||||
},
|
||||
"app.appearance-settings.native-decorations.description": {
|
||||
"message": "Utiliser le cadre fenêtre du système (redémarrage de l'application requis)."
|
||||
},
|
||||
"app.appearance-settings.native-decorations.title": {
|
||||
"message": "Décorations natives"
|
||||
},
|
||||
"app.appearance-settings.select-option": {
|
||||
"message": "Sélectionnez une option"
|
||||
},
|
||||
"app.appearance-settings.toggle-sidebar.description": {
|
||||
"message": "Permet d'ouvrir de de fermer la barre latérale."
|
||||
},
|
||||
"app.appearance-settings.toggle-sidebar.title": {
|
||||
"message": "Tiroir latéral"
|
||||
},
|
||||
"app.appearance-settings.unknown-pack-warning.description": {
|
||||
"message": "Si vous essayez d'installer un fichier Modrinth Pack (.mrpack) qui n'est pas hébergé sur Modrinth, nous nous assurerons que vous comprenez les risques avant de l'installer."
|
||||
},
|
||||
"app.appearance-settings.unknown-pack-warning.title": {
|
||||
"message": "M'avertir avant d'installer des modpacks inconnus"
|
||||
},
|
||||
"app.auth-servers.unreachable.body": {
|
||||
"message": "Les serveurs d'authentification de Minecraft sont peut-être actuellement hors ligne. Vérifiez votre connexion Internet et essayez à nouveau dans quelques instants."
|
||||
},
|
||||
@@ -30,14 +129,32 @@
|
||||
"message": "Découvrir des serveurs"
|
||||
},
|
||||
"app.browse.hide-added-servers": {
|
||||
"message": "Masquer les serveurs ajoutés"
|
||||
"message": "Masquer les serveurs déjà ajoutés"
|
||||
},
|
||||
"app.browse.hide-installed-content": {
|
||||
"message": "Masquer le contenu installé"
|
||||
"message": "Masquer le contenu déjà ajouté"
|
||||
},
|
||||
"app.browse.install-content-to-instance": {
|
||||
"message": "Installer du contenu à l'instance"
|
||||
},
|
||||
"app.browse.project-type.modpacks": {
|
||||
"message": "Modpacks"
|
||||
},
|
||||
"app.browse.server.install": {
|
||||
"message": "Installer"
|
||||
},
|
||||
"app.browse.server.installed": {
|
||||
"message": "Installé"
|
||||
},
|
||||
"app.browse.server.installing": {
|
||||
"message": "Installation en cours"
|
||||
},
|
||||
"app.creation-modal.installing-modpack.description": {
|
||||
"message": "{fileName}"
|
||||
},
|
||||
"app.creation-modal.installing-modpack.title": {
|
||||
"message": "Installation du modpack..."
|
||||
},
|
||||
"app.export-modal.description-placeholder": {
|
||||
"message": "Saisir la description du modpack..."
|
||||
},
|
||||
@@ -575,6 +692,24 @@
|
||||
"instance.worlds.world_in_use": {
|
||||
"message": "Le monde en cours d'utilisation"
|
||||
},
|
||||
"minecraft-account.add-account": {
|
||||
"message": "Ajouter un compte"
|
||||
},
|
||||
"minecraft-account.label": {
|
||||
"message": "Compte Minecraft"
|
||||
},
|
||||
"minecraft-account.not-signed-in": {
|
||||
"message": "Pas connecté"
|
||||
},
|
||||
"minecraft-account.remove-account": {
|
||||
"message": "Retirer le compte"
|
||||
},
|
||||
"minecraft-account.select-account": {
|
||||
"message": "Sélectionner un compte"
|
||||
},
|
||||
"minecraft-account.sign-in": {
|
||||
"message": "Se connecter à Minecraft"
|
||||
},
|
||||
"search.filter.locked.instance": {
|
||||
"message": "Procuré par l'instance"
|
||||
},
|
||||
@@ -598,5 +733,26 @@
|
||||
},
|
||||
"search.filter.locked.server-loader.title": {
|
||||
"message": "Le loader est procuré par le serveur"
|
||||
},
|
||||
"unknown-pack-warning-modal.body": {
|
||||
"message": "Un fichier n’est révisé que s’il 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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,12 +29,6 @@
|
||||
"app.browse.discover-servers": {
|
||||
"message": "גלה שרתים"
|
||||
},
|
||||
"app.browse.hide-added-servers": {
|
||||
"message": "הסתר שרתים שנוספו"
|
||||
},
|
||||
"app.browse.hide-installed-content": {
|
||||
"message": "הסתר תוכן מותקן"
|
||||
},
|
||||
"app.browse.install-content-to-instance": {
|
||||
"message": "הוספת התוכן להתקנה"
|
||||
},
|
||||
|
||||
@@ -1,4 +1,25 @@
|
||||
{
|
||||
"app.action-bar.downloading-java": {
|
||||
"message": "A Java {version} letöltése"
|
||||
},
|
||||
"app.action-bar.downloads": {
|
||||
"message": "Letöltések"
|
||||
},
|
||||
"app.action-bar.hide-more-running-instances": {
|
||||
"message": "További futó példányok elrejtése"
|
||||
},
|
||||
"app.action-bar.make-primary-instance": {
|
||||
"message": "Elsődleges példány létrehozása"
|
||||
},
|
||||
"app.action-bar.no-instances-running": {
|
||||
"message": "Nincsenek futó példányok"
|
||||
},
|
||||
"app.action-bar.offline": {
|
||||
"message": "Offline"
|
||||
},
|
||||
"app.action-bar.primary-instance": {
|
||||
"message": "Elsődleges példány"
|
||||
},
|
||||
"app.auth-servers.unreachable.body": {
|
||||
"message": "A Minecraft hitelesítő szerverek lehet, hogy nem üzemelnek. Bizonyosodj meg róla, hogy van internetkapcsolatod és próbáld meg újra."
|
||||
},
|
||||
@@ -30,14 +51,26 @@
|
||||
"message": "Szerverek böngészése"
|
||||
},
|
||||
"app.browse.hide-added-servers": {
|
||||
"message": "Hozzáadott szerverek elrejtése"
|
||||
"message": "Már hozzáadott szerverek elrejtése"
|
||||
},
|
||||
"app.browse.hide-installed-content": {
|
||||
"message": "A telepített tartalmak elrejtése"
|
||||
"message": "Már telepített tartalom elrejtése"
|
||||
},
|
||||
"app.browse.install-content-to-instance": {
|
||||
"message": "Tartalom letöltése a profilhoz"
|
||||
},
|
||||
"app.browse.project-type.modpacks": {
|
||||
"message": "Modcsomagok"
|
||||
},
|
||||
"app.browse.server.install": {
|
||||
"message": "Letöltés"
|
||||
},
|
||||
"app.browse.server.installed": {
|
||||
"message": "Letöltve"
|
||||
},
|
||||
"app.browse.server.installing": {
|
||||
"message": "Letöltés..."
|
||||
},
|
||||
"app.export-modal.description-placeholder": {
|
||||
"message": "Írd be a modcsomag leírását..."
|
||||
},
|
||||
@@ -210,10 +243,10 @@
|
||||
"message": "Erőforráskezelés"
|
||||
},
|
||||
"app.update-popup.body": {
|
||||
"message": "A Modrinth App v{version} telepítésre kész! Frissítéshez Töltsd újra az oldalt, vagy a Modrinth App bezárásakor automatikusan frissül."
|
||||
"message": "A Modrinth App v{version} telepítésre kész! Frissítéshez válaszd ki a Frissítés opciót, vagy az alkalmazás bezárásakor automatikusan frissül."
|
||||
},
|
||||
"app.update-popup.body.download-complete": {
|
||||
"message": "A Modrinth App v{version} letöltése befejeződött. Frissítéshez Töltsd újra az oldalt, vagy a Modrinth App bezárásakor automatikusan frissül."
|
||||
"message": "A Modrinth App v{version} letöltése befejeződött. Frissítéshez válaszd ki a Frissítés opciót, vagy az alkalmazás bezárásakor automatikusan frissül."
|
||||
},
|
||||
"app.update-popup.body.linux": {
|
||||
"message": "A Modrinth App v{version} elérhető. Használd a csomagkezelőt a legújabb funkciók és javítások frissítéséhez!"
|
||||
@@ -231,7 +264,7 @@
|
||||
"message": "Sikeres letöltés"
|
||||
},
|
||||
"app.update-popup.reload": {
|
||||
"message": "Újratöltés"
|
||||
"message": "Frissítés"
|
||||
},
|
||||
"app.update-popup.title": {
|
||||
"message": "Frissítés elérhető"
|
||||
@@ -555,7 +588,7 @@
|
||||
"message": "A szerver nem kompatibilis"
|
||||
},
|
||||
"instance.worlds.linked_server": {
|
||||
"message": "Szerverprojekt által kezelt"
|
||||
"message": "A szerver projektje által kezelt"
|
||||
},
|
||||
"instance.worlds.no_contact": {
|
||||
"message": "A szerverrel nem lehet kapcsolatot létesíteni"
|
||||
|
||||
@@ -1,4 +1,28 @@
|
||||
{
|
||||
"app.action-bar.downloading-java": {
|
||||
"message": "Mengunduh Java {version}"
|
||||
},
|
||||
"app.action-bar.downloads": {
|
||||
"message": "Mengunduh"
|
||||
},
|
||||
"app.action-bar.hide-more-running-instances": {
|
||||
"message": "Sembunyikan lebih banyak instance yang sedang berjalan"
|
||||
},
|
||||
"app.action-bar.make-primary-instance": {
|
||||
"message": "Jadikan sebagai instance utama"
|
||||
},
|
||||
"app.action-bar.no-instances-running": {
|
||||
"message": "Tidak ada instance yang sedang berjalan"
|
||||
},
|
||||
"app.action-bar.offline": {
|
||||
"message": "Offline"
|
||||
},
|
||||
"app.action-bar.primary-instance": {
|
||||
"message": "Instance utama"
|
||||
},
|
||||
"app.action-bar.show-more-running-instances": {
|
||||
"message": "Tampilkan lebih banyak instance yang sedang berjalan"
|
||||
},
|
||||
"app.auth-servers.unreachable.body": {
|
||||
"message": "Server autentikasi Minecraft mungkin sedang tidak tersedia saat ini. Periksa koneksi internet Anda dan coba lagi nanti."
|
||||
},
|
||||
@@ -29,12 +53,6 @@
|
||||
"app.browse.discover-servers": {
|
||||
"message": "Temukan server"
|
||||
},
|
||||
"app.browse.hide-added-servers": {
|
||||
"message": "Sembuyikan server tertambah"
|
||||
},
|
||||
"app.browse.hide-installed-content": {
|
||||
"message": "Sembunyikan konten terpasang"
|
||||
},
|
||||
"app.browse.install-content-to-instance": {
|
||||
"message": "Pasang konten ke instans"
|
||||
},
|
||||
|
||||
@@ -1,4 +1,103 @@
|
||||
{
|
||||
"app.action-bar.downloading-java": {
|
||||
"message": "Scaricando Java {version}"
|
||||
},
|
||||
"app.action-bar.downloads": {
|
||||
"message": "Download"
|
||||
},
|
||||
"app.action-bar.hide-more-running-instances": {
|
||||
"message": "Nascondi le istanze in esecuzione"
|
||||
},
|
||||
"app.action-bar.make-primary-instance": {
|
||||
"message": "Rendi primaria"
|
||||
},
|
||||
"app.action-bar.no-instances-running": {
|
||||
"message": "Nessuna istanza in esecuzione"
|
||||
},
|
||||
"app.action-bar.offline": {
|
||||
"message": "Offline"
|
||||
},
|
||||
"app.action-bar.primary-instance": {
|
||||
"message": "Primaria"
|
||||
},
|
||||
"app.action-bar.show-more-running-instances": {
|
||||
"message": "Mostra le istanze in esecuzione"
|
||||
},
|
||||
"app.action-bar.stop-instance": {
|
||||
"message": "Ferma l'istanza"
|
||||
},
|
||||
"app.action-bar.view-active-downloads": {
|
||||
"message": "Vedi i download attivi"
|
||||
},
|
||||
"app.action-bar.view-instance": {
|
||||
"message": "Vedi l'istanza"
|
||||
},
|
||||
"app.action-bar.view-logs": {
|
||||
"message": "Vedi i log"
|
||||
},
|
||||
"app.appearance-settings.advanced-rendering.description": {
|
||||
"message": "Abilita alcuni effetti come la sfocatura, ma può causare problemi di prestazioni senza accelerazione hardware."
|
||||
},
|
||||
"app.appearance-settings.advanced-rendering.title": {
|
||||
"message": "Rendering avanzato"
|
||||
},
|
||||
"app.appearance-settings.color-theme.description": {
|
||||
"message": "Scegli il tema che Modrinth App userà su questo dispositivo."
|
||||
},
|
||||
"app.appearance-settings.color-theme.title": {
|
||||
"message": "Tema"
|
||||
},
|
||||
"app.appearance-settings.default-landing-page.description": {
|
||||
"message": "Imposta la pagina che si aprirà all'avvio del launcher."
|
||||
},
|
||||
"app.appearance-settings.default-landing-page.home": {
|
||||
"message": "Home"
|
||||
},
|
||||
"app.appearance-settings.default-landing-page.library": {
|
||||
"message": "Libreria"
|
||||
},
|
||||
"app.appearance-settings.default-landing-page.title": {
|
||||
"message": "Pagina di avvio"
|
||||
},
|
||||
"app.appearance-settings.hide-nametag.description": {
|
||||
"message": "Rimuove il nametag sopra la testa del giocatore nella pagina delle skin."
|
||||
},
|
||||
"app.appearance-settings.hide-nametag.title": {
|
||||
"message": "Nascondi nametag"
|
||||
},
|
||||
"app.appearance-settings.jump-back-into-worlds.description": {
|
||||
"message": "Aggiunge i mondi recenti nella sezione \"Avvio rapido\" della pagina Home."
|
||||
},
|
||||
"app.appearance-settings.jump-back-into-worlds.title": {
|
||||
"message": "Avvio rapido nei mondi"
|
||||
},
|
||||
"app.appearance-settings.minimize-launcher.description": {
|
||||
"message": "Riduci il launcher quanto si avvia Minecraft."
|
||||
},
|
||||
"app.appearance-settings.minimize-launcher.title": {
|
||||
"message": "Riduci il launcher"
|
||||
},
|
||||
"app.appearance-settings.native-decorations.description": {
|
||||
"message": "Usa la cornice di sistema (riavvio richiesto)."
|
||||
},
|
||||
"app.appearance-settings.native-decorations.title": {
|
||||
"message": "Decorazioni native"
|
||||
},
|
||||
"app.appearance-settings.select-option": {
|
||||
"message": "Seleziona un'opzione"
|
||||
},
|
||||
"app.appearance-settings.toggle-sidebar.description": {
|
||||
"message": "Scegli se mostrare o nascondere la barra laterale."
|
||||
},
|
||||
"app.appearance-settings.toggle-sidebar.title": {
|
||||
"message": "Abilita la barra laterale"
|
||||
},
|
||||
"app.appearance-settings.unknown-pack-warning.description": {
|
||||
"message": "Apparirà un avviso prima di installare un file Modrinth Pack (.mrpack) non proveniente da Modrinth, così da conoscerne i rischi."
|
||||
},
|
||||
"app.appearance-settings.unknown-pack-warning.title": {
|
||||
"message": "Avvisami prima di installare pacchetti sconosciuti"
|
||||
},
|
||||
"app.auth-servers.unreachable.body": {
|
||||
"message": "I server di autenticazione di Minecraft stanno riscontrando problemi. Controlla la tua connessione a Internet e riprova più tardi."
|
||||
},
|
||||
@@ -30,14 +129,32 @@
|
||||
"message": "Esplora i server"
|
||||
},
|
||||
"app.browse.hide-added-servers": {
|
||||
"message": "Nascondi server aggiunti"
|
||||
"message": "Nascondi server già aggiunti"
|
||||
},
|
||||
"app.browse.hide-installed-content": {
|
||||
"message": "Nascondi contenuti installati"
|
||||
"message": "Nascondi contenuti già installati"
|
||||
},
|
||||
"app.browse.install-content-to-instance": {
|
||||
"message": "Installa contenuti nell'istanza"
|
||||
},
|
||||
"app.browse.project-type.modpacks": {
|
||||
"message": "Pacchetti di mod"
|
||||
},
|
||||
"app.browse.server.install": {
|
||||
"message": "Installa"
|
||||
},
|
||||
"app.browse.server.installed": {
|
||||
"message": "Installato"
|
||||
},
|
||||
"app.browse.server.installing": {
|
||||
"message": "Installando"
|
||||
},
|
||||
"app.creation-modal.installing-modpack.description": {
|
||||
"message": "{fileName}"
|
||||
},
|
||||
"app.creation-modal.installing-modpack.title": {
|
||||
"message": "Installando il pacchetto..."
|
||||
},
|
||||
"app.export-modal.description-placeholder": {
|
||||
"message": "Inserisci descrizione del pacchetto..."
|
||||
},
|
||||
@@ -575,6 +692,24 @@
|
||||
"instance.worlds.world_in_use": {
|
||||
"message": "Mondo già in uso"
|
||||
},
|
||||
"minecraft-account.add-account": {
|
||||
"message": "Aggiungi account"
|
||||
},
|
||||
"minecraft-account.label": {
|
||||
"message": "Account di Minecraft"
|
||||
},
|
||||
"minecraft-account.not-signed-in": {
|
||||
"message": "Accesso non effettuato"
|
||||
},
|
||||
"minecraft-account.remove-account": {
|
||||
"message": "Rimuovi account"
|
||||
},
|
||||
"minecraft-account.select-account": {
|
||||
"message": "Seleziona un account"
|
||||
},
|
||||
"minecraft-account.sign-in": {
|
||||
"message": "Accedi a Minecraft"
|
||||
},
|
||||
"search.filter.locked.instance": {
|
||||
"message": "Determinato dall'istanza"
|
||||
},
|
||||
@@ -598,5 +733,26 @@
|
||||
},
|
||||
"search.filter.locked.server-loader.title": {
|
||||
"message": "Il loader è determinato dal server"
|
||||
},
|
||||
"unknown-pack-warning-modal.body": {
|
||||
"message": "Un file è esaminato quando è caricato su Modrinth, indipendentemente dal suo 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": "Molto spesso i malware sono distribuiti attraverso i pacchetti di mod, distribuiti in piattaforme come Discord."
|
||||
},
|
||||
"unknown-pack-warning-modal.warning.body": {
|
||||
"message": "Non siamo riusciti a trovare questo file su Modrinth. Consigliamo di installare file solo da fonti di cui ti fidi."
|
||||
},
|
||||
"unknown-pack-warning-modal.warning.title": {
|
||||
"message": "Tipo di file sconosciuto"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,103 @@
|
||||
{
|
||||
"app.action-bar.downloading-java": {
|
||||
"message": "Java {version} をダウンロード中"
|
||||
},
|
||||
"app.action-bar.downloads": {
|
||||
"message": "ダウンロード"
|
||||
},
|
||||
"app.action-bar.hide-more-running-instances": {
|
||||
"message": "実行中のインスタンスの一部を非表示"
|
||||
},
|
||||
"app.action-bar.make-primary-instance": {
|
||||
"message": "デフォルトのインスタンスを作成"
|
||||
},
|
||||
"app.action-bar.no-instances-running": {
|
||||
"message": "実行中のインスタンスはありません"
|
||||
},
|
||||
"app.action-bar.offline": {
|
||||
"message": "オフライン"
|
||||
},
|
||||
"app.action-bar.primary-instance": {
|
||||
"message": "デフォルトのインスタンス"
|
||||
},
|
||||
"app.action-bar.show-more-running-instances": {
|
||||
"message": "実行中のインスタンスをさらに表示"
|
||||
},
|
||||
"app.action-bar.stop-instance": {
|
||||
"message": "インスタンスを停止"
|
||||
},
|
||||
"app.action-bar.view-active-downloads": {
|
||||
"message": "進行中のダウンロードを表示"
|
||||
},
|
||||
"app.action-bar.view-instance": {
|
||||
"message": "インスタンスを表示"
|
||||
},
|
||||
"app.action-bar.view-logs": {
|
||||
"message": "ログを表示"
|
||||
},
|
||||
"app.appearance-settings.advanced-rendering.description": {
|
||||
"message": "ぼかし効果などの高度なレンダリングを有効にします。グラフィックアクセラレーションが使用できない場合は、パフォーマンスが低下する可能性があります。"
|
||||
},
|
||||
"app.appearance-settings.advanced-rendering.title": {
|
||||
"message": "高度なレンダリング"
|
||||
},
|
||||
"app.appearance-settings.color-theme.description": {
|
||||
"message": "Modrinth Appでのお好みのテーマを選択してください"
|
||||
},
|
||||
"app.appearance-settings.color-theme.title": {
|
||||
"message": "テーマ"
|
||||
},
|
||||
"app.appearance-settings.default-landing-page.description": {
|
||||
"message": "ランチャー起動時に表示するページを変更します"
|
||||
},
|
||||
"app.appearance-settings.default-landing-page.home": {
|
||||
"message": "ホーム"
|
||||
},
|
||||
"app.appearance-settings.default-landing-page.library": {
|
||||
"message": "ライブラリ"
|
||||
},
|
||||
"app.appearance-settings.default-landing-page.title": {
|
||||
"message": "デフォルトの起動ページ"
|
||||
},
|
||||
"app.appearance-settings.hide-nametag.description": {
|
||||
"message": "スキンページで、プレイヤー名の上にある名前タグを無効にします。"
|
||||
},
|
||||
"app.appearance-settings.hide-nametag.title": {
|
||||
"message": "名札を非表示にする"
|
||||
},
|
||||
"app.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": "OSデフォルトのウィンドウUIを使用します(アプリの再起動が必要です)"
|
||||
},
|
||||
"app.appearance-settings.native-decorations.title": {
|
||||
"message": "OSのウィンドウUIを使用"
|
||||
},
|
||||
"app.appearance-settings.select-option": {
|
||||
"message": "オプションを選択してください"
|
||||
},
|
||||
"app.appearance-settings.toggle-sidebar.description": {
|
||||
"message": "サイドバーの表示/非表示を切り替える機能を有効にします。"
|
||||
},
|
||||
"app.appearance-settings.toggle-sidebar.title": {
|
||||
"message": "サイドバーの表示/非表示を切り替える"
|
||||
},
|
||||
"app.appearance-settings.unknown-pack-warning.description": {
|
||||
"message": "Modrinth上にホストされていないModrinth Packファイル(.mrpack)をインストールしようとした場合、インストール前にそのリスクを理解していただけるよう確認します。"
|
||||
},
|
||||
"app.appearance-settings.unknown-pack-warning.title": {
|
||||
"message": "未知のMODパックをインストールする前に警告してください"
|
||||
},
|
||||
"app.auth-servers.unreachable.body": {
|
||||
"message": "Minecraftの認証サーバーは現在停止している可能性があります。インターネット接続を確認し、しばらくしてからもう一度お試しください。"
|
||||
},
|
||||
@@ -30,14 +129,32 @@
|
||||
"message": "サーバーを探す"
|
||||
},
|
||||
"app.browse.hide-added-servers": {
|
||||
"message": "追加済みのサーバーを隠す"
|
||||
"message": "既に追加済みのサーバーを非表示にする"
|
||||
},
|
||||
"app.browse.hide-installed-content": {
|
||||
"message": "インストール済みのコンテンツを隠す"
|
||||
"message": "既にインストールされているコンテンツを非表示にする"
|
||||
},
|
||||
"app.browse.install-content-to-instance": {
|
||||
"message": "コンテンツをインスタンスにインストールする"
|
||||
},
|
||||
"app.browse.project-type.modpacks": {
|
||||
"message": "Modパック"
|
||||
},
|
||||
"app.browse.server.install": {
|
||||
"message": "インストール"
|
||||
},
|
||||
"app.browse.server.installed": {
|
||||
"message": "インストール済み"
|
||||
},
|
||||
"app.browse.server.installing": {
|
||||
"message": "インストール中"
|
||||
},
|
||||
"app.creation-modal.installing-modpack.description": {
|
||||
"message": "{fileName}"
|
||||
},
|
||||
"app.creation-modal.installing-modpack.title": {
|
||||
"message": "Modパックをインストール中..."
|
||||
},
|
||||
"app.export-modal.description-placeholder": {
|
||||
"message": "Modパックの説明を入力…"
|
||||
},
|
||||
@@ -598,5 +715,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": "不明なファイルの警告"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,103 @@
|
||||
{
|
||||
"app.action-bar.downloading-java": {
|
||||
"message": "Java {version} 다운로드 중"
|
||||
},
|
||||
"app.action-bar.downloads": {
|
||||
"message": "다운로드"
|
||||
},
|
||||
"app.action-bar.hide-more-running-instances": {
|
||||
"message": "실행 중인 인스턴스 숨기기"
|
||||
},
|
||||
"app.action-bar.make-primary-instance": {
|
||||
"message": "기본 인스턴스로 지정"
|
||||
},
|
||||
"app.action-bar.no-instances-running": {
|
||||
"message": "실행 중인 인스턴스 없음"
|
||||
},
|
||||
"app.action-bar.offline": {
|
||||
"message": "오프라인"
|
||||
},
|
||||
"app.action-bar.primary-instance": {
|
||||
"message": "기본 인스턴스"
|
||||
},
|
||||
"app.action-bar.show-more-running-instances": {
|
||||
"message": "실행 중인 인스턴스 보이기"
|
||||
},
|
||||
"app.action-bar.stop-instance": {
|
||||
"message": "인스턴스 중지"
|
||||
},
|
||||
"app.action-bar.view-active-downloads": {
|
||||
"message": "다운로드 목록 보기"
|
||||
},
|
||||
"app.action-bar.view-instance": {
|
||||
"message": "인스턴스 보기"
|
||||
},
|
||||
"app.action-bar.view-logs": {
|
||||
"message": "로그 보기"
|
||||
},
|
||||
"app.appearance-settings.advanced-rendering.description": {
|
||||
"message": "블러 효과와 같은 고급 렌더링 기능을 활성화합니다. 하드웨어 가속 없이는 성능 저하가 발생할 수 있습니다."
|
||||
},
|
||||
"app.appearance-settings.advanced-rendering.title": {
|
||||
"message": "고급 렌더링"
|
||||
},
|
||||
"app.appearance-settings.color-theme.description": {
|
||||
"message": "Modrinth App에서 선호하는 색상 테마를 선택합니다."
|
||||
},
|
||||
"app.appearance-settings.color-theme.title": {
|
||||
"message": "색상 테마"
|
||||
},
|
||||
"app.appearance-settings.default-landing-page.description": {
|
||||
"message": "런처의 기본 시작 화면을 변경합니다."
|
||||
},
|
||||
"app.appearance-settings.default-landing-page.home": {
|
||||
"message": "홈"
|
||||
},
|
||||
"app.appearance-settings.default-landing-page.library": {
|
||||
"message": "라이브러리"
|
||||
},
|
||||
"app.appearance-settings.default-landing-page.title": {
|
||||
"message": "기본 시작 화면"
|
||||
},
|
||||
"app.appearance-settings.hide-nametag.description": {
|
||||
"message": "스킨 화면에서 플레이어 위에 있는 이름표를 비활성화합니다."
|
||||
},
|
||||
"app.appearance-settings.hide-nametag.title": {
|
||||
"message": "이름표 숨기기"
|
||||
},
|
||||
"app.appearance-settings.jump-back-into-worlds.description": {
|
||||
"message": "홈 화면의 \"바로 입장\" 부분에 최근 플레이한 월드를 표시합니다."
|
||||
},
|
||||
"app.appearance-settings.jump-back-into-worlds.title": {
|
||||
"message": "세계로 바로 입장"
|
||||
},
|
||||
"app.appearance-settings.minimize-launcher.description": {
|
||||
"message": "마인크래프트 실행 시 런처를 최소화합니다."
|
||||
},
|
||||
"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.toggle-sidebar.description": {
|
||||
"message": "사이드바를 접거나 펼칠 수 있는 기능을 활성화합니다."
|
||||
},
|
||||
"app.appearance-settings.toggle-sidebar.title": {
|
||||
"message": "사이드바 토글"
|
||||
},
|
||||
"app.appearance-settings.unknown-pack-warning.description": {
|
||||
"message": "Modrinth 외부에서 가져온 모드팩 파일(.mrpack)을 설치할 때, 사용자가 위험성을 인지할 수 있도록 경고를 표시합니다."
|
||||
},
|
||||
"app.appearance-settings.unknown-pack-warning.title": {
|
||||
"message": "출처를 알 수 없는 모드팩 설치 전 경고"
|
||||
},
|
||||
"app.auth-servers.unreachable.body": {
|
||||
"message": "Minecraft 인증 서버가 일시적으로 중단되었을 수 있습니다. 인터넷 연결을 확인한 후 나중에 다시 시도하세요."
|
||||
},
|
||||
@@ -30,14 +129,32 @@
|
||||
"message": "서버 탐색하기"
|
||||
},
|
||||
"app.browse.hide-added-servers": {
|
||||
"message": "추가된 서버 숨기기"
|
||||
"message": "이미 추가된 서버 숨기기"
|
||||
},
|
||||
"app.browse.hide-installed-content": {
|
||||
"message": "설치된 콘텐츠 숨기기"
|
||||
"message": "이미 설치된 콘텐츠 숨기기"
|
||||
},
|
||||
"app.browse.install-content-to-instance": {
|
||||
"message": "인스턴스에 콘텐츠 설치"
|
||||
},
|
||||
"app.browse.project-type.modpacks": {
|
||||
"message": "모드팩"
|
||||
},
|
||||
"app.browse.server.install": {
|
||||
"message": "설치"
|
||||
},
|
||||
"app.browse.server.installed": {
|
||||
"message": "설치됨"
|
||||
},
|
||||
"app.browse.server.installing": {
|
||||
"message": "설치 중"
|
||||
},
|
||||
"app.creation-modal.installing-modpack.description": {
|
||||
"message": "{fileName}"
|
||||
},
|
||||
"app.creation-modal.installing-modpack.title": {
|
||||
"message": "모드팩 설치중..."
|
||||
},
|
||||
"app.export-modal.description-placeholder": {
|
||||
"message": "모드팩 설명 입력..."
|
||||
},
|
||||
@@ -598,5 +715,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": "출처를 알 수 없는 파일 경고"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,13 @@
|
||||
{
|
||||
"app.action-bar.downloading-java": {
|
||||
"message": "Sedang memuat turun Java {version}"
|
||||
},
|
||||
"app.action-bar.downloads": {
|
||||
"message": "Muat Turun"
|
||||
},
|
||||
"app.appearance-settings.default-landing-page.home": {
|
||||
"message": "Laman utama"
|
||||
},
|
||||
"app.auth-servers.unreachable.body": {
|
||||
"message": "Pelayan pengesahan Minecraft mungkin sedang tergendala sekarang. Periksa sambungan internet anda dan cuba lagi kemudian."
|
||||
},
|
||||
@@ -29,12 +38,6 @@
|
||||
"app.browse.discover-servers": {
|
||||
"message": "Temui pelayan"
|
||||
},
|
||||
"app.browse.hide-added-servers": {
|
||||
"message": "Sembunyikan pelayan yang ditambah"
|
||||
},
|
||||
"app.browse.hide-installed-content": {
|
||||
"message": "Sembunyikan kandungan yang dipasang"
|
||||
},
|
||||
"app.browse.install-content-to-instance": {
|
||||
"message": "Pasang kandungan ke dalam pemasangan"
|
||||
},
|
||||
|
||||
@@ -29,12 +29,6 @@
|
||||
"app.browse.discover-servers": {
|
||||
"message": "Ontdek servers"
|
||||
},
|
||||
"app.browse.hide-added-servers": {
|
||||
"message": "Verstop toegevoegde servers"
|
||||
},
|
||||
"app.browse.hide-installed-content": {
|
||||
"message": "Verberg Geïnstalleerde inhoud"
|
||||
},
|
||||
"app.browse.install-content-to-instance": {
|
||||
"message": "Installeer inhoud naar instantie"
|
||||
},
|
||||
|
||||
@@ -1,4 +1,103 @@
|
||||
{
|
||||
"app.action-bar.downloading-java": {
|
||||
"message": "Pobieranie Java {version}"
|
||||
},
|
||||
"app.action-bar.downloads": {
|
||||
"message": "Pobrania"
|
||||
},
|
||||
"app.action-bar.hide-more-running-instances": {
|
||||
"message": "Ukryj włączone instancje"
|
||||
},
|
||||
"app.action-bar.make-primary-instance": {
|
||||
"message": "Ustaw jako główną instancję"
|
||||
},
|
||||
"app.action-bar.no-instances-running": {
|
||||
"message": "Żadna instancja nie jest włączona"
|
||||
},
|
||||
"app.action-bar.offline": {
|
||||
"message": "Offline"
|
||||
},
|
||||
"app.action-bar.primary-instance": {
|
||||
"message": "Główna instancja"
|
||||
},
|
||||
"app.action-bar.show-more-running-instances": {
|
||||
"message": "Pokaż więcej włączonych instancji"
|
||||
},
|
||||
"app.action-bar.stop-instance": {
|
||||
"message": "Zatrzymaj instancję"
|
||||
},
|
||||
"app.action-bar.view-active-downloads": {
|
||||
"message": "Pokaż aktualnie pobierane"
|
||||
},
|
||||
"app.action-bar.view-instance": {
|
||||
"message": "Pokaż instancję"
|
||||
},
|
||||
"app.action-bar.view-logs": {
|
||||
"message": "Pokaż logi"
|
||||
},
|
||||
"app.appearance-settings.advanced-rendering.description": {
|
||||
"message": "Włącza zaawansowane renderowanie, takie jak efekty rozmycia, które może powodować problemy z wydajnością bez sprzętowej akceleracji renderowania."
|
||||
},
|
||||
"app.appearance-settings.advanced-rendering.title": {
|
||||
"message": "Zaawansowane renderowanie"
|
||||
},
|
||||
"app.appearance-settings.color-theme.description": {
|
||||
"message": "Wybierz preferowany motyw w Modrinth App."
|
||||
},
|
||||
"app.appearance-settings.color-theme.title": {
|
||||
"message": "Motyw"
|
||||
},
|
||||
"app.appearance-settings.default-landing-page.description": {
|
||||
"message": "Ustaw stronę, która pojawia się po włączeniu launchera."
|
||||
},
|
||||
"app.appearance-settings.default-landing-page.home": {
|
||||
"message": "Strona główna"
|
||||
},
|
||||
"app.appearance-settings.default-landing-page.library": {
|
||||
"message": "Biblioteka"
|
||||
},
|
||||
"app.appearance-settings.default-landing-page.title": {
|
||||
"message": "Domyślna strona główna"
|
||||
},
|
||||
"app.appearance-settings.hide-nametag.description": {
|
||||
"message": "Wyłącza nazwę użytkownika na stronie skórek."
|
||||
},
|
||||
"app.appearance-settings.hide-nametag.title": {
|
||||
"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."
|
||||
},
|
||||
"app.appearance-settings.jump-back-into-worlds.title": {
|
||||
"message": "Wskocz z powrotem do światów"
|
||||
},
|
||||
"app.appearance-settings.minimize-launcher.description": {
|
||||
"message": "Minimalizuj launcher po włączeniu procesu Minecraft."
|
||||
},
|
||||
"app.appearance-settings.minimize-launcher.title": {
|
||||
"message": "Minimalizuj launcher"
|
||||
},
|
||||
"app.appearance-settings.native-decorations.description": {
|
||||
"message": "Używaj systemowej ramki okna (wymaga ponownego uruchomienia)."
|
||||
},
|
||||
"app.appearance-settings.native-decorations.title": {
|
||||
"message": "Systemowe dekoracje"
|
||||
},
|
||||
"app.appearance-settings.select-option": {
|
||||
"message": "Wybierz opcję"
|
||||
},
|
||||
"app.appearance-settings.toggle-sidebar.description": {
|
||||
"message": "Umożliwia przełączenie paska bocznego."
|
||||
},
|
||||
"app.appearance-settings.toggle-sidebar.title": {
|
||||
"message": "Przełącz pasek boczny"
|
||||
},
|
||||
"app.appearance-settings.unknown-pack-warning.description": {
|
||||
"message": "Jeżeli spróbujesz zainstalować plik Modrinth Pack (.mrpack) którego nie można znaleźć na Modrinth, poinformujemy cię o możliwym ryzyku, zanim go zainstalujesz."
|
||||
},
|
||||
"app.appearance-settings.unknown-pack-warning.title": {
|
||||
"message": "Ostrzegaj mnie przed instalowaniem nieznanych paczek modów"
|
||||
},
|
||||
"app.auth-servers.unreachable.body": {
|
||||
"message": "Serwery uwierzytelniania Minecraft mogą aktualnie nie działać. Sprawdź swoje połączenie z internetem i spróbuj ponownie później."
|
||||
},
|
||||
@@ -30,14 +129,32 @@
|
||||
"message": "Odkryj serwery"
|
||||
},
|
||||
"app.browse.hide-added-servers": {
|
||||
"message": "Ukryj dodane serwery"
|
||||
"message": "Ukryj już dodane serwery"
|
||||
},
|
||||
"app.browse.hide-installed-content": {
|
||||
"message": "Ukryj zainstalowane zasoby"
|
||||
"message": "Ukryj już dodane zasoby"
|
||||
},
|
||||
"app.browse.install-content-to-instance": {
|
||||
"message": "Zainstaluj zasoby do instancji"
|
||||
},
|
||||
"app.browse.project-type.modpacks": {
|
||||
"message": "Paczki modów"
|
||||
},
|
||||
"app.browse.server.install": {
|
||||
"message": "Zainstaluj"
|
||||
},
|
||||
"app.browse.server.installed": {
|
||||
"message": "Zainstalowano"
|
||||
},
|
||||
"app.browse.server.installing": {
|
||||
"message": "Instalowanie"
|
||||
},
|
||||
"app.creation-modal.installing-modpack.description": {
|
||||
"message": "{fileName}"
|
||||
},
|
||||
"app.creation-modal.installing-modpack.title": {
|
||||
"message": "Instalowanie paczki modów..."
|
||||
},
|
||||
"app.export-modal.description-placeholder": {
|
||||
"message": "Wprowadź opis paczki modów..."
|
||||
},
|
||||
@@ -598,5 +715,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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,103 @@
|
||||
{
|
||||
"app.action-bar.downloading-java": {
|
||||
"message": "Baixando java {version}"
|
||||
},
|
||||
"app.action-bar.downloads": {
|
||||
"message": "Downloads"
|
||||
},
|
||||
"app.action-bar.hide-more-running-instances": {
|
||||
"message": "Ocultar mais instâncias em execução"
|
||||
},
|
||||
"app.action-bar.make-primary-instance": {
|
||||
"message": "Criar instância primária"
|
||||
},
|
||||
"app.action-bar.no-instances-running": {
|
||||
"message": "Nenhuma instância em execução"
|
||||
},
|
||||
"app.action-bar.offline": {
|
||||
"message": "Offline"
|
||||
},
|
||||
"app.action-bar.primary-instance": {
|
||||
"message": "Instância primária"
|
||||
},
|
||||
"app.action-bar.show-more-running-instances": {
|
||||
"message": "Mostrar mais instâncias em execução"
|
||||
},
|
||||
"app.action-bar.stop-instance": {
|
||||
"message": "Parar instância"
|
||||
},
|
||||
"app.action-bar.view-active-downloads": {
|
||||
"message": "Ver downloads ativos"
|
||||
},
|
||||
"app.action-bar.view-instance": {
|
||||
"message": "Ver instância"
|
||||
},
|
||||
"app.action-bar.view-logs": {
|
||||
"message": "Ver logs"
|
||||
},
|
||||
"app.appearance-settings.advanced-rendering.description": {
|
||||
"message": "Ativa a renderização avançada, como efeitos de desfoque que podem causar problemas de desempenho sem a renderização acelerada de hardware."
|
||||
},
|
||||
"app.appearance-settings.advanced-rendering.title": {
|
||||
"message": "Renderização avançada"
|
||||
},
|
||||
"app.appearance-settings.color-theme.description": {
|
||||
"message": "Selecione seu tema de cores preferido para o Modrinth App."
|
||||
},
|
||||
"app.appearance-settings.color-theme.title": {
|
||||
"message": "Tema de Cores"
|
||||
},
|
||||
"app.appearance-settings.default-landing-page.description": {
|
||||
"message": "Alterar a página em que o launcher é aberto."
|
||||
},
|
||||
"app.appearance-settings.default-landing-page.home": {
|
||||
"message": "Início"
|
||||
},
|
||||
"app.appearance-settings.default-landing-page.library": {
|
||||
"message": "Biblioteca"
|
||||
},
|
||||
"app.appearance-settings.default-landing-page.title": {
|
||||
"message": "Página inicial padrão"
|
||||
},
|
||||
"app.appearance-settings.hide-nametag.description": {
|
||||
"message": "Desativa o nome acima do seu jogador na página de skins."
|
||||
},
|
||||
"app.appearance-settings.hide-nametag.title": {
|
||||
"message": "Ocultar nome"
|
||||
},
|
||||
"app.appearance-settings.jump-back-into-worlds.description": {
|
||||
"message": "Inclui mundos recentes na seção \"Voltar à ação\" na página inicial."
|
||||
},
|
||||
"app.appearance-settings.jump-back-into-worlds.title": {
|
||||
"message": "Volte aos mundos"
|
||||
},
|
||||
"app.appearance-settings.minimize-launcher.description": {
|
||||
"message": "Minimize o launcher quando um processo do Minecraft for iniciado."
|
||||
},
|
||||
"app.appearance-settings.minimize-launcher.title": {
|
||||
"message": "Minimizar launcher"
|
||||
},
|
||||
"app.appearance-settings.native-decorations.description": {
|
||||
"message": "Usar molduras de janela do sistema (requer reiniciar o aplicativo)."
|
||||
},
|
||||
"app.appearance-settings.native-decorations.title": {
|
||||
"message": "Decorações nativas"
|
||||
},
|
||||
"app.appearance-settings.select-option": {
|
||||
"message": "Selecione uma opção"
|
||||
},
|
||||
"app.appearance-settings.toggle-sidebar.description": {
|
||||
"message": "Ativa a capacidade de alternar a visibilidade da barra lateral."
|
||||
},
|
||||
"app.appearance-settings.toggle-sidebar.title": {
|
||||
"message": "Alternar barra lateral"
|
||||
},
|
||||
"app.appearance-settings.unknown-pack-warning.description": {
|
||||
"message": "Se você tentar instalar um arquivo Modrinth Pack (.mrpack) que não esteja hospedado no Modrinth, garantiremos que você entenda os riscos antes de instalá-lo."
|
||||
},
|
||||
"app.appearance-settings.unknown-pack-warning.title": {
|
||||
"message": "Avise-me antes de instalar pacotes de mods desconhecidos"
|
||||
},
|
||||
"app.auth-servers.unreachable.body": {
|
||||
"message": "Os servidores de autenticação do Minecraft podem estar indisponíveis no momento. Verifique sua conexão com a rede e tente novamente mais tarde."
|
||||
},
|
||||
@@ -30,14 +129,32 @@
|
||||
"message": "Descubra servidores"
|
||||
},
|
||||
"app.browse.hide-added-servers": {
|
||||
"message": "Ocultar servidores adicionados"
|
||||
"message": "Ocultar servidores já adicionados"
|
||||
},
|
||||
"app.browse.hide-installed-content": {
|
||||
"message": "Ocultar conteúdo instalado"
|
||||
"message": "Ocultar conteúdo já adicionado"
|
||||
},
|
||||
"app.browse.install-content-to-instance": {
|
||||
"message": "Instalar conteúdo na instância"
|
||||
},
|
||||
"app.browse.project-type.modpacks": {
|
||||
"message": "Pacotes de mods"
|
||||
},
|
||||
"app.browse.server.install": {
|
||||
"message": "Instalar"
|
||||
},
|
||||
"app.browse.server.installed": {
|
||||
"message": "Instalado"
|
||||
},
|
||||
"app.browse.server.installing": {
|
||||
"message": "Instalando"
|
||||
},
|
||||
"app.creation-modal.installing-modpack.description": {
|
||||
"message": "{fileName}"
|
||||
},
|
||||
"app.creation-modal.installing-modpack.title": {
|
||||
"message": "Instalando pacote de mods..."
|
||||
},
|
||||
"app.export-modal.description-placeholder": {
|
||||
"message": "Insira uma descrição para o pacote de mods..."
|
||||
},
|
||||
@@ -564,7 +681,7 @@
|
||||
"message": "Você só pode entrar diretamente em servidores a partir do Minecraft 1.0.5"
|
||||
},
|
||||
"instance.worlds.no_singleplayer_quick_play": {
|
||||
"message": "Você só pode entrar diretamente em mundos de um jogador a partir do Minecraft 1.20"
|
||||
"message": "Você só pode entrar diretamente em mundos de um jogador a partir do Minecraft 1.20+"
|
||||
},
|
||||
"instance.worlds.play_instance": {
|
||||
"message": "Jogar na instância"
|
||||
@@ -575,6 +692,24 @@
|
||||
"instance.worlds.world_in_use": {
|
||||
"message": "Mundo em uso"
|
||||
},
|
||||
"minecraft-account.add-account": {
|
||||
"message": "Adicionar conta"
|
||||
},
|
||||
"minecraft-account.label": {
|
||||
"message": "Conta do minecraft"
|
||||
},
|
||||
"minecraft-account.not-signed-in": {
|
||||
"message": "Não conectado"
|
||||
},
|
||||
"minecraft-account.remove-account": {
|
||||
"message": "Remover conta"
|
||||
},
|
||||
"minecraft-account.select-account": {
|
||||
"message": "Selecionar conta"
|
||||
},
|
||||
"minecraft-account.sign-in": {
|
||||
"message": "Faça login no Minecraft"
|
||||
},
|
||||
"search.filter.locked.instance": {
|
||||
"message": "Fornecido pela instância"
|
||||
},
|
||||
@@ -598,5 +733,26 @@
|
||||
},
|
||||
"search.filter.locked.server-loader.title": {
|
||||
"message": "O carregador é fornecido pelo servidor"
|
||||
},
|
||||
"unknown-pack-warning-modal.body": {
|
||||
"message": "Um arquivo só é revisado se for enviado para o Modrinth, não importa o seu formato de arquivo (incluindo .mrpack)."
|
||||
},
|
||||
"unknown-pack-warning-modal.dont-show-again": {
|
||||
"message": "Não mostrar este 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": "Malware é frequentemente distribuído através de arquivos de pacotes de mods compartilhados em plataformas como o Discord."
|
||||
},
|
||||
"unknown-pack-warning-modal.warning.body": {
|
||||
"message": "Não foi possível encontrar este arquivo no Modrinth. Recomendamos fortemente que você instale apenas arquivos de fontes confiáveis."
|
||||
},
|
||||
"unknown-pack-warning-modal.warning.title": {
|
||||
"message": "Aviso de arquivo desconhecido"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,12 +29,6 @@
|
||||
"app.browse.discover-servers": {
|
||||
"message": "Descobrir servidores"
|
||||
},
|
||||
"app.browse.hide-added-servers": {
|
||||
"message": "Esconder servidores adicionados"
|
||||
},
|
||||
"app.browse.hide-installed-content": {
|
||||
"message": "Esconder conteúdo instalado"
|
||||
},
|
||||
"app.browse.install-content-to-instance": {
|
||||
"message": "Instalar conteúdo à instância"
|
||||
},
|
||||
|
||||
@@ -1,4 +1,103 @@
|
||||
{
|
||||
"app.action-bar.downloading-java": {
|
||||
"message": "Скачивание Java {version}"
|
||||
},
|
||||
"app.action-bar.downloads": {
|
||||
"message": "Скачивания"
|
||||
},
|
||||
"app.action-bar.hide-more-running-instances": {
|
||||
"message": "Скрыть другие активные сборки"
|
||||
},
|
||||
"app.action-bar.make-primary-instance": {
|
||||
"message": "Сделать основной сборкой"
|
||||
},
|
||||
"app.action-bar.no-instances-running": {
|
||||
"message": "Нет активных сборок"
|
||||
},
|
||||
"app.action-bar.offline": {
|
||||
"message": "Офлайн"
|
||||
},
|
||||
"app.action-bar.primary-instance": {
|
||||
"message": "Основная сборка"
|
||||
},
|
||||
"app.action-bar.show-more-running-instances": {
|
||||
"message": "Показать другие активные сборки"
|
||||
},
|
||||
"app.action-bar.stop-instance": {
|
||||
"message": "Остановить сборку"
|
||||
},
|
||||
"app.action-bar.view-active-downloads": {
|
||||
"message": "Посмотреть текущие скачивания"
|
||||
},
|
||||
"app.action-bar.view-instance": {
|
||||
"message": "Посмотреть сборку"
|
||||
},
|
||||
"app.action-bar.view-logs": {
|
||||
"message": "Посмотреть журнал"
|
||||
},
|
||||
"app.appearance-settings.advanced-rendering.description": {
|
||||
"message": "Включает продвинутые эффекты вроде размытия, но может снизить производительность на устройствах без аппаратного ускорения."
|
||||
},
|
||||
"app.appearance-settings.advanced-rendering.title": {
|
||||
"message": "Расширенные эффекты"
|
||||
},
|
||||
"app.appearance-settings.color-theme.description": {
|
||||
"message": "Выберите тему для Modrinth App."
|
||||
},
|
||||
"app.appearance-settings.color-theme.title": {
|
||||
"message": "Тема"
|
||||
},
|
||||
"app.appearance-settings.default-landing-page.description": {
|
||||
"message": "Страница, которая открывается при запуске лаунчера."
|
||||
},
|
||||
"app.appearance-settings.default-landing-page.home": {
|
||||
"message": "Главная"
|
||||
},
|
||||
"app.appearance-settings.default-landing-page.library": {
|
||||
"message": "Библиотека"
|
||||
},
|
||||
"app.appearance-settings.default-landing-page.title": {
|
||||
"message": "Стартовая страница"
|
||||
},
|
||||
"app.appearance-settings.hide-nametag.description": {
|
||||
"message": "Скрывать имя над игроком на странице скинов."
|
||||
},
|
||||
"app.appearance-settings.hide-nametag.title": {
|
||||
"message": "Без имени над скином"
|
||||
},
|
||||
"app.appearance-settings.jump-back-into-worlds.description": {
|
||||
"message": "Показывать недавние миры в разделе «Jump back in» на главной."
|
||||
},
|
||||
"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.toggle-sidebar.description": {
|
||||
"message": "Показывать кнопку скрытия боковой панели."
|
||||
},
|
||||
"app.appearance-settings.toggle-sidebar.title": {
|
||||
"message": "Переключатель боковой панели"
|
||||
},
|
||||
"app.appearance-settings.unknown-pack-warning.description": {
|
||||
"message": "Предупреждать о рисках перед установкой файлов .mrpack, не размещённых на Modrinth."
|
||||
},
|
||||
"app.appearance-settings.unknown-pack-warning.title": {
|
||||
"message": "Предупреждение о сторонних сборках"
|
||||
},
|
||||
"app.auth-servers.unreachable.body": {
|
||||
"message": "Серверы аутентификации Minecraft сейчас могут быть недоступны. Проверьте подключение к интернету и повторите попытку позже."
|
||||
},
|
||||
@@ -38,6 +137,24 @@
|
||||
"app.browse.install-content-to-instance": {
|
||||
"message": "Установка контента в сборку"
|
||||
},
|
||||
"app.browse.project-type.modpacks": {
|
||||
"message": "Сборки"
|
||||
},
|
||||
"app.browse.server.install": {
|
||||
"message": "Установить"
|
||||
},
|
||||
"app.browse.server.installed": {
|
||||
"message": "Установлено"
|
||||
},
|
||||
"app.browse.server.installing": {
|
||||
"message": "Установка"
|
||||
},
|
||||
"app.creation-modal.installing-modpack.description": {
|
||||
"message": "{fileName}"
|
||||
},
|
||||
"app.creation-modal.installing-modpack.title": {
|
||||
"message": "Установка сборки..."
|
||||
},
|
||||
"app.export-modal.description-placeholder": {
|
||||
"message": "Введите описание сборки..."
|
||||
},
|
||||
@@ -90,10 +207,10 @@
|
||||
"message": "проект"
|
||||
},
|
||||
"app.instance.mods.project-was-added": {
|
||||
"message": "\"{name}\" был добавлен"
|
||||
"message": "«{name}» добавлен"
|
||||
},
|
||||
"app.instance.mods.projects-were-added": {
|
||||
"message": "{count} Проектов было добавлено"
|
||||
"message": "{count, plural, one {Добавлен # проект} few {Добавлено # проекта} other {Добавлено # проектов}}"
|
||||
},
|
||||
"app.instance.mods.share-text": {
|
||||
"message": "Что в моей сборке:"
|
||||
@@ -369,7 +486,7 @@
|
||||
"message": "Настройка мира"
|
||||
},
|
||||
"instance.files.adding-files": {
|
||||
"message": "Добавление файлов ({completed}/{total})"
|
||||
"message": "Добавление файлов ({completed, number}/{total, number})"
|
||||
},
|
||||
"instance.files.save-as": {
|
||||
"message": "Сохранить как..."
|
||||
@@ -598,5 +715,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": "Предупреждение о неизвестном файле"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,12 +29,6 @@
|
||||
"app.browse.discover-servers": {
|
||||
"message": "Otkrij servere"
|
||||
},
|
||||
"app.browse.hide-added-servers": {
|
||||
"message": "Sakrije dodate servere"
|
||||
},
|
||||
"app.browse.hide-installed-content": {
|
||||
"message": "Sakrije instalirani sadržaj"
|
||||
},
|
||||
"app.browse.install-content-to-instance": {
|
||||
"message": "Instaliraj sadržaj na instancu"
|
||||
},
|
||||
|
||||
@@ -29,15 +29,21 @@
|
||||
"app.browse.discover-servers": {
|
||||
"message": "Upptäck servrar"
|
||||
},
|
||||
"app.browse.hide-added-servers": {
|
||||
"message": "Dölj tillagda servrar"
|
||||
},
|
||||
"app.browse.hide-installed-content": {
|
||||
"message": "Dölj installerat innehåll"
|
||||
},
|
||||
"app.browse.install-content-to-instance": {
|
||||
"message": "Installera innehåll till instans"
|
||||
},
|
||||
"app.browse.project-type.modpacks": {
|
||||
"message": "Modpaket"
|
||||
},
|
||||
"app.browse.server.install": {
|
||||
"message": "Installera"
|
||||
},
|
||||
"app.browse.server.installed": {
|
||||
"message": "Installerad"
|
||||
},
|
||||
"app.browse.server.installing": {
|
||||
"message": "Installerar"
|
||||
},
|
||||
"app.export-modal.description-placeholder": {
|
||||
"message": "Ange modpaketets beskrivning..."
|
||||
},
|
||||
|
||||
@@ -23,12 +23,6 @@
|
||||
"app.browse.already-added": {
|
||||
"message": "เพิ่มแล้ว"
|
||||
},
|
||||
"app.browse.hide-added-servers": {
|
||||
"message": "ซ่อนเซิร์ฟเวอร์ที่เพิ่มแล้ว"
|
||||
},
|
||||
"app.browse.hide-installed-content": {
|
||||
"message": "ซ่อนเนื้อหาที่ติดตั้งแล้ว"
|
||||
},
|
||||
"app.browse.install-content-to-instance": {
|
||||
"message": "ติดตั้งเนื้อหาลงในอินสแตนซ์"
|
||||
},
|
||||
|
||||
@@ -1,4 +1,103 @@
|
||||
{
|
||||
"app.action-bar.downloading-java": {
|
||||
"message": "Java {version} indiriliyor"
|
||||
},
|
||||
"app.action-bar.downloads": {
|
||||
"message": "İndirmeler"
|
||||
},
|
||||
"app.action-bar.hide-more-running-instances": {
|
||||
"message": "Diğer çalışan kurulumları gizle"
|
||||
},
|
||||
"app.action-bar.make-primary-instance": {
|
||||
"message": "Birincil kurulum yap"
|
||||
},
|
||||
"app.action-bar.no-instances-running": {
|
||||
"message": "Herhangi bir kurulum çalışmıyor"
|
||||
},
|
||||
"app.action-bar.offline": {
|
||||
"message": "Çevrimdışı"
|
||||
},
|
||||
"app.action-bar.primary-instance": {
|
||||
"message": "Birincil kurulum"
|
||||
},
|
||||
"app.action-bar.show-more-running-instances": {
|
||||
"message": "Daha fazla çalışan kurulum göster"
|
||||
},
|
||||
"app.action-bar.stop-instance": {
|
||||
"message": "Kurulumu durdur"
|
||||
},
|
||||
"app.action-bar.view-active-downloads": {
|
||||
"message": "Şu anda indirilienleri göster"
|
||||
},
|
||||
"app.action-bar.view-instance": {
|
||||
"message": "Kurulumu göster"
|
||||
},
|
||||
"app.action-bar.view-logs": {
|
||||
"message": "Günlükleri görüntüle"
|
||||
},
|
||||
"app.appearance-settings.advanced-rendering.description": {
|
||||
"message": "Donanım hızlandırmalı işleme kapalıyken, performans sorunlarına yol açabilecek bulanıklık efektleri gibi gelişmiş işlemeleri aktif eder."
|
||||
},
|
||||
"app.appearance-settings.advanced-rendering.title": {
|
||||
"message": "Gelişmiş işleme"
|
||||
},
|
||||
"app.appearance-settings.color-theme.description": {
|
||||
"message": "Modrinth App için tercih ettiğiniz temayı seçin."
|
||||
},
|
||||
"app.appearance-settings.color-theme.title": {
|
||||
"message": "Tema"
|
||||
},
|
||||
"app.appearance-settings.default-landing-page.description": {
|
||||
"message": "Launcher açıldığında varsayılan olarak açılacak sayfayı seçin."
|
||||
},
|
||||
"app.appearance-settings.default-landing-page.home": {
|
||||
"message": "Ana Sayfa"
|
||||
},
|
||||
"app.appearance-settings.default-landing-page.library": {
|
||||
"message": "Kütüphane"
|
||||
},
|
||||
"app.appearance-settings.default-landing-page.title": {
|
||||
"message": "Varsayılan karşılama sayfası"
|
||||
},
|
||||
"app.appearance-settings.hide-nametag.description": {
|
||||
"message": "Kostümler sayfasındaki karakterinizin üzerindeki isim etiketini devre dışı bırakır."
|
||||
},
|
||||
"app.appearance-settings.hide-nametag.title": {
|
||||
"message": "İsim etiketini gizle"
|
||||
},
|
||||
"app.appearance-settings.jump-back-into-worlds.description": {
|
||||
"message": "Ana sayfadaki 'Hızlıca Geri Dön' bölümünde son oynanan dünyaları gösterir."
|
||||
},
|
||||
"app.appearance-settings.jump-back-into-worlds.title": {
|
||||
"message": "Dünyalara hızlıca geri dön"
|
||||
},
|
||||
"app.appearance-settings.minimize-launcher.description": {
|
||||
"message": "Minecraft başlatıldığında launcher'i simge durumuna at."
|
||||
},
|
||||
"app.appearance-settings.minimize-launcher.title": {
|
||||
"message": "Launcher'i simge durumuna at"
|
||||
},
|
||||
"app.appearance-settings.native-decorations.description": {
|
||||
"message": "Sistem pencere kenarlıklarını kullan (Yeniden başlatma gerekli)."
|
||||
},
|
||||
"app.appearance-settings.native-decorations.title": {
|
||||
"message": "Yerel pencere öğeleri"
|
||||
},
|
||||
"app.appearance-settings.select-option": {
|
||||
"message": "Bir seçenek seçin"
|
||||
},
|
||||
"app.appearance-settings.toggle-sidebar.description": {
|
||||
"message": "Kenar çubuğunu ayarlama özelliğini aktifleştirir."
|
||||
},
|
||||
"app.appearance-settings.toggle-sidebar.title": {
|
||||
"message": "Kenar çubuğunu ayarla"
|
||||
},
|
||||
"app.appearance-settings.unknown-pack-warning.description": {
|
||||
"message": "Modrinth üzerinde barındırılmayan bir Modrinth Paket dosyasını (.mrpack) yüklemeye çalışırsanız, yükleme öncesinde riskleri anladığınızdan emin oluruz."
|
||||
},
|
||||
"app.appearance-settings.unknown-pack-warning.title": {
|
||||
"message": "Bilinmeyen mod paketlerini indirmeden önce beni uyar"
|
||||
},
|
||||
"app.auth-servers.unreachable.body": {
|
||||
"message": "Minecraft doğrulama sunucuları kapalı olabilir. İnternet bağlantını kontrol et ve daha sonra tekrar dene."
|
||||
},
|
||||
@@ -6,7 +105,7 @@
|
||||
"message": "Doğrulama sunucularına erişilemedi"
|
||||
},
|
||||
"app.browse.add-server-to-instance": {
|
||||
"message": "Kurulumunuza sunucu ekleyin"
|
||||
"message": "Sunucuyu kurulumu ekle"
|
||||
},
|
||||
"app.browse.add-servers-to-instance": {
|
||||
"message": "Kurulumunuza sunucular ekleyin"
|
||||
@@ -30,14 +129,32 @@
|
||||
"message": "Sunucu keşfet"
|
||||
},
|
||||
"app.browse.hide-added-servers": {
|
||||
"message": "Ekli sunucuları gizle"
|
||||
"message": "Zaten eklenmiş sunucuları gizle"
|
||||
},
|
||||
"app.browse.hide-installed-content": {
|
||||
"message": "Kurulu içerikleri gizle"
|
||||
"message": "Zaten yüklenmiş içerikleri gizle"
|
||||
},
|
||||
"app.browse.install-content-to-instance": {
|
||||
"message": "İçeriği kuruluma yükle"
|
||||
},
|
||||
"app.browse.project-type.modpacks": {
|
||||
"message": "Mod paketleri"
|
||||
},
|
||||
"app.browse.server.install": {
|
||||
"message": "Yükle"
|
||||
},
|
||||
"app.browse.server.installed": {
|
||||
"message": "Yüklü"
|
||||
},
|
||||
"app.browse.server.installing": {
|
||||
"message": "Yükleniyor"
|
||||
},
|
||||
"app.creation-modal.installing-modpack.description": {
|
||||
"message": "{fileName}"
|
||||
},
|
||||
"app.creation-modal.installing-modpack.title": {
|
||||
"message": "Mod paketi indiriliyor..."
|
||||
},
|
||||
"app.export-modal.description-placeholder": {
|
||||
"message": "Mod paketi açıklaması girin..."
|
||||
},
|
||||
@@ -598,5 +715,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ı"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,103 @@
|
||||
{
|
||||
"app.action-bar.downloading-java": {
|
||||
"message": "Завантаження Java {version}"
|
||||
},
|
||||
"app.action-bar.downloads": {
|
||||
"message": "Завантаження"
|
||||
},
|
||||
"app.action-bar.hide-more-running-instances": {
|
||||
"message": "Приховати більше запущених екземплярів"
|
||||
},
|
||||
"app.action-bar.make-primary-instance": {
|
||||
"message": "Зробити основним екземпляром"
|
||||
},
|
||||
"app.action-bar.no-instances-running": {
|
||||
"message": "Немає запущених екземплярів"
|
||||
},
|
||||
"app.action-bar.offline": {
|
||||
"message": "Офлайн"
|
||||
},
|
||||
"app.action-bar.primary-instance": {
|
||||
"message": "Основний екземпляр"
|
||||
},
|
||||
"app.action-bar.show-more-running-instances": {
|
||||
"message": "Показати більше запущених екземплярів"
|
||||
},
|
||||
"app.action-bar.stop-instance": {
|
||||
"message": "Зупинити екземпляр"
|
||||
},
|
||||
"app.action-bar.view-active-downloads": {
|
||||
"message": "Переглянути активні завантаження"
|
||||
},
|
||||
"app.action-bar.view-instance": {
|
||||
"message": "Переглянути екземпляр"
|
||||
},
|
||||
"app.action-bar.view-logs": {
|
||||
"message": "Переглянути журнали"
|
||||
},
|
||||
"app.appearance-settings.advanced-rendering.description": {
|
||||
"message": "Дозволяє розширений рендеринг, такий як ефекти розмиття, які можуть спричиняти проблеми з продуктивністю без апаратно-прискореного рендерингу."
|
||||
},
|
||||
"app.appearance-settings.advanced-rendering.title": {
|
||||
"message": "Розширений рендеринг"
|
||||
},
|
||||
"app.appearance-settings.color-theme.description": {
|
||||
"message": "Виберіть бажану колірну тему для програми Modrinth App."
|
||||
},
|
||||
"app.appearance-settings.color-theme.title": {
|
||||
"message": "Колірна тема"
|
||||
},
|
||||
"app.appearance-settings.default-landing-page.description": {
|
||||
"message": "Змініть сторінку, на якій відкривається панель запуску."
|
||||
},
|
||||
"app.appearance-settings.default-landing-page.home": {
|
||||
"message": "Головна"
|
||||
},
|
||||
"app.appearance-settings.default-landing-page.library": {
|
||||
"message": "Бібліотека"
|
||||
},
|
||||
"app.appearance-settings.default-landing-page.title": {
|
||||
"message": "Цільова сторінка за замовчуванням"
|
||||
},
|
||||
"app.appearance-settings.hide-nametag.description": {
|
||||
"message": "Вимикає іменний бейдж над вашим гравцем на сторінці скінів."
|
||||
},
|
||||
"app.appearance-settings.hide-nametag.title": {
|
||||
"message": "Приховати бейдж з іменем"
|
||||
},
|
||||
"app.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.toggle-sidebar.description": {
|
||||
"message": "Вмикає можливість перемикання бічної панелі."
|
||||
},
|
||||
"app.appearance-settings.toggle-sidebar.title": {
|
||||
"message": "Перемикання бічної панелі"
|
||||
},
|
||||
"app.appearance-settings.unknown-pack-warning.description": {
|
||||
"message": "Якщо ви спробуєте встановити файл пакета Modrinth (.mrpack), який не розміщено на Modrinth, ми переконаємося, що ви розумієте ризики, перш ніж встановлювати його."
|
||||
},
|
||||
"app.appearance-settings.unknown-pack-warning.title": {
|
||||
"message": "Попереджати мене перед встановленням невідомих модпаків"
|
||||
},
|
||||
"app.auth-servers.unreachable.body": {
|
||||
"message": "Сервери автентифікації Minecraft можуть зараз не працювати. Перевірте з’єднання з інтернетом та спробуйте пізніше."
|
||||
},
|
||||
@@ -30,16 +129,34 @@
|
||||
"message": "Дослідити сервера"
|
||||
},
|
||||
"app.browse.hide-added-servers": {
|
||||
"message": "Сховати додані сервери"
|
||||
"message": "Скрити наявні сервери"
|
||||
},
|
||||
"app.browse.hide-installed-content": {
|
||||
"message": "Сховати встановлений уміст"
|
||||
"message": "Скрити доданий контент"
|
||||
},
|
||||
"app.browse.install-content-to-instance": {
|
||||
"message": "Установити вміст в профіль"
|
||||
},
|
||||
"app.browse.project-type.modpacks": {
|
||||
"message": "Модпаки"
|
||||
},
|
||||
"app.browse.server.install": {
|
||||
"message": "Установлено"
|
||||
},
|
||||
"app.browse.server.installed": {
|
||||
"message": "Встановлено"
|
||||
},
|
||||
"app.browse.server.installing": {
|
||||
"message": "Встановлення"
|
||||
},
|
||||
"app.creation-modal.installing-modpack.description": {
|
||||
"message": "{fileName}"
|
||||
},
|
||||
"app.creation-modal.installing-modpack.title": {
|
||||
"message": "Встановлення модпаку..."
|
||||
},
|
||||
"app.export-modal.description-placeholder": {
|
||||
"message": "Уведіть опис збірки…"
|
||||
"message": "Уведіть опис збірки..."
|
||||
},
|
||||
"app.export-modal.export-button": {
|
||||
"message": "Експортувати"
|
||||
@@ -144,7 +261,7 @@
|
||||
"message": "Ви впевнені, що хочете видалити «{name}»?"
|
||||
},
|
||||
"app.instance.worlds.search-worlds-placeholder": {
|
||||
"message": "Пошук {count} світів…"
|
||||
"message": "Пошук {count} світів..."
|
||||
},
|
||||
"app.instance.worlds.this-server": {
|
||||
"message": "цей сервер"
|
||||
@@ -324,7 +441,7 @@
|
||||
"message": "Немає друзів, які збігаються з «{query}»"
|
||||
},
|
||||
"friends.search-friends-placeholder": {
|
||||
"message": "Пошук друзів…"
|
||||
"message": "Пошук друзів..."
|
||||
},
|
||||
"friends.section.heading": {
|
||||
"message": "{title} — {count}"
|
||||
@@ -372,7 +489,7 @@
|
||||
"message": "Додавання файлів ({completed}/{total})"
|
||||
},
|
||||
"instance.files.save-as": {
|
||||
"message": "Зберегти як…"
|
||||
"message": "Зберегти як..."
|
||||
},
|
||||
"instance.server-modal.address": {
|
||||
"message": "Адреса"
|
||||
@@ -598,5 +715,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": "Попередження про невідомий файл"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,12 +29,6 @@
|
||||
"app.browse.discover-servers": {
|
||||
"message": "Khám phá máy chủ"
|
||||
},
|
||||
"app.browse.hide-added-servers": {
|
||||
"message": "Ẩn các máy chủ đã thêm"
|
||||
},
|
||||
"app.browse.hide-installed-content": {
|
||||
"message": "Ẩn các nội dung đã được tải xuống"
|
||||
},
|
||||
"app.browse.install-content-to-instance": {
|
||||
"message": "Tải nội dung này vào hồ sơ"
|
||||
},
|
||||
|
||||
@@ -1,4 +1,103 @@
|
||||
{
|
||||
"app.action-bar.downloading-java": {
|
||||
"message": "正在下载 Java {version}"
|
||||
},
|
||||
"app.action-bar.downloads": {
|
||||
"message": "下载"
|
||||
},
|
||||
"app.action-bar.hide-more-running-instances": {
|
||||
"message": "隐藏更多运行中的实例"
|
||||
},
|
||||
"app.action-bar.make-primary-instance": {
|
||||
"message": "设为主实例"
|
||||
},
|
||||
"app.action-bar.no-instances-running": {
|
||||
"message": "没有实例正在运行"
|
||||
},
|
||||
"app.action-bar.offline": {
|
||||
"message": "离线"
|
||||
},
|
||||
"app.action-bar.primary-instance": {
|
||||
"message": "主实例"
|
||||
},
|
||||
"app.action-bar.show-more-running-instances": {
|
||||
"message": "显示更多运行中的实例"
|
||||
},
|
||||
"app.action-bar.stop-instance": {
|
||||
"message": "停止实例"
|
||||
},
|
||||
"app.action-bar.view-active-downloads": {
|
||||
"message": "查看正在下载内容"
|
||||
},
|
||||
"app.action-bar.view-instance": {
|
||||
"message": "查看实例"
|
||||
},
|
||||
"app.action-bar.view-logs": {
|
||||
"message": "查看日志"
|
||||
},
|
||||
"app.appearance-settings.advanced-rendering.description": {
|
||||
"message": "启用模糊效果等高级渲染。可能会在没有硬件加速渲染的情况下造成性能问题。"
|
||||
},
|
||||
"app.appearance-settings.advanced-rendering.title": {
|
||||
"message": "高级渲染"
|
||||
},
|
||||
"app.appearance-settings.color-theme.description": {
|
||||
"message": "为 Modrinth App 选择偏好的色彩主题。"
|
||||
},
|
||||
"app.appearance-settings.color-theme.title": {
|
||||
"message": "色彩主题"
|
||||
},
|
||||
"app.appearance-settings.default-landing-page.description": {
|
||||
"message": "更改启动器打开的页面。"
|
||||
},
|
||||
"app.appearance-settings.default-landing-page.home": {
|
||||
"message": "首页"
|
||||
},
|
||||
"app.appearance-settings.default-landing-page.library": {
|
||||
"message": "库"
|
||||
},
|
||||
"app.appearance-settings.default-landing-page.title": {
|
||||
"message": "默认起始页"
|
||||
},
|
||||
"app.appearance-settings.hide-nametag.description": {
|
||||
"message": "在皮肤页面中禁用你玩家头顶上的名称标签。"
|
||||
},
|
||||
"app.appearance-settings.hide-nametag.title": {
|
||||
"message": "隐藏名称标签"
|
||||
},
|
||||
"app.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.toggle-sidebar.description": {
|
||||
"message": "启用切换侧边栏的功能。"
|
||||
},
|
||||
"app.appearance-settings.toggle-sidebar.title": {
|
||||
"message": "切换侧边栏"
|
||||
},
|
||||
"app.appearance-settings.unknown-pack-warning.description": {
|
||||
"message": "如果你尝试安装一个未托管在 Modrinth 上的 Modrinth 打包文件(.mrpack),我们会确保你在安装之前了解相关风险。"
|
||||
},
|
||||
"app.appearance-settings.unknown-pack-warning.title": {
|
||||
"message": "安装未知整合包前提醒我"
|
||||
},
|
||||
"app.auth-servers.unreachable.body": {
|
||||
"message": "Minecraft 身份验证服务器现在可能无法使用。请检查你的网络连接,并稍后再试。"
|
||||
},
|
||||
@@ -38,6 +137,24 @@
|
||||
"app.browse.install-content-to-instance": {
|
||||
"message": "将内容安装到实例中"
|
||||
},
|
||||
"app.browse.project-type.modpacks": {
|
||||
"message": "整合包"
|
||||
},
|
||||
"app.browse.server.install": {
|
||||
"message": "安装"
|
||||
},
|
||||
"app.browse.server.installed": {
|
||||
"message": "已安装"
|
||||
},
|
||||
"app.browse.server.installing": {
|
||||
"message": "安装中"
|
||||
},
|
||||
"app.creation-modal.installing-modpack.description": {
|
||||
"message": "{fileName}"
|
||||
},
|
||||
"app.creation-modal.installing-modpack.title": {
|
||||
"message": "正在安装整合包……"
|
||||
},
|
||||
"app.export-modal.description-placeholder": {
|
||||
"message": "输入整合包描述……"
|
||||
},
|
||||
@@ -575,6 +692,24 @@
|
||||
"instance.worlds.world_in_use": {
|
||||
"message": "世界正在使用中"
|
||||
},
|
||||
"minecraft-account.add-account": {
|
||||
"message": "添加账户"
|
||||
},
|
||||
"minecraft-account.label": {
|
||||
"message": "Minecraft 账户"
|
||||
},
|
||||
"minecraft-account.not-signed-in": {
|
||||
"message": "未登录"
|
||||
},
|
||||
"minecraft-account.remove-account": {
|
||||
"message": "移除账户"
|
||||
},
|
||||
"minecraft-account.select-account": {
|
||||
"message": "选择账户"
|
||||
},
|
||||
"minecraft-account.sign-in": {
|
||||
"message": "登录 Minecraft"
|
||||
},
|
||||
"search.filter.locked.instance": {
|
||||
"message": "由该实例提供"
|
||||
},
|
||||
@@ -598,5 +733,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": "未知文件警告"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,103 @@
|
||||
{
|
||||
"app.action-bar.downloading-java": {
|
||||
"message": "正在下載 Java {version}"
|
||||
},
|
||||
"app.action-bar.downloads": {
|
||||
"message": "下載"
|
||||
},
|
||||
"app.action-bar.hide-more-running-instances": {
|
||||
"message": "隱藏更多執行中的實例"
|
||||
},
|
||||
"app.action-bar.make-primary-instance": {
|
||||
"message": "設為主要實例"
|
||||
},
|
||||
"app.action-bar.no-instances-running": {
|
||||
"message": "沒有執行中的實例"
|
||||
},
|
||||
"app.action-bar.offline": {
|
||||
"message": "離線"
|
||||
},
|
||||
"app.action-bar.primary-instance": {
|
||||
"message": "主要實例"
|
||||
},
|
||||
"app.action-bar.show-more-running-instances": {
|
||||
"message": "顯示更多執行中的實例"
|
||||
},
|
||||
"app.action-bar.stop-instance": {
|
||||
"message": "停止實例"
|
||||
},
|
||||
"app.action-bar.view-active-downloads": {
|
||||
"message": "查看正在下載的項目"
|
||||
},
|
||||
"app.action-bar.view-instance": {
|
||||
"message": "查看實例"
|
||||
},
|
||||
"app.action-bar.view-logs": {
|
||||
"message": "查看紀錄檔"
|
||||
},
|
||||
"app.appearance-settings.advanced-rendering.description": {
|
||||
"message": "啟用進階繪製(如模糊效果);若無硬體加速可能會導致效能問題。"
|
||||
},
|
||||
"app.appearance-settings.advanced-rendering.title": {
|
||||
"message": "進階繪製"
|
||||
},
|
||||
"app.appearance-settings.color-theme.description": {
|
||||
"message": "請選擇你偏好的 Modrinth App 色彩主題。"
|
||||
},
|
||||
"app.appearance-settings.color-theme.title": {
|
||||
"message": "色彩主題"
|
||||
},
|
||||
"app.appearance-settings.default-landing-page.description": {
|
||||
"message": "變更啟動器開啟時的頁面。"
|
||||
},
|
||||
"app.appearance-settings.default-landing-page.home": {
|
||||
"message": "首頁"
|
||||
},
|
||||
"app.appearance-settings.default-landing-page.library": {
|
||||
"message": "遊戲庫"
|
||||
},
|
||||
"app.appearance-settings.default-landing-page.title": {
|
||||
"message": "預設起始頁面"
|
||||
},
|
||||
"app.appearance-settings.hide-nametag.description": {
|
||||
"message": "在外觀頁面中隱藏玩家上方的名牌。"
|
||||
},
|
||||
"app.appearance-settings.hide-nametag.title": {
|
||||
"message": "隱藏名牌"
|
||||
},
|
||||
"app.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.toggle-sidebar.description": {
|
||||
"message": "啟用切換側邊欄的功能。"
|
||||
},
|
||||
"app.appearance-settings.toggle-sidebar.title": {
|
||||
"message": "切換側邊欄"
|
||||
},
|
||||
"app.appearance-settings.unknown-pack-warning.description": {
|
||||
"message": "如果你嘗試安裝非 Modrinth 代管的 Modrinth 封裝檔案 (.mrpack),我們會在安裝前確保你已了解相關風險。"
|
||||
},
|
||||
"app.appearance-settings.unknown-pack-warning.title": {
|
||||
"message": "在安裝未知模組包前發出警告"
|
||||
},
|
||||
"app.auth-servers.unreachable.body": {
|
||||
"message": "Minecraft 驗證伺服器現在可能無法使用。請檢查網際網路連線,然後再試一次。"
|
||||
},
|
||||
@@ -38,6 +137,24 @@
|
||||
"app.browse.install-content-to-instance": {
|
||||
"message": "安裝至實例中"
|
||||
},
|
||||
"app.browse.project-type.modpacks": {
|
||||
"message": "模組包"
|
||||
},
|
||||
"app.browse.server.install": {
|
||||
"message": "安裝"
|
||||
},
|
||||
"app.browse.server.installed": {
|
||||
"message": "已安裝"
|
||||
},
|
||||
"app.browse.server.installing": {
|
||||
"message": "安裝中"
|
||||
},
|
||||
"app.creation-modal.installing-modpack.description": {
|
||||
"message": "{fileName}"
|
||||
},
|
||||
"app.creation-modal.installing-modpack.title": {
|
||||
"message": "正在安裝模組包..."
|
||||
},
|
||||
"app.export-modal.description-placeholder": {
|
||||
"message": "輸入模組包描述..."
|
||||
},
|
||||
@@ -75,7 +192,7 @@
|
||||
"message": "刪除實例"
|
||||
},
|
||||
"app.instance.modpack-already-installed.body": {
|
||||
"message": "此模組包已經安裝在<bold>{instanceName}</bold> 實例中了,您確定要再次安裝嗎?"
|
||||
"message": "這個模組包已經安裝在實例「<bold>{instanceName}</bold>」中了,確定要再次安裝嗎?"
|
||||
},
|
||||
"app.instance.modpack-already-installed.create": {
|
||||
"message": "建立"
|
||||
@@ -90,10 +207,10 @@
|
||||
"message": "專案"
|
||||
},
|
||||
"app.instance.mods.project-was-added": {
|
||||
"message": "新增了「{name}」"
|
||||
"message": "已新增「{name}」"
|
||||
},
|
||||
"app.instance.mods.projects-were-added": {
|
||||
"message": "新增了 {count} 個專案"
|
||||
"message": "已新增 {count} 個專案"
|
||||
},
|
||||
"app.instance.mods.share-text": {
|
||||
"message": "快來看看我在模組包中使用的專案!"
|
||||
@@ -102,7 +219,7 @@
|
||||
"message": "分享模組包內容"
|
||||
},
|
||||
"app.instance.mods.successfully-uploaded": {
|
||||
"message": "上傳成功"
|
||||
"message": "已成功上傳"
|
||||
},
|
||||
"app.instance.worlds.add-server": {
|
||||
"message": "新增伺服器"
|
||||
@@ -111,10 +228,10 @@
|
||||
"message": "瀏覽伺服器"
|
||||
},
|
||||
"app.instance.worlds.delete-world-description": {
|
||||
"message": "「{name}」將會被**永久刪除**,且無法還原。"
|
||||
"message": "「{name}」將**永久刪除**,且無法還原。"
|
||||
},
|
||||
"app.instance.worlds.delete-world-title": {
|
||||
"message": "你確定要永久刪除這個世界嗎?"
|
||||
"message": "確定要永久刪除這個世界嗎?"
|
||||
},
|
||||
"app.instance.worlds.filter-modded": {
|
||||
"message": "模組"
|
||||
@@ -141,7 +258,7 @@
|
||||
"message": "「{name}」({address}) 將從你的清單中移除(包含遊戲內),且無法還原。"
|
||||
},
|
||||
"app.instance.worlds.remove-server-title": {
|
||||
"message": "你確定要移除「{name}」嗎?"
|
||||
"message": "確定要移除「{name}」嗎?"
|
||||
},
|
||||
"app.instance.worlds.search-worlds-placeholder": {
|
||||
"message": "搜尋 {count} 個世界..."
|
||||
@@ -321,7 +438,7 @@
|
||||
"message": "待處理"
|
||||
},
|
||||
"friends.no-friends-match": {
|
||||
"message": "沒有符合「{query}」的好友"
|
||||
"message": "找不到相符「{query}」的好友"
|
||||
},
|
||||
"friends.search-friends-placeholder": {
|
||||
"message": "搜尋好友..."
|
||||
@@ -426,13 +543,13 @@
|
||||
"message": "選擇圖示"
|
||||
},
|
||||
"instance.settings.tabs.general.library-groups": {
|
||||
"message": "實例庫群組"
|
||||
"message": "遊戲庫群組"
|
||||
},
|
||||
"instance.settings.tabs.general.library-groups.create": {
|
||||
"message": "建立新的群組"
|
||||
},
|
||||
"instance.settings.tabs.general.library-groups.description": {
|
||||
"message": "實例庫群組讓你可以將實例整理到實例庫中的不同分類。"
|
||||
"message": "遊戲庫群組讓你可以將實例整理到遊戲庫中的不同分類。"
|
||||
},
|
||||
"instance.settings.tabs.general.library-groups.enter-name": {
|
||||
"message": "輸入群組名稱"
|
||||
@@ -447,10 +564,10 @@
|
||||
"message": "自訂啟動掛勾"
|
||||
},
|
||||
"instance.settings.tabs.hooks.description": {
|
||||
"message": "掛勾讓進階使用者能在遊戲啟動前和啟動後執行特定的系統命令。"
|
||||
"message": "掛勾讓進階使用者能在遊戲啟動前和啟動後執行特定的系統指令。"
|
||||
},
|
||||
"instance.settings.tabs.hooks.post-exit": {
|
||||
"message": "結束後執行"
|
||||
"message": "結束後"
|
||||
},
|
||||
"instance.settings.tabs.hooks.post-exit.description": {
|
||||
"message": "遊戲關閉後執行。"
|
||||
@@ -575,6 +692,24 @@
|
||||
"instance.worlds.world_in_use": {
|
||||
"message": "世界正在使用中"
|
||||
},
|
||||
"minecraft-account.add-account": {
|
||||
"message": "添加賬號"
|
||||
},
|
||||
"minecraft-account.label": {
|
||||
"message": "Minecraft 賬號"
|
||||
},
|
||||
"minecraft-account.not-signed-in": {
|
||||
"message": "未登錄"
|
||||
},
|
||||
"minecraft-account.remove-account": {
|
||||
"message": "移除賬號"
|
||||
},
|
||||
"minecraft-account.select-account": {
|
||||
"message": "選擇賬號"
|
||||
},
|
||||
"minecraft-account.sign-in": {
|
||||
"message": "登錄 Minecraft"
|
||||
},
|
||||
"search.filter.locked.instance": {
|
||||
"message": "由該實例提供"
|
||||
},
|
||||
@@ -598,5 +733,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": "未知檔案警告"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,46 +5,49 @@ import {
|
||||
ClipboardCopyIcon,
|
||||
ExternalIcon,
|
||||
GlobeIcon,
|
||||
PlayIcon,
|
||||
PlusIcon,
|
||||
SpinnerIcon,
|
||||
StopCircleIcon,
|
||||
} from '@modrinth/assets'
|
||||
import type { CardAction, ProjectType, Tags } from '@modrinth/ui'
|
||||
import type { BrowseInstallContentType, CardAction, ProjectType, Tags } from '@modrinth/ui'
|
||||
import {
|
||||
BrowsePageLayout,
|
||||
BrowseSidebar,
|
||||
commonMessages,
|
||||
CreationFlowModal,
|
||||
defineMessages,
|
||||
getLatestMatchingInstallVersion,
|
||||
getSelectedInstallPreferences,
|
||||
getTargetInstallPreferences,
|
||||
injectNotificationManager,
|
||||
preferencesDiffer,
|
||||
provideBrowseManager,
|
||||
requestInstall,
|
||||
useBrowseSearch,
|
||||
useDebugLogger,
|
||||
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 type { Ref } from 'vue'
|
||||
import { computed, onUnmounted, ref, shallowRef, watch } from 'vue'
|
||||
import { computed, ref, watch } from 'vue'
|
||||
import type { LocationQuery } from 'vue-router'
|
||||
import { onBeforeRouteLeave, useRoute, useRouter } from 'vue-router'
|
||||
|
||||
import ContextMenu from '@/components/ui/ContextMenu.vue'
|
||||
import { get_project_v3, get_search_results_v3 } from '@/helpers/cache.js'
|
||||
import { process_listener } from '@/helpers/events'
|
||||
import { useAppServerBrowse } from '@/composables/browse/use-app-server-browse'
|
||||
import {
|
||||
get_project,
|
||||
get_project_v3,
|
||||
get_search_results_v3,
|
||||
get_version_many,
|
||||
} from '@/helpers/cache.js'
|
||||
import { get_loader_versions as getLoaderManifest } from '@/helpers/metadata'
|
||||
import { get_by_profile_path } from '@/helpers/process'
|
||||
import {
|
||||
get as getInstance,
|
||||
get_installed_project_ids as getInstalledProjectIds,
|
||||
kill,
|
||||
list as listInstances,
|
||||
} from '@/helpers/profile.js'
|
||||
import { get_categories, get_game_versions, get_loaders } from '@/helpers/tags'
|
||||
import type { GameInstance } from '@/helpers/types'
|
||||
import { add_server_to_profile, get_profile_worlds, getServerLatency } from '@/helpers/worlds'
|
||||
import { get_profile_worlds } from '@/helpers/worlds'
|
||||
import { injectContentInstall } from '@/providers/content-install'
|
||||
import { injectServerInstall } from '@/providers/server-install'
|
||||
import {
|
||||
@@ -52,7 +55,6 @@ import {
|
||||
provideServerInstallContent,
|
||||
} from '@/providers/setup/server-install-content'
|
||||
import { useBreadcrumbs } from '@/store/breadcrumbs'
|
||||
import { getServerAddress } from '@/store/install.js'
|
||||
|
||||
const { handleError } = injectNotificationManager()
|
||||
const { formatMessage } = useVIntl()
|
||||
@@ -76,15 +78,27 @@ const {
|
||||
effectiveServerWorldId,
|
||||
serverContextServerData,
|
||||
serverContentProjectIds,
|
||||
queuedServerInstallProjectIds,
|
||||
queuedServerInstallCount,
|
||||
selectedServerInstallProjects,
|
||||
isInstallingQueuedServerInstalls,
|
||||
queuedInstallProgress,
|
||||
serverBackUrl,
|
||||
serverBackLabel,
|
||||
serverBrowseHeading,
|
||||
clearQueuedServerInstalls,
|
||||
removeQueuedServerInstall,
|
||||
flushQueuedServerInstalls,
|
||||
discardQueuedServerInstallsAndBack,
|
||||
installQueuedServerInstallsAndBack,
|
||||
initServerContext,
|
||||
watchServerContextChanges,
|
||||
searchServerModpacks,
|
||||
getServerProjectVersions,
|
||||
enforceSetupModpackRoute,
|
||||
installProjectToServer,
|
||||
getQueuedServerInstallPlans,
|
||||
setQueuedServerInstallPlans,
|
||||
openServerModpackInstallFlow,
|
||||
onServerFlowBack,
|
||||
handleServerModpackFlowCreate,
|
||||
markServerProjectInstalled,
|
||||
@@ -254,6 +268,7 @@ const instanceFilters = computed(() => {
|
||||
})
|
||||
|
||||
const serverHideInstalled = ref(false)
|
||||
const hideSelectedServerInstalls = ref(false)
|
||||
if (route.query.shi) {
|
||||
serverHideInstalled.value = route.query.shi === 'true'
|
||||
}
|
||||
@@ -291,6 +306,12 @@ const serverContextFilters = computed(() => {
|
||||
filters.push({ type: 'plugin_loader', option: platform })
|
||||
|
||||
if (pt === 'mod') filters.push({ type: 'environment', option: 'server' })
|
||||
|
||||
if (hideSelectedServerInstalls.value && queuedServerInstallProjectIds.value.size > 0) {
|
||||
for (const id of queuedServerInstallProjectIds.value) {
|
||||
filters.push({ type: 'project_id', option: `project_id:${id}`, negative: true })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (pt === 'modpack') {
|
||||
@@ -313,134 +334,24 @@ const combinedProvidedFilters = computed(() =>
|
||||
isServerContext.value ? serverContextFilters.value : instanceFilters.value,
|
||||
)
|
||||
|
||||
const serverPings = shallowRef<Record<string, number | undefined>>({})
|
||||
const serverPingCache = new Map<string, number | undefined>()
|
||||
const pendingServerPings = new Map<string, Promise<number | undefined>>()
|
||||
let serverPingCacheActive = true
|
||||
const runningServerProjects = ref<Record<string, string>>({})
|
||||
|
||||
async function checkServerRunningStates(hits: Labrinth.Search.v3.ResultSearchProject[]) {
|
||||
debugLog('checkServerRunningStates', { hitCount: hits.length })
|
||||
const packs = await listInstances()
|
||||
const newRunning: Record<string, string> = {}
|
||||
for (const hit of hits) {
|
||||
const inst = packs.find((p: GameInstance) => p.linked_data?.project_id === hit.project_id)
|
||||
if (inst) {
|
||||
const processes = await get_by_profile_path(inst.path).catch(() => [])
|
||||
if (Array.isArray(processes) && processes.length > 0) {
|
||||
newRunning[hit.project_id] = inst.path
|
||||
}
|
||||
}
|
||||
}
|
||||
debugLog('runningServerProjects updated', newRunning)
|
||||
runningServerProjects.value = newRunning
|
||||
}
|
||||
|
||||
async function handleStopServerProject(projectId: string) {
|
||||
debugLog('handleStopServerProject', projectId)
|
||||
const instancePath = runningServerProjects.value[projectId]
|
||||
if (!instancePath) return
|
||||
await kill(instancePath).catch(() => {})
|
||||
const { [projectId]: _, ...rest } = runningServerProjects.value
|
||||
runningServerProjects.value = rest
|
||||
}
|
||||
|
||||
async function handlePlayServerProject(projectId: string) {
|
||||
debugLog('handlePlayServerProject', projectId)
|
||||
await playServerProject(projectId)
|
||||
checkServerRunningStates(lastServerHits.value)
|
||||
}
|
||||
|
||||
const lastServerHits = shallowRef<Labrinth.Search.v3.ResultSearchProject[]>([])
|
||||
|
||||
async function handleAddServerToInstance(project: Labrinth.Search.v3.ResultSearchProject) {
|
||||
debugLog('handleAddServerToInstance', { projectId: project.project_id, name: project.name })
|
||||
const address = getServerAddress(project.minecraft_java_server)
|
||||
if (!address) return
|
||||
|
||||
if (instance.value) {
|
||||
try {
|
||||
await add_server_to_profile(
|
||||
instance.value.path,
|
||||
project.name,
|
||||
address,
|
||||
'prompt',
|
||||
project.project_id,
|
||||
project.minecraft_java_server?.content?.kind,
|
||||
)
|
||||
newlyInstalled.value.push(project.project_id)
|
||||
} catch (err) {
|
||||
handleError(err as Error)
|
||||
}
|
||||
} else {
|
||||
showAddServerToInstanceModal(project.name, address)
|
||||
}
|
||||
}
|
||||
|
||||
async function pingServerHits(hits: Labrinth.Search.v3.ResultSearchProject[]) {
|
||||
debugLog('pingServerHits', { hitCount: hits.length })
|
||||
const pingsToFetch = hits.flatMap((hit) => {
|
||||
const address = hit.minecraft_java_server?.address
|
||||
if (!address) return []
|
||||
return [{ hit, address }]
|
||||
})
|
||||
const nextPings = { ...serverPings.value }
|
||||
for (const { hit, address } of pingsToFetch) {
|
||||
if (serverPingCache.has(address)) {
|
||||
nextPings[hit.project_id] = serverPingCache.get(address)
|
||||
}
|
||||
}
|
||||
serverPings.value = nextPings
|
||||
|
||||
await Promise.all(
|
||||
pingsToFetch.map(async ({ hit, address }) => {
|
||||
if (serverPingCache.has(address)) return
|
||||
|
||||
let pending = pendingServerPings.get(address)
|
||||
if (!pending) {
|
||||
pending = getServerLatency(address)
|
||||
.then((latency) => {
|
||||
if (serverPingCacheActive) serverPingCache.set(address, latency)
|
||||
return latency
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(`Failed to ping server ${address}:`, err)
|
||||
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 }
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
const unlistenProcesses = await process_listener(
|
||||
(e: { event: string; profile_path_id: string }) => {
|
||||
debugLog('process event', e)
|
||||
if (e.event === 'finished') {
|
||||
const projectId = Object.entries(runningServerProjects.value).find(
|
||||
([, path]) => path === e.profile_path_id,
|
||||
)?.[0]
|
||||
if (projectId) {
|
||||
const { [projectId]: _, ...rest } = runningServerProjects.value
|
||||
runningServerProjects.value = rest
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
onUnmounted(() => {
|
||||
serverPingCacheActive = false
|
||||
unlistenProcesses()
|
||||
serverPingCache.clear()
|
||||
pendingServerPings.clear()
|
||||
const {
|
||||
serverPings,
|
||||
contextMenuRef,
|
||||
updateServerHits,
|
||||
getServerModpackContent,
|
||||
getServerCardActions,
|
||||
handleRightClick,
|
||||
handleOptionsClick,
|
||||
} = useAppServerBrowse({
|
||||
instance,
|
||||
isFromWorlds,
|
||||
allInstalledIds,
|
||||
newlyInstalled,
|
||||
installingServerProjects,
|
||||
playServerProject,
|
||||
showAddServerToInstanceModal,
|
||||
handleError,
|
||||
router,
|
||||
})
|
||||
|
||||
const offline = ref(!navigator.onLine)
|
||||
@@ -454,29 +365,13 @@ window.addEventListener('online', () => {
|
||||
})
|
||||
|
||||
const messages = defineMessages({
|
||||
addServerToInstance: {
|
||||
id: 'app.browse.add-server-to-instance',
|
||||
defaultMessage: 'Add server to instance',
|
||||
},
|
||||
addServersToInstance: {
|
||||
id: 'app.browse.add-servers-to-instance',
|
||||
defaultMessage: 'Add servers to your instance',
|
||||
defaultMessage: 'Adding server to instance',
|
||||
},
|
||||
addToInstance: {
|
||||
id: 'app.browse.add-to-instance',
|
||||
defaultMessage: 'Add to instance',
|
||||
},
|
||||
addToInstanceName: {
|
||||
id: 'app.browse.add-to-instance-name',
|
||||
defaultMessage: 'Add to {instanceName}',
|
||||
},
|
||||
added: {
|
||||
id: 'app.browse.added',
|
||||
defaultMessage: 'Added',
|
||||
},
|
||||
alreadyAdded: {
|
||||
id: 'app.browse.already-added',
|
||||
defaultMessage: 'Already added',
|
||||
addToAnInstance: {
|
||||
id: 'app.browse.add-to-an-instance',
|
||||
defaultMessage: 'Add to an instance',
|
||||
},
|
||||
discoverContent: {
|
||||
id: 'app.browse.discover-content',
|
||||
@@ -502,26 +397,19 @@ const messages = defineMessages({
|
||||
id: 'app.browse.hide-added-servers',
|
||||
defaultMessage: 'Hide already added servers',
|
||||
},
|
||||
hideInstalledContent: {
|
||||
id: 'app.browse.hide-installed-content',
|
||||
defaultMessage: 'Hide already installed content',
|
||||
},
|
||||
installContentToInstance: {
|
||||
id: 'app.browse.install-content-to-instance',
|
||||
defaultMessage: 'Install content to instance',
|
||||
},
|
||||
installToServer: {
|
||||
id: 'app.browse.server.install',
|
||||
defaultMessage: 'Install',
|
||||
},
|
||||
installedToServer: {
|
||||
id: 'app.browse.server.installed',
|
||||
defaultMessage: 'Installed',
|
||||
},
|
||||
installingToServer: {
|
||||
id: 'app.browse.server.installing',
|
||||
defaultMessage: 'Installing',
|
||||
},
|
||||
backToInstance: {
|
||||
id: 'app.browse.back-to-instance',
|
||||
defaultMessage: 'Back to instance',
|
||||
},
|
||||
serverInstanceContentWarning: {
|
||||
id: 'app.browse.server-instance-content-warning',
|
||||
defaultMessage:
|
||||
'Adding content can break compatibility when joining the server. Any added content will also be lost when you update the server instance content.',
|
||||
},
|
||||
modLoaderProvidedByInstance: {
|
||||
id: 'search.filter.locked.instance-loader.title',
|
||||
defaultMessage: 'Loader is provided by the instance',
|
||||
@@ -654,46 +542,6 @@ const selectableProjectTypes = computed(() => {
|
||||
]
|
||||
})
|
||||
|
||||
const getServerModpackContent = (project: Labrinth.Search.v3.ResultSearchProject) => {
|
||||
const content = project.minecraft_java_server?.content
|
||||
if (content?.kind === 'modpack') {
|
||||
const { project_name, project_icon, project_id } = content
|
||||
if (!project_name) return undefined
|
||||
return {
|
||||
name: project_name,
|
||||
icon: project_icon ?? undefined,
|
||||
onclick:
|
||||
project_id !== project.project_id
|
||||
? () => {
|
||||
router.push(`/project/${project_id}`)
|
||||
}
|
||||
: undefined,
|
||||
showCustomModpackTooltip: project_id === project.project_id,
|
||||
}
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
|
||||
const contextMenuRef = ref(null)
|
||||
// @ts-expect-error - no event types
|
||||
const handleRightClick = (event, result) => {
|
||||
// @ts-ignore
|
||||
contextMenuRef.value?.showMenu(event, result, [{ name: 'open_link' }, { name: 'copy_link' }])
|
||||
}
|
||||
// @ts-expect-error - no event types
|
||||
const handleOptionsClick = (args) => {
|
||||
switch (args.option) {
|
||||
case 'open_link':
|
||||
openUrl(`https://modrinth.com/${args.item.project_types?.[0] ?? 'project'}/${args.item.slug}`)
|
||||
break
|
||||
case 'copy_link':
|
||||
navigator.clipboard.writeText(
|
||||
`https://modrinth.com/${args.item.project_types?.[0] ?? 'project'}/${args.item.slug}`,
|
||||
)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
const installContext = computed(() => {
|
||||
if (isServerContext.value && serverContextServerData.value) {
|
||||
return {
|
||||
@@ -707,6 +555,15 @@ const installContext = computed(() => {
|
||||
backUrl: serverBackUrl.value,
|
||||
backLabel: serverBackLabel.value,
|
||||
heading: serverBrowseHeading.value,
|
||||
queuedCount: queuedServerInstallCount.value,
|
||||
selectedProjects: selectedServerInstallProjects.value,
|
||||
isInstallingSelected: isInstallingQueuedServerInstalls.value,
|
||||
installProgress: queuedInstallProgress.value,
|
||||
clearQueued: clearQueuedServerInstalls,
|
||||
clearSelected: clearQueuedServerInstalls,
|
||||
onBack: flushQueuedServerInstalls,
|
||||
discardSelectedAndBack: discardQueuedServerInstallsAndBack,
|
||||
installSelected: installQueuedServerInstallsAndBack,
|
||||
}
|
||||
}
|
||||
if (instance.value) {
|
||||
@@ -716,13 +573,13 @@ const installContext = computed(() => {
|
||||
gameVersion: instance.value.game_version,
|
||||
iconSrc: instance.value.icon_path ? convertFileSrc(instance.value.icon_path) : null,
|
||||
backUrl: `/instance/${encodeURIComponent(instance.value.path)}${isFromWorlds.value ? '/worlds' : ''}`,
|
||||
backLabel: 'Back to instance',
|
||||
backLabel: formatMessage(messages.backToInstance),
|
||||
heading: formatMessage(
|
||||
isFromWorlds.value ? messages.addServersToInstance : messages.installContentToInstance,
|
||||
isFromWorlds.value ? messages.addServersToInstance : commonMessages.installingContentLabel,
|
||||
),
|
||||
warning:
|
||||
isServerInstance.value && !isFromWorlds.value
|
||||
? 'Adding content can break compatibility when joining the server. Any added content will also be lost when you update the server instance content.'
|
||||
? formatMessage(messages.serverInstanceContentWarning)
|
||||
: undefined,
|
||||
}
|
||||
}
|
||||
@@ -741,71 +598,82 @@ function setProjectInstalling(projectId: string, installing: boolean) {
|
||||
installingProjectIds.value = next
|
||||
}
|
||||
|
||||
const serverInstallQueue = {
|
||||
get: getQueuedServerInstallPlans,
|
||||
set: setQueuedServerInstallPlans,
|
||||
}
|
||||
|
||||
function getCurrentSelectedInstallPreferences(projectTypeValue: string) {
|
||||
return getSelectedInstallPreferences({
|
||||
contentType: projectTypeValue,
|
||||
selectedFilters: searchState.currentFilters.value,
|
||||
providedFilters: combinedProvidedFilters.value,
|
||||
overriddenProvidedFilterTypes: searchState.overriddenProvidedFilterTypes.value,
|
||||
})
|
||||
}
|
||||
|
||||
function getServerInstallTargetPreferences(contentType: BrowseInstallContentType) {
|
||||
return getTargetInstallPreferences(
|
||||
{
|
||||
gameVersion: serverContextServerData.value?.mc_version,
|
||||
loader: serverContextServerData.value?.loader,
|
||||
},
|
||||
contentType,
|
||||
)
|
||||
}
|
||||
|
||||
function getInstanceInstallTargetPreferences(projectTypeValue: string) {
|
||||
return getTargetInstallPreferences(
|
||||
{
|
||||
gameVersion: instance.value?.game_version,
|
||||
loader: instance.value?.loader,
|
||||
},
|
||||
projectTypeValue,
|
||||
)
|
||||
}
|
||||
|
||||
async function getInstallProjectVersions(projectId: string) {
|
||||
const project = await get_project(projectId, 'must_revalidate')
|
||||
return (await get_version_many(
|
||||
project.versions,
|
||||
'must_revalidate',
|
||||
)) as Labrinth.Versions.v2.Version[]
|
||||
}
|
||||
|
||||
async function chooseInstanceInstallVersion(
|
||||
project: Labrinth.Search.v2.ResultSearchProject & Labrinth.Search.v3.ResultSearchProject,
|
||||
projectTypeValue: string,
|
||||
) {
|
||||
const targetInstance = instance.value
|
||||
if (!targetInstance) {
|
||||
return { versionId: null as string | null }
|
||||
}
|
||||
|
||||
const selectedPreferences = getCurrentSelectedInstallPreferences(projectTypeValue)
|
||||
const targetPreferences = getInstanceInstallTargetPreferences(projectTypeValue)
|
||||
if (!preferencesDiffer(selectedPreferences, targetPreferences)) {
|
||||
return { versionId: null as string | null }
|
||||
}
|
||||
|
||||
const selectedVersion = getLatestMatchingInstallVersion(
|
||||
await getInstallProjectVersions(project.project_id),
|
||||
selectedPreferences,
|
||||
projectTypeValue,
|
||||
)
|
||||
|
||||
if (!selectedVersion) {
|
||||
return { versionId: null as string | null }
|
||||
}
|
||||
|
||||
return { versionId: selectedVersion.id }
|
||||
}
|
||||
|
||||
function getCardActions(
|
||||
result: Labrinth.Search.v2.ResultSearchProject | Labrinth.Search.v3.ResultSearchProject,
|
||||
currentProjectType: string,
|
||||
): CardAction[] {
|
||||
if (currentProjectType === 'server') {
|
||||
const serverResult = result as Labrinth.Search.v3.ResultSearchProject
|
||||
const isInstalled = allInstalledIds.value.has(serverResult.project_id)
|
||||
|
||||
if (isFromWorlds.value && instance.value) {
|
||||
return [
|
||||
{
|
||||
key: 'add-to-instance',
|
||||
label: formatMessage(isInstalled ? messages.added : messages.addToInstance),
|
||||
icon: isInstalled ? CheckIcon : PlusIcon,
|
||||
disabled: isInstalled,
|
||||
color: 'brand',
|
||||
type: 'outlined',
|
||||
onClick: () => handleAddServerToInstance(serverResult),
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
const actions: CardAction[] = []
|
||||
|
||||
actions.push({
|
||||
key: 'add',
|
||||
label: '',
|
||||
icon: isInstalled ? CheckIcon : PlusIcon,
|
||||
disabled: isInstalled,
|
||||
circular: true,
|
||||
tooltip: isInstalled
|
||||
? formatMessage(messages.alreadyAdded)
|
||||
: instance.value
|
||||
? formatMessage(messages.addToInstanceName, { instanceName: instance.value.name })
|
||||
: formatMessage(messages.addServerToInstance),
|
||||
onClick: () => handleAddServerToInstance(serverResult),
|
||||
})
|
||||
|
||||
if (runningServerProjects.value[serverResult.project_id]) {
|
||||
actions.push({
|
||||
key: 'stop',
|
||||
label: formatMessage(commonMessages.stopButton),
|
||||
icon: StopCircleIcon,
|
||||
color: 'red',
|
||||
type: 'outlined',
|
||||
onClick: () => handleStopServerProject(serverResult.project_id),
|
||||
})
|
||||
} else {
|
||||
const isInstalling = (installingServerProjects.value as string[]).includes(
|
||||
serverResult.project_id,
|
||||
)
|
||||
actions.push({
|
||||
key: 'play',
|
||||
label: formatMessage(
|
||||
isInstalling ? commonMessages.installingLabel : commonMessages.playButton,
|
||||
),
|
||||
icon: PlayIcon,
|
||||
disabled: isInstalling,
|
||||
color: 'brand',
|
||||
type: 'outlined',
|
||||
onClick: () => handlePlayServerProject(serverResult.project_id),
|
||||
})
|
||||
}
|
||||
|
||||
return actions
|
||||
return getServerCardActions(result as Labrinth.Search.v3.ResultSearchProject)
|
||||
}
|
||||
|
||||
// Non-server project actions
|
||||
@@ -817,39 +685,84 @@ function getCardActions(
|
||||
const isInstalled =
|
||||
projectResult.installed ||
|
||||
allInstalledIds.value.has(projectResult.project_id || '') ||
|
||||
serverContentProjectIds.value.has(projectResult.project_id || '')
|
||||
serverContentProjectIds.value.has(projectResult.project_id || '') ||
|
||||
serverContextServerData.value?.upstream?.project_id === projectResult.project_id
|
||||
const isInstalling = installingProjectIds.value.has(projectResult.project_id)
|
||||
|
||||
if (
|
||||
isServerContext.value &&
|
||||
['modpack', 'mod', 'plugin', 'datapack'].includes(currentProjectType)
|
||||
) {
|
||||
const isQueued = queuedServerInstallProjectIds.value.has(projectResult.project_id)
|
||||
const isInstallingSelection = isInstallingQueuedServerInstalls.value
|
||||
const validatingInstall =
|
||||
isInstalling && currentProjectType !== 'modpack' && !isInstallingSelection
|
||||
const installLabel = isInstalled
|
||||
? commonMessages.installedLabel
|
||||
: isQueued
|
||||
? isInstalling || isInstallingSelection
|
||||
? validatingInstall
|
||||
? commonMessages.validatingLabel
|
||||
: messages.installingToServer
|
||||
: commonMessages.selectedLabel
|
||||
: isInstalling || isInstallingSelection
|
||||
? validatingInstall
|
||||
? commonMessages.validatingLabel
|
||||
: messages.installingToServer
|
||||
: commonMessages.installButton
|
||||
return [
|
||||
{
|
||||
key: 'install',
|
||||
label: formatMessage(
|
||||
isInstalling
|
||||
? messages.installingToServer
|
||||
: isInstalled
|
||||
? messages.installedToServer
|
||||
: messages.installToServer,
|
||||
),
|
||||
icon: isInstalled ? CheckIcon : PlusIcon,
|
||||
iconClass: isInstalling ? 'animate-spin' : undefined,
|
||||
disabled: isInstalled || isInstalling,
|
||||
color: 'brand',
|
||||
label: formatMessage(installLabel),
|
||||
icon:
|
||||
isInstalling || isInstallingSelection
|
||||
? SpinnerIcon
|
||||
: isQueued || isInstalled
|
||||
? CheckIcon
|
||||
: PlusIcon,
|
||||
iconClass: isInstalling || isInstallingSelection ? 'animate-spin' : undefined,
|
||||
disabled: isInstalled || isInstalling || isInstallingSelection,
|
||||
color: isQueued && !isInstalling && !isInstallingSelection ? 'green' : 'brand',
|
||||
type: 'outlined',
|
||||
onClick: async () => {
|
||||
setProjectInstalling(projectResult.project_id, true)
|
||||
if (isQueued) {
|
||||
removeQueuedServerInstall(projectResult.project_id)
|
||||
return
|
||||
}
|
||||
|
||||
const contentType = currentProjectType as BrowseInstallContentType
|
||||
const isModpack = contentType === 'modpack'
|
||||
const shouldShowInstalling = isModpack || !isQueued
|
||||
if (shouldShowInstalling) {
|
||||
setProjectInstalling(projectResult.project_id, true)
|
||||
}
|
||||
try {
|
||||
const didInstall = await installProjectToServer(projectResult)
|
||||
if (didInstall !== false) {
|
||||
onSearchResultInstalled(projectResult.project_id)
|
||||
}
|
||||
await requestInstall({
|
||||
project: projectResult,
|
||||
contentType,
|
||||
mode: isModpack ? 'immediate' : 'queue',
|
||||
selectedFilters: isModpack ? [] : searchState.currentFilters.value,
|
||||
providedFilters: isModpack ? [] : combinedProvidedFilters.value,
|
||||
overriddenProvidedFilterTypes: isModpack
|
||||
? []
|
||||
: searchState.overriddenProvidedFilterTypes.value,
|
||||
targetPreferences: getServerInstallTargetPreferences(contentType),
|
||||
getProjectVersions: getInstallProjectVersions,
|
||||
queue: serverInstallQueue,
|
||||
install: (plan) =>
|
||||
openServerModpackInstallFlow({
|
||||
projectId: plan.projectId,
|
||||
versionId: plan.versionId,
|
||||
name: plan.project.name,
|
||||
iconUrl: plan.project.icon_url ?? undefined,
|
||||
}),
|
||||
})
|
||||
} catch (err) {
|
||||
handleError(err as Error)
|
||||
} finally {
|
||||
setProjectInstalling(projectResult.project_id, false)
|
||||
if (shouldShowInstalling) {
|
||||
setProjectInstalling(projectResult.project_id, false)
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
@@ -862,13 +775,15 @@ function getCardActions(
|
||||
return [
|
||||
{
|
||||
key: 'install',
|
||||
label: isInstalling
|
||||
? 'Installing'
|
||||
: isInstalled
|
||||
? 'Installed'
|
||||
: shouldUseInstallIcon
|
||||
? 'Install'
|
||||
: 'Add to an instance',
|
||||
label: formatMessage(
|
||||
isInstalling
|
||||
? messages.installingToServer
|
||||
: isInstalled
|
||||
? commonMessages.installedLabel
|
||||
: shouldUseInstallIcon
|
||||
? commonMessages.installButton
|
||||
: messages.addToAnInstance,
|
||||
),
|
||||
icon: isInstalling ? SpinnerIcon : isInstalled ? CheckIcon : PlusIcon,
|
||||
iconClass: isInstalling ? 'animate-spin' : undefined,
|
||||
disabled: isInstalled || isInstalling,
|
||||
@@ -876,28 +791,39 @@ function getCardActions(
|
||||
type: 'outlined',
|
||||
onClick: async () => {
|
||||
setProjectInstalling(projectResult.project_id, true)
|
||||
await installVersion(
|
||||
projectResult.project_id,
|
||||
null,
|
||||
instance.value ? instance.value.path : null,
|
||||
'SearchCard',
|
||||
(versionId) => {
|
||||
try {
|
||||
const selectedInstall = instance.value
|
||||
? await chooseInstanceInstallVersion(projectResult, currentProjectType)
|
||||
: { versionId: null as string | null }
|
||||
if (selectedInstall === null) {
|
||||
setProjectInstalling(projectResult.project_id, false)
|
||||
if (versionId) {
|
||||
onSearchResultInstalled(projectResult.project_id)
|
||||
}
|
||||
},
|
||||
(profile) => {
|
||||
router.push(`/instance/${profile}`)
|
||||
},
|
||||
{
|
||||
preferredLoader: instance.value?.loader ?? undefined,
|
||||
preferredGameVersion: instance.value?.game_version ?? undefined,
|
||||
},
|
||||
).catch((err) => {
|
||||
return
|
||||
}
|
||||
const selectedPreferences = getCurrentSelectedInstallPreferences(currentProjectType)
|
||||
await installVersion(
|
||||
projectResult.project_id,
|
||||
selectedInstall.versionId,
|
||||
instance.value ? instance.value.path : null,
|
||||
'SearchCard',
|
||||
(versionId) => {
|
||||
setProjectInstalling(projectResult.project_id, false)
|
||||
if (versionId) {
|
||||
onSearchResultInstalled(projectResult.project_id)
|
||||
}
|
||||
},
|
||||
(profile) => {
|
||||
router.push(`/instance/${profile}`)
|
||||
},
|
||||
{
|
||||
preferredLoader: instance.value?.loader ?? selectedPreferences.loaders?.[0],
|
||||
preferredGameVersion:
|
||||
instance.value?.game_version ?? selectedPreferences.gameVersions?.[0],
|
||||
},
|
||||
)
|
||||
} catch (err) {
|
||||
setProjectInstalling(projectResult.project_id, false)
|
||||
handleError(err)
|
||||
})
|
||||
}
|
||||
},
|
||||
},
|
||||
]
|
||||
@@ -937,9 +863,7 @@ async function search(requestParams: string) {
|
||||
|
||||
if (isServer) {
|
||||
const hits = rawResults.result.hits ?? []
|
||||
lastServerHits.value = hits
|
||||
pingServerHits(hits)
|
||||
checkServerRunningStates(hits)
|
||||
updateServerHits(hits)
|
||||
return {
|
||||
projectHits: [],
|
||||
serverHits: hits,
|
||||
@@ -1024,6 +948,12 @@ watch(
|
||||
{ deep: true },
|
||||
)
|
||||
|
||||
watch(queuedServerInstallCount, (count) => {
|
||||
if (count === 0) {
|
||||
hideSelectedServerInstalls.value = false
|
||||
}
|
||||
})
|
||||
|
||||
if (instance.value?.game_version) {
|
||||
const gv = instance.value.game_version
|
||||
const alreadyHasGv = searchState.serverCurrentFilters.value.some(
|
||||
@@ -1036,16 +966,26 @@ if (instance.value?.game_version) {
|
||||
|
||||
await searchState.refreshSearch()
|
||||
|
||||
function getProjectBrowseQuery() {
|
||||
if (!installContext.value) return undefined
|
||||
return {
|
||||
...route.query,
|
||||
b: route.fullPath,
|
||||
}
|
||||
}
|
||||
|
||||
provideBrowseManager({
|
||||
tags,
|
||||
projectType,
|
||||
...searchState,
|
||||
getProjectLink: (result: Labrinth.Search.v2.ResultSearchProject) => ({
|
||||
path: `/project/${result.project_id ?? result.slug}`,
|
||||
query: instance.value ? { i: instance.value.path } : undefined,
|
||||
query: getProjectBrowseQuery(),
|
||||
}),
|
||||
getServerProjectLink: (result: Labrinth.Search.v3.ResultSearchProject) => ({
|
||||
path: `/project/${result.slug ?? result.project_id}`,
|
||||
query: getProjectBrowseQuery(),
|
||||
}),
|
||||
getServerProjectLink: (result: Labrinth.Search.v3.ResultSearchProject) =>
|
||||
`/project/${result.slug ?? result.project_id}`,
|
||||
selectableProjectTypes,
|
||||
showProjectTypeTabs: computed(() => !isServerContext.value),
|
||||
variant: 'app',
|
||||
@@ -1068,8 +1008,18 @@ provideBrowseManager({
|
||||
() => (isServerContext.value && projectType.value !== 'modpack') || !!instance.value,
|
||||
),
|
||||
hideInstalledLabel: computed(() =>
|
||||
formatMessage(isFromWorlds.value ? messages.hideAddedServers : messages.hideInstalledContent),
|
||||
formatMessage(
|
||||
isFromWorlds.value ? messages.hideAddedServers : commonMessages.hideInstalledContentLabel,
|
||||
),
|
||||
),
|
||||
hideSelected: hideSelectedServerInstalls,
|
||||
showHideSelected: computed(
|
||||
() =>
|
||||
isServerContext.value &&
|
||||
projectType.value !== 'modpack' &&
|
||||
queuedServerInstallCount.value > 0,
|
||||
),
|
||||
hideSelectedLabel: computed(() => formatMessage(commonMessages.hideSelectedContentLabel)),
|
||||
onInstalled: onSearchResultInstalled,
|
||||
serverPings,
|
||||
getServerModpackContent,
|
||||
@@ -1084,8 +1034,12 @@ provideBrowseManager({
|
||||
<BrowsePageLayout>
|
||||
<template #after>
|
||||
<ContextMenu ref="contextMenuRef" @option-clicked="handleOptionsClick">
|
||||
<template #open_link> <GlobeIcon /> Open in Modrinth <ExternalIcon /> </template>
|
||||
<template #copy_link> <ClipboardCopyIcon /> Copy link </template>
|
||||
<template #open_link>
|
||||
<GlobeIcon /> {{ formatMessage(commonMessages.openInModrinthButton) }} <ExternalIcon />
|
||||
</template>
|
||||
<template #copy_link>
|
||||
<ClipboardCopyIcon /> {{ formatMessage(commonMessages.copyLinkButton) }}
|
||||
</template>
|
||||
</ContextMenu>
|
||||
</template>
|
||||
</BrowsePageLayout>
|
||||
|
||||
@@ -311,7 +311,7 @@ async function updateProject(mod: ContentItem) {
|
||||
const profile = await get(props.instance.path).catch(handleError)
|
||||
|
||||
if (profile) {
|
||||
await installVersionDependencies(profile, versionData).catch(handleError)
|
||||
await installVersionDependencies(profile, versionData, 'update').catch(handleError)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -347,7 +347,7 @@ async function switchProjectVersion(mod: ContentItem, version: Labrinth.Versions
|
||||
|
||||
const profile = await get(props.instance.path).catch(handleError)
|
||||
if (profile) {
|
||||
await installVersionDependencies(profile, version).catch(handleError)
|
||||
await installVersionDependencies(profile, version, 'update').catch(handleError)
|
||||
}
|
||||
|
||||
mod.file_path = newPath
|
||||
|
||||
@@ -45,7 +45,13 @@
|
||||
/>
|
||||
</Teleport>
|
||||
<div class="flex flex-col gap-4 p-6">
|
||||
<InstanceIndicator v-if="instance" :instance="instance" />
|
||||
<div
|
||||
v-if="projectInstallContext"
|
||||
class="sticky top-0 z-20 -mx-6 -mt-6 rounded-tl-[--radius-xl] border-0 border-b border-solid bg-surface-1 p-3 border-surface-5"
|
||||
>
|
||||
<BrowseInstallHeader :install-context="projectInstallContext" />
|
||||
</div>
|
||||
<InstanceIndicator v-if="instance && !projectInstallContext" :instance="instance" />
|
||||
<template v-if="data">
|
||||
<Teleport
|
||||
v-if="themeStore.featureFlags.project_background"
|
||||
@@ -64,7 +70,7 @@
|
||||
<ButtonStyled v-if="serverPlaying" size="large" color="red">
|
||||
<button @click="handleStopServer">
|
||||
<StopCircleIcon />
|
||||
Stop
|
||||
{{ formatMessage(commonMessages.stopButton) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled v-else size="large" color="brand">
|
||||
@@ -73,11 +79,18 @@
|
||||
@click="handleClickPlay"
|
||||
>
|
||||
<PlayIcon />
|
||||
{{ data && installingServerProjects.includes(data.id) ? 'Installing...' : 'Play' }}
|
||||
{{
|
||||
data && installingServerProjects.includes(data.id)
|
||||
? formatMessage(commonMessages.installingLabel)
|
||||
: formatMessage(commonMessages.playButton)
|
||||
}}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled size="large" circular>
|
||||
<button v-tooltip="'Add server to instance'" @click="handleAddServerToInstance">
|
||||
<button
|
||||
v-tooltip="formatMessage(commonMessages.addServerToInstanceButton)"
|
||||
@click="handleAddServerToInstance"
|
||||
>
|
||||
<PlusIcon />
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
@@ -111,13 +124,17 @@
|
||||
<template v-else #actions>
|
||||
<ButtonStyled size="large" color="brand">
|
||||
<button
|
||||
v-tooltip="installed ? `This project is already installed` : null"
|
||||
:disabled="installed || installing"
|
||||
v-tooltip="installButtonTooltip"
|
||||
:disabled="installButtonDisabled"
|
||||
@click="install(null)"
|
||||
>
|
||||
<DownloadIcon v-if="!installed && !installing" />
|
||||
<CheckIcon v-else-if="installed" />
|
||||
{{ installing ? 'Installing...' : installed ? 'Installed' : 'Install' }}
|
||||
<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">
|
||||
@@ -166,7 +183,7 @@
|
||||
:links="[
|
||||
{
|
||||
label: 'Description',
|
||||
href: `/project/${$route.params.id}`,
|
||||
href: projectDescriptionHref,
|
||||
},
|
||||
{
|
||||
label: 'Versions',
|
||||
@@ -176,7 +193,7 @@
|
||||
},
|
||||
{
|
||||
label: 'Gallery',
|
||||
href: `/project/${$route.params.id}/gallery`,
|
||||
href: projectGalleryHref,
|
||||
shown: data.gallery.length > 0,
|
||||
},
|
||||
]"
|
||||
@@ -195,11 +212,39 @@
|
||||
</template>
|
||||
<template v-else> Project data couldn't not be loaded. </template>
|
||||
</div>
|
||||
<SelectedProjectsFloatingBar
|
||||
v-if="projectInstallContext"
|
||||
:install-context="projectInstallContext"
|
||||
/>
|
||||
<ContextMenu ref="options" @option-clicked="handleOptionsClick">
|
||||
<template #install> <DownloadIcon /> Install </template>
|
||||
<template #open_link> <GlobeIcon /> Open in Modrinth <ExternalIcon /> </template>
|
||||
<template #copy_link> <ClipboardCopyIcon /> Copy link </template>
|
||||
<template #install>
|
||||
<DownloadIcon /> {{ formatMessage(commonMessages.installButton) }}
|
||||
</template>
|
||||
<template #open_link>
|
||||
<GlobeIcon /> {{ formatMessage(commonMessages.openInModrinthButton) }} <ExternalIcon />
|
||||
</template>
|
||||
<template #copy_link>
|
||||
<ClipboardCopyIcon /> {{ formatMessage(commonMessages.copyLinkButton) }}
|
||||
</template>
|
||||
</ContextMenu>
|
||||
<CreationFlowModal
|
||||
v-if="serverInstallContent.isServerContext.value && data?.project_type === 'modpack'"
|
||||
ref="serverSetupModalRef"
|
||||
:type="
|
||||
serverInstallContent.serverFlowFrom.value === 'reset-server'
|
||||
? 'reset-server'
|
||||
: 'server-onboarding'
|
||||
"
|
||||
:available-loaders="['vanilla', 'fabric', 'neoforge', 'forge', 'quilt', 'paper', 'purpur']"
|
||||
:show-snapshot-toggle="true"
|
||||
:on-back="serverInstallContent.onServerFlowBack"
|
||||
:search-modpacks="serverInstallContent.searchServerModpacks"
|
||||
:get-project-versions="serverInstallContent.getServerProjectVersions"
|
||||
:get-loader-manifest="getLoaderManifest"
|
||||
@hide="() => {}"
|
||||
@browse-modpacks="() => {}"
|
||||
@create="serverInstallContent.handleServerModpackFlowCreate"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -216,10 +261,16 @@ import {
|
||||
PlayIcon,
|
||||
PlusIcon,
|
||||
ReportIcon,
|
||||
SpinnerIcon,
|
||||
StopCircleIcon,
|
||||
} from '@modrinth/assets'
|
||||
import {
|
||||
BrowseInstallHeader,
|
||||
ButtonStyled,
|
||||
commonMessages,
|
||||
CreationFlowModal,
|
||||
defineMessages,
|
||||
getTargetInstallPreferences,
|
||||
injectNotificationManager,
|
||||
NavTabs,
|
||||
OverflowMenu,
|
||||
@@ -231,7 +282,11 @@ import {
|
||||
ProjectSidebarLinks,
|
||||
ProjectSidebarServerInfo,
|
||||
ProjectSidebarTags,
|
||||
requestInstall,
|
||||
SelectedProjectsFloatingBar,
|
||||
useVIntl,
|
||||
} from '@modrinth/ui'
|
||||
import { convertFileSrc } from '@tauri-apps/api/core'
|
||||
import { openUrl } from '@tauri-apps/plugin-opener'
|
||||
import dayjs from 'dayjs'
|
||||
import relativeTime from 'dayjs/plugin/relativeTime'
|
||||
@@ -249,6 +304,7 @@ import {
|
||||
get_version_many,
|
||||
} from '@/helpers/cache.js'
|
||||
import { process_listener } from '@/helpers/events'
|
||||
import { get_loader_versions as getLoaderManifest } from '@/helpers/metadata'
|
||||
import { get_by_profile_path } from '@/helpers/process'
|
||||
import {
|
||||
get as getInstance,
|
||||
@@ -260,6 +316,7 @@ import { get_categories, get_game_versions, get_loaders } from '@/helpers/tags'
|
||||
import { getServerLatency } from '@/helpers/worlds'
|
||||
import { injectContentInstall } from '@/providers/content-install'
|
||||
import { injectServerInstall } from '@/providers/server-install'
|
||||
import { createServerInstallContent } from '@/providers/setup/server-install-content'
|
||||
import { useBreadcrumbs } from '@/store/breadcrumbs'
|
||||
import { getServerAddress } from '@/store/install.js'
|
||||
import { useTheming } from '@/store/state.js'
|
||||
@@ -272,6 +329,22 @@ const route = useRoute()
|
||||
const router = useRouter()
|
||||
const breadcrumbs = useBreadcrumbs()
|
||||
const themeStore = useTheming()
|
||||
const { formatMessage } = useVIntl()
|
||||
|
||||
const messages = defineMessages({
|
||||
backToBrowse: {
|
||||
id: 'app.project.install-context.back-to-browse',
|
||||
defaultMessage: 'Back to browse',
|
||||
},
|
||||
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',
|
||||
},
|
||||
})
|
||||
|
||||
const { installingServerProjects, playServerProject, showAddServerToInstanceModal } =
|
||||
injectServerInstall()
|
||||
@@ -296,6 +369,11 @@ const serverPing = ref(undefined)
|
||||
const serverStatusOnline = ref(false)
|
||||
const serverInstancePath = ref(null)
|
||||
const serverPlaying = ref(false)
|
||||
const serverSetupModalRef = ref(null)
|
||||
const serverInstallContent = createServerInstallContent({ serverSetupModalRef })
|
||||
|
||||
serverInstallContent.watchServerContextChanges()
|
||||
await serverInstallContent.initServerContext()
|
||||
|
||||
const instanceFilters = computed(() => {
|
||||
if (!instance.value) {
|
||||
@@ -315,11 +393,9 @@ const instanceFilters = computed(() => {
|
||||
return { l: loaders, g: instance.value.game_version }
|
||||
})
|
||||
|
||||
const versionsHref = computed(() => {
|
||||
const base = `/project/${route.params.id}/versions`
|
||||
const filters = instanceFilters.value
|
||||
function buildProjectHref(path, extraQuery = {}) {
|
||||
const params = new URLSearchParams()
|
||||
for (const [key, val] of Object.entries(filters)) {
|
||||
for (const [key, val] of Object.entries({ ...route.query, ...extraQuery })) {
|
||||
if (Array.isArray(val)) {
|
||||
for (const v of val) params.append(key, v)
|
||||
} else if (val) {
|
||||
@@ -327,7 +403,102 @@ const versionsHref = computed(() => {
|
||||
}
|
||||
}
|
||||
const qs = params.toString()
|
||||
return qs ? `${base}?${qs}` : base
|
||||
return qs ? `${path}?${qs}` : path
|
||||
}
|
||||
|
||||
const projectDescriptionHref = computed(() => buildProjectHref(`/project/${route.params.id}`))
|
||||
const versionsHref = computed(() =>
|
||||
buildProjectHref(`/project/${route.params.id}/versions`, instanceFilters.value),
|
||||
)
|
||||
const projectGalleryHref = computed(() => buildProjectHref(`/project/${route.params.id}/gallery`))
|
||||
|
||||
const projectBrowseBackUrl = computed(() => {
|
||||
const browsePath = route.query.b
|
||||
if (typeof browsePath === 'string' && browsePath.startsWith('/browse/')) return browsePath
|
||||
const type = data.value?.project_type ? `${data.value.project_type}s` : 'mods'
|
||||
return `/browse/${type}`
|
||||
})
|
||||
|
||||
const projectInstallContext = computed(() => {
|
||||
const serverData = serverInstallContent.serverContextServerData.value
|
||||
if (serverData) {
|
||||
return {
|
||||
name: serverData.name,
|
||||
loader: serverData.loader ?? '',
|
||||
gameVersion: serverData.mc_version ?? '',
|
||||
serverId: serverInstallContent.serverIdQuery.value,
|
||||
upstream: serverData.upstream,
|
||||
iconSrc: null,
|
||||
isMedal: serverData.is_medal,
|
||||
backUrl: projectBrowseBackUrl.value,
|
||||
backLabel: formatMessage(messages.backToBrowse),
|
||||
heading: serverInstallContent.serverBrowseHeading.value,
|
||||
queuedCount: serverInstallContent.queuedServerInstallCount.value,
|
||||
selectedProjects: serverInstallContent.selectedServerInstallProjects.value,
|
||||
isInstallingSelected: serverInstallContent.isInstallingQueuedServerInstalls.value,
|
||||
installProgress: serverInstallContent.queuedInstallProgress.value,
|
||||
clearQueued: serverInstallContent.clearQueuedServerInstalls,
|
||||
clearSelected: serverInstallContent.clearQueuedServerInstalls,
|
||||
discardSelectedAndBack: serverInstallContent.discardQueuedServerInstallsAndBack,
|
||||
installSelected: serverInstallContent.installQueuedServerInstallsAndBack,
|
||||
}
|
||||
}
|
||||
|
||||
if (instance.value) {
|
||||
return {
|
||||
name: instance.value.name,
|
||||
loader: instance.value.loader,
|
||||
gameVersion: instance.value.game_version,
|
||||
iconSrc: instance.value.icon_path ? convertFileSrc(instance.value.icon_path) : null,
|
||||
backUrl: projectBrowseBackUrl.value,
|
||||
backLabel: formatMessage(messages.backToBrowse),
|
||||
heading: formatMessage(messages.installContentToInstance),
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
})
|
||||
|
||||
const serverProjectInstallContext = computed(
|
||||
() =>
|
||||
!!serverInstallContent.serverContextServerData.value &&
|
||||
['modpack', 'mod', 'plugin', 'datapack'].includes(data.value?.project_type),
|
||||
)
|
||||
const serverProjectSelected = computed(
|
||||
() => !!data.value && serverInstallContent.queuedServerInstallProjectIds.value.has(data.value.id),
|
||||
)
|
||||
const serverProjectInstalled = computed(
|
||||
() =>
|
||||
!!data.value &&
|
||||
(serverInstallContent.serverContentProjectIds.value.has(data.value.id) ||
|
||||
serverInstallContent.serverContextServerData.value?.upstream?.project_id === data.value.id),
|
||||
)
|
||||
const installButtonLoading = computed(
|
||||
() => installing.value || serverInstallContent.isInstallingQueuedServerInstalls.value,
|
||||
)
|
||||
const installButtonValidating = computed(
|
||||
() =>
|
||||
serverProjectInstallContext.value &&
|
||||
installing.value &&
|
||||
data.value?.project_type !== 'modpack' &&
|
||||
!serverInstallContent.isInstallingQueuedServerInstalls.value,
|
||||
)
|
||||
const installButtonInstalled = computed(() =>
|
||||
serverProjectInstallContext.value ? serverProjectInstalled.value : installed.value,
|
||||
)
|
||||
const installButtonDisabled = computed(
|
||||
() => installButtonInstalled.value || installButtonLoading.value,
|
||||
)
|
||||
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 [allLoaders, allGameVersions] = await Promise.all([
|
||||
@@ -499,6 +670,55 @@ watch(
|
||||
)
|
||||
|
||||
async function install(version) {
|
||||
if (serverProjectInstallContext.value && data.value) {
|
||||
if (serverProjectSelected.value) {
|
||||
serverInstallContent.removeQueuedServerInstall(data.value.id)
|
||||
return
|
||||
}
|
||||
if (installButtonDisabled.value) return
|
||||
|
||||
installing.value = true
|
||||
try {
|
||||
const contentType = data.value.project_type
|
||||
await requestInstall({
|
||||
project: {
|
||||
...data.value,
|
||||
project_id: data.value.id,
|
||||
icon_url: data.value.icon_url,
|
||||
},
|
||||
contentType,
|
||||
mode: contentType === 'modpack' ? 'immediate' : 'queue',
|
||||
selectedFilters: [],
|
||||
providedFilters: [],
|
||||
overriddenProvidedFilterTypes: [],
|
||||
targetPreferences: getTargetInstallPreferences(
|
||||
{
|
||||
gameVersion: serverInstallContent.serverContextServerData.value?.mc_version,
|
||||
loader: serverInstallContent.serverContextServerData.value?.loader,
|
||||
},
|
||||
contentType,
|
||||
),
|
||||
getProjectVersions: async () => versions.value,
|
||||
queue: {
|
||||
get: serverInstallContent.getQueuedServerInstallPlans,
|
||||
set: serverInstallContent.setQueuedServerInstallPlans,
|
||||
},
|
||||
install: (plan) =>
|
||||
serverInstallContent.openServerModpackInstallFlow({
|
||||
projectId: plan.projectId,
|
||||
versionId: plan.versionId,
|
||||
name: plan.project.title ?? plan.project.name ?? data.value.title,
|
||||
iconUrl: plan.project.icon_url ?? undefined,
|
||||
}),
|
||||
})
|
||||
} catch (err) {
|
||||
handleError(err)
|
||||
} finally {
|
||||
installing.value = false
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
installing.value = true
|
||||
await installVersion(
|
||||
data.value.id,
|
||||
|
||||
@@ -176,9 +176,10 @@ import {
|
||||
ButtonStyled,
|
||||
Card,
|
||||
CopyCode,
|
||||
useFormatBytes,
|
||||
useFormatDateTime,
|
||||
} from '@modrinth/ui'
|
||||
import { formatBytes, renderString } from '@modrinth/utils'
|
||||
import { renderString } from '@modrinth/utils'
|
||||
import { computed, ref, watch } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
|
||||
@@ -191,6 +192,7 @@ const formatDateTime = useFormatDateTime({
|
||||
timeStyle: 'short',
|
||||
dateStyle: 'long',
|
||||
})
|
||||
const formatBytes = useFormatBytes()
|
||||
|
||||
const breadcrumbs = useBreadcrumbs()
|
||||
|
||||
|
||||
@@ -430,6 +430,7 @@ export function createContentInstall(opts: {
|
||||
await installVersionDependencies(
|
||||
profile,
|
||||
version,
|
||||
'dependency',
|
||||
(depProject: Labrinth.Projects.v2.Project, depVersion?: Labrinth.Versions.v2.Version) => {
|
||||
addInstallingItem(instance.id, depProject, depVersion)
|
||||
installedProjectIds.push(depProject.id)
|
||||
@@ -485,10 +486,10 @@ export function createContentInstall(opts: {
|
||||
if (!id) return
|
||||
|
||||
await add_project_from_version(id, version.id, 'standalone')
|
||||
await opts.router.push(`/instance/${encodeURIComponent(id)}/`)
|
||||
await opts.router.push(`/instance/${encodeURIComponent(id)}`)
|
||||
|
||||
const instance = await get(id)
|
||||
await installVersionDependencies(instance, version)
|
||||
await installVersionDependencies(instance, version, 'dependency')
|
||||
|
||||
trackEvent('InstanceCreate', {
|
||||
source: 'ProjectInstallModal',
|
||||
@@ -512,7 +513,7 @@ export function createContentInstall(opts: {
|
||||
|
||||
function handleNavigate(instance: ContentInstallInstance) {
|
||||
modalRef?.hide()
|
||||
opts.router.push(`/instance/${encodeURIComponent(instance.id)}/`)
|
||||
opts.router.push(`/instance/${encodeURIComponent(instance.id)}`)
|
||||
}
|
||||
|
||||
function handleCancel() {
|
||||
@@ -589,6 +590,7 @@ export function createContentInstall(opts: {
|
||||
await installVersionDependencies(
|
||||
instance,
|
||||
version,
|
||||
'dependency',
|
||||
(
|
||||
depProject: Labrinth.Projects.v2.Project,
|
||||
depVersion?: Labrinth.Versions.v2.Version,
|
||||
@@ -664,7 +666,7 @@ export function createContentInstall(opts: {
|
||||
},
|
||||
handleModpackDuplicateGoToInstance(instancePath: string) {
|
||||
pendingModpackInstall = null
|
||||
opts.router.push(`/instance/${encodeURIComponent(instancePath)}/`)
|
||||
opts.router.push(`/instance/${encodeURIComponent(instancePath)}`)
|
||||
},
|
||||
setIncompatibilityWarningModal(ref: IncompatibilityWarningModalRef) {
|
||||
incompatibilityWarningModalRef = ref
|
||||
|
||||
@@ -1,6 +1,17 @@
|
||||
import { provideFilePicker } from '@modrinth/ui'
|
||||
import { convertFileSrc } from '@tauri-apps/api/core'
|
||||
import { open } from '@tauri-apps/plugin-dialog'
|
||||
import { readFile } from '@tauri-apps/plugin-fs'
|
||||
|
||||
function getFileName(path: string, fallback: string) {
|
||||
return path.split(/[\\/]/).pop() || fallback
|
||||
}
|
||||
|
||||
async function createFileFromPath(path: string, fallbackName: string, type?: string) {
|
||||
const bytes = await readFile(path)
|
||||
const name = getFileName(path, fallbackName)
|
||||
return new File([bytes], name, type ? { type } : undefined)
|
||||
}
|
||||
|
||||
export function setupFilePickerProvider() {
|
||||
provideFilePicker({
|
||||
@@ -12,8 +23,7 @@ export function setupFilePickerProvider() {
|
||||
if (!result) return null
|
||||
const path = result.path ?? result
|
||||
if (!path) return null
|
||||
const name = path.split(/[\\/]/).pop() || 'icon'
|
||||
const file = new File([], name)
|
||||
const file = await createFileFromPath(path, 'icon')
|
||||
return { file, path, previewUrl: convertFileSrc(path) }
|
||||
},
|
||||
async pickModpackFile() {
|
||||
@@ -24,8 +34,11 @@ export function setupFilePickerProvider() {
|
||||
if (!result) return null
|
||||
const path = result.path ?? result
|
||||
if (!path) return null
|
||||
const name = path.split(/[\\/]/).pop() || 'modpack.mrpack'
|
||||
const file = new File([], name)
|
||||
const file = await createFileFromPath(
|
||||
path,
|
||||
'modpack.mrpack',
|
||||
'application/x-modrinth-modpack+zip',
|
||||
)
|
||||
return { file, path, previewUrl: '' }
|
||||
},
|
||||
})
|
||||
|
||||
@@ -1,22 +1,32 @@
|
||||
import type { Archon, Labrinth } from '@modrinth/api-client'
|
||||
import type { AbstractModrinthClient, Archon, Labrinth } from '@modrinth/api-client'
|
||||
import {
|
||||
addPendingServerContentInstalls,
|
||||
type BrowseInstallPlan,
|
||||
type BrowseSelectedProject,
|
||||
createContext,
|
||||
type CreationFlowContextValue,
|
||||
flushInstallQueue,
|
||||
injectModrinthClient,
|
||||
injectNotificationManager,
|
||||
type PendingServerContentInstall,
|
||||
type PendingServerContentInstallType,
|
||||
readPendingServerContentInstalls,
|
||||
removePendingServerContentInstall,
|
||||
writePendingServerContentInstallBaseline,
|
||||
} from '@modrinth/ui'
|
||||
import { computed, type ComputedRef, nextTick, type Ref, ref, watch } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
|
||||
type ServerFlowFrom = 'onboarding' | 'reset-server'
|
||||
type ServerInstallableType = 'modpack' | 'mod' | 'plugin' | 'datapack'
|
||||
|
||||
type InstallableSearchResult = Labrinth.Search.v3.ResultSearchProject & {
|
||||
title?: string
|
||||
installing?: boolean
|
||||
installed?: boolean
|
||||
}
|
||||
type PendingServerContentInstallInput = Omit<PendingServerContentInstall, 'createdAt'>
|
||||
|
||||
interface ServerModpackSelectionRequest {
|
||||
export interface ServerModpackSelectionRequest {
|
||||
projectId: string
|
||||
versionId: string
|
||||
name: string
|
||||
@@ -40,9 +50,19 @@ export interface ServerInstallContentContext {
|
||||
effectiveServerWorldId: ComputedRef<string | null>
|
||||
serverContextServerData: Ref<Archon.Servers.v0.Server | null>
|
||||
serverContentProjectIds: Ref<Set<string>>
|
||||
queuedServerInstallProjectIds: ComputedRef<Set<string>>
|
||||
queuedServerInstallCount: ComputedRef<number>
|
||||
selectedServerInstallProjects: ComputedRef<BrowseSelectedProject[]>
|
||||
isInstallingQueuedServerInstalls: Ref<boolean>
|
||||
queuedInstallProgress: Ref<{ completed: number; total: number }>
|
||||
serverBackUrl: ComputedRef<string>
|
||||
serverBackLabel: ComputedRef<string>
|
||||
serverBrowseHeading: ComputedRef<string>
|
||||
clearQueuedServerInstalls: () => void
|
||||
removeQueuedServerInstall: (projectId: string) => void
|
||||
flushQueuedServerInstalls: () => Promise<boolean>
|
||||
discardQueuedServerInstallsAndBack: () => Promise<void>
|
||||
installQueuedServerInstallsAndBack: () => Promise<boolean>
|
||||
initServerContext: () => Promise<void>
|
||||
watchServerContextChanges: () => void
|
||||
searchServerModpacks: (
|
||||
@@ -51,7 +71,11 @@ export interface ServerInstallContentContext {
|
||||
) => Promise<Labrinth.Projects.v2.SearchResult>
|
||||
getServerProjectVersions: (projectId: string) => Promise<{ id: string }[]>
|
||||
enforceSetupModpackRoute: (currentProjectType: string | undefined) => void
|
||||
installProjectToServer: (project: InstallableSearchResult) => Promise<boolean>
|
||||
getQueuedServerInstallPlans: () => Map<string, BrowseInstallPlan<InstallableSearchResult>>
|
||||
setQueuedServerInstallPlans: (
|
||||
plans: Map<string, BrowseInstallPlan<InstallableSearchResult>>,
|
||||
) => void
|
||||
openServerModpackInstallFlow: (request: ServerModpackSelectionRequest) => Promise<void>
|
||||
onServerFlowBack: () => void
|
||||
handleServerModpackFlowCreate: (config: CreationFlowContextValue) => Promise<void>
|
||||
markServerProjectInstalled: (id: string) => void
|
||||
@@ -65,6 +89,145 @@ function readQueryString(value: unknown): string | null {
|
||||
return typeof value === 'string' && value.length > 0 ? value : null
|
||||
}
|
||||
|
||||
function getQueueStorageKey(serverId: string | null, worldId: string | null) {
|
||||
if (!serverId || !worldId) return null
|
||||
return `server-install-queue:${serverId}:${worldId}`
|
||||
}
|
||||
|
||||
function readStoredQueue(serverId: string | null, worldId: string | null) {
|
||||
const key = getQueueStorageKey(serverId, worldId)
|
||||
if (!key) return new Map<string, BrowseInstallPlan<InstallableSearchResult>>()
|
||||
try {
|
||||
const raw = localStorage.getItem(key)
|
||||
if (!raw) return new Map<string, BrowseInstallPlan<InstallableSearchResult>>()
|
||||
return new Map<string, BrowseInstallPlan<InstallableSearchResult>>(JSON.parse(raw))
|
||||
} catch {
|
||||
return new Map<string, BrowseInstallPlan<InstallableSearchResult>>()
|
||||
}
|
||||
}
|
||||
|
||||
function writeStoredQueue(
|
||||
serverId: string | null,
|
||||
worldId: string | null,
|
||||
plans: Map<string, BrowseInstallPlan<InstallableSearchResult>>,
|
||||
) {
|
||||
const key = getQueueStorageKey(serverId, worldId)
|
||||
if (!key) return
|
||||
if (plans.size === 0) {
|
||||
localStorage.removeItem(key)
|
||||
return
|
||||
}
|
||||
localStorage.setItem(key, JSON.stringify(Array.from(plans.entries())))
|
||||
}
|
||||
|
||||
function getQueuedInstallOwnerFallback(project: InstallableSearchResult) {
|
||||
if (project.organization) {
|
||||
const ownerId = project.organization_id ?? project.organization
|
||||
return {
|
||||
id: ownerId,
|
||||
name: project.organization,
|
||||
type: 'organization' as const,
|
||||
link: `https://modrinth.com/organization/${ownerId}`,
|
||||
}
|
||||
}
|
||||
|
||||
if (!project.author) return null
|
||||
|
||||
const ownerId = project.author_id ?? project.author
|
||||
return {
|
||||
id: ownerId,
|
||||
name: project.author,
|
||||
type: 'user' as const,
|
||||
link: `https://modrinth.com/user/${ownerId}`,
|
||||
}
|
||||
}
|
||||
|
||||
async function getQueuedInstallOwner(
|
||||
client: AbstractModrinthClient,
|
||||
project: InstallableSearchResult,
|
||||
) {
|
||||
const fallback = getQueuedInstallOwnerFallback(project)
|
||||
|
||||
try {
|
||||
if (project.organization) {
|
||||
const organization = await client.labrinth.projects_v3.getOrganization(project.project_id)
|
||||
if (organization) {
|
||||
return {
|
||||
id: organization.id,
|
||||
name: organization.name,
|
||||
type: 'organization' as const,
|
||||
avatar_url: organization.icon_url ?? undefined,
|
||||
link: `https://modrinth.com/organization/${organization.slug}`,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const members = await client.labrinth.projects_v3.getMembers(project.project_id)
|
||||
const owner =
|
||||
members.find((member) => member.user.id === project.author_id)?.user ??
|
||||
members.find((member) => member.is_owner || member.role === 'Owner')?.user ??
|
||||
members[0]?.user
|
||||
|
||||
if (owner) {
|
||||
return {
|
||||
id: owner.id,
|
||||
name: owner.username,
|
||||
type: 'user' as const,
|
||||
avatar_url: owner.avatar_url,
|
||||
link: `https://modrinth.com/user/${owner.username}`,
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
return fallback
|
||||
}
|
||||
|
||||
return fallback
|
||||
}
|
||||
|
||||
function getQueuedAddonInstallPlans(
|
||||
plans: Map<string, BrowseInstallPlan<InstallableSearchResult>>,
|
||||
) {
|
||||
return Array.from(plans.values()).filter((plan) => plan.contentType !== 'modpack')
|
||||
}
|
||||
|
||||
function getQueuedInstallPlaceholder(
|
||||
plan: BrowseInstallPlan<InstallableSearchResult>,
|
||||
owner: PendingServerContentInstallInput['owner'],
|
||||
): PendingServerContentInstallInput {
|
||||
const project = plan.project as InstallableSearchResult & { slug?: string | null }
|
||||
return {
|
||||
projectId: plan.projectId,
|
||||
versionId: plan.versionId,
|
||||
contentType: plan.contentType as PendingServerContentInstallType,
|
||||
title: project.title ?? project.name ?? 'Project',
|
||||
versionName: plan.versionName ?? null,
|
||||
versionNumber: plan.versionNumber ?? null,
|
||||
fileName: plan.fileName ?? null,
|
||||
owner,
|
||||
slug: project.slug ?? plan.projectId,
|
||||
iconUrl: project.icon_url ?? null,
|
||||
}
|
||||
}
|
||||
|
||||
function getQueuedInstallPlaceholderFallbacks(
|
||||
plans: Map<string, BrowseInstallPlan<InstallableSearchResult>>,
|
||||
) {
|
||||
return getQueuedAddonInstallPlans(plans).map((plan) =>
|
||||
getQueuedInstallPlaceholder(plan, getQueuedInstallOwnerFallback(plan.project)),
|
||||
)
|
||||
}
|
||||
|
||||
async function getQueuedInstallPlaceholders(
|
||||
client: AbstractModrinthClient,
|
||||
plans: Map<string, BrowseInstallPlan<InstallableSearchResult>>,
|
||||
) {
|
||||
return Promise.all(
|
||||
getQueuedAddonInstallPlans(plans).map(async (plan) =>
|
||||
getQueuedInstallPlaceholder(plan, await getQueuedInstallOwner(client, plan.project)),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
export function createServerInstallContent(opts: {
|
||||
serverSetupModalRef: Ref<ServerSetupModalHandle | null>
|
||||
}) {
|
||||
@@ -72,7 +235,7 @@ export function createServerInstallContent(opts: {
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const client = injectModrinthClient()
|
||||
const { handleError } = injectNotificationManager()
|
||||
const { addNotification, handleError } = injectNotificationManager()
|
||||
|
||||
const serverIdQuery = computed(() => readQueryString(route.query.sid))
|
||||
const worldIdQuery = computed(() => readQueryString(route.query.wid))
|
||||
@@ -90,8 +253,22 @@ export function createServerInstallContent(opts: {
|
||||
const serverContextWorldId = ref<string | null>(worldIdQuery.value)
|
||||
const serverContextServerData = ref<Archon.Servers.v0.Server | null>(null)
|
||||
const serverContentProjectIds = ref<Set<string>>(new Set())
|
||||
const serverContentInstallKeys = ref<Set<string>>(new Set())
|
||||
const queuedServerInstalls = ref<Map<string, BrowseInstallPlan<InstallableSearchResult>>>(
|
||||
new Map(),
|
||||
)
|
||||
const queuedServerInstallProjectIds = computed(() => new Set(queuedServerInstalls.value.keys()))
|
||||
const queuedServerInstallCount = computed(() => queuedServerInstalls.value.size)
|
||||
const selectedServerInstallProjects = computed<BrowseSelectedProject[]>(() =>
|
||||
Array.from(queuedServerInstalls.value.values()).map((plan) => ({
|
||||
id: plan.projectId,
|
||||
name: plan.project.title ?? plan.project.name ?? 'Project',
|
||||
iconUrl: plan.project.icon_url ?? null,
|
||||
})),
|
||||
)
|
||||
const isInstallingQueuedServerInstalls = ref(false)
|
||||
const queuedInstallProgress = ref({ completed: 0, total: 0 })
|
||||
const effectiveServerWorldId = computed(() => worldIdQuery.value ?? serverContextWorldId.value)
|
||||
|
||||
const serverBackUrl = computed(() => {
|
||||
const sid = serverIdQuery.value
|
||||
if (!sid) return '/hosting/manage'
|
||||
@@ -110,9 +287,9 @@ export function createServerInstallContent(opts: {
|
||||
})
|
||||
const serverBrowseHeading = computed(() => {
|
||||
if (serverFlowFrom.value === 'reset-server') {
|
||||
return 'Select modpack to install after reset'
|
||||
return 'Selecting modpack to install after reset'
|
||||
}
|
||||
return 'Install content to server'
|
||||
return 'Installing content'
|
||||
})
|
||||
|
||||
async function resolveServerContextWorldId(serverId: string) {
|
||||
@@ -134,7 +311,11 @@ export function createServerInstallContent(opts: {
|
||||
.map((addon) => addon.project_id)
|
||||
.filter((projectId): projectId is string => !!projectId),
|
||||
)
|
||||
const keys = new Set(
|
||||
(content.addons ?? []).map((addon) => addon.project_id ?? addon.filename),
|
||||
)
|
||||
serverContentProjectIds.value = ids
|
||||
serverContentInstallKeys.value = keys
|
||||
} catch (err) {
|
||||
handleError(err as Error)
|
||||
}
|
||||
@@ -159,6 +340,7 @@ export function createServerInstallContent(opts: {
|
||||
}
|
||||
|
||||
if (resolvedWorldId) {
|
||||
queuedServerInstalls.value = readStoredQueue(sid, resolvedWorldId)
|
||||
await refreshServerInstalledContent(sid, resolvedWorldId)
|
||||
}
|
||||
}
|
||||
@@ -168,11 +350,15 @@ export function createServerInstallContent(opts: {
|
||||
if (!sid) {
|
||||
serverContextServerData.value = null
|
||||
serverContentProjectIds.value = new Set()
|
||||
serverContentInstallKeys.value = new Set()
|
||||
setQueuedServerInstallPlans(new Map())
|
||||
return
|
||||
}
|
||||
|
||||
if (sid !== prevSid) {
|
||||
serverContentProjectIds.value = new Set()
|
||||
serverContentInstallKeys.value = new Set()
|
||||
queuedServerInstalls.value = readStoredQueue(sid, wid)
|
||||
try {
|
||||
serverContextServerData.value = await client.archon.servers_v0.get(sid)
|
||||
} catch (err) {
|
||||
@@ -180,28 +366,16 @@ export function createServerInstallContent(opts: {
|
||||
}
|
||||
}
|
||||
|
||||
if (wid !== prevWid) {
|
||||
queuedServerInstalls.value = readStoredQueue(sid, wid)
|
||||
}
|
||||
|
||||
if (wid && (sid !== prevSid || wid !== prevWid)) {
|
||||
await refreshServerInstalledContent(sid, wid)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function normalizeLoader(loader: string) {
|
||||
return loader.toLowerCase().replaceAll('_', '').replaceAll('-', '').replaceAll(' ', '')
|
||||
}
|
||||
|
||||
function getCompatibleLoaders(loader: string) {
|
||||
const normalized = normalizeLoader(loader)
|
||||
if (!normalized) return new Set<string>()
|
||||
if (normalized === 'paper' || normalized === 'purpur' || normalized === 'spigot') {
|
||||
return new Set(['paper', 'purpur', 'spigot', 'bukkit'])
|
||||
}
|
||||
if (normalized === 'neoforge' || normalized === 'neo') {
|
||||
return new Set(['neoforge', 'neo'])
|
||||
}
|
||||
return new Set([normalized])
|
||||
}
|
||||
|
||||
function enforceSetupModpackRoute(currentProjectType: string | undefined) {
|
||||
if (!isSetupServerContext.value || currentProjectType === 'modpack') return
|
||||
router.replace({
|
||||
@@ -248,82 +422,132 @@ export function createServerInstallContent(opts: {
|
||||
ctx.modal.value?.setStage('final-config')
|
||||
}
|
||||
|
||||
function getCurrentServerInstallType(): ServerInstallableType {
|
||||
const raw = Array.isArray(route.params.projectType)
|
||||
? route.params.projectType[0]
|
||||
: route.params.projectType
|
||||
if (raw === 'modpack' || raw === 'mod' || raw === 'plugin' || raw === 'datapack') {
|
||||
return raw
|
||||
}
|
||||
throw new Error('This content type cannot be installed to a server from browse.')
|
||||
function clearQueuedServerInstalls() {
|
||||
setQueuedServerInstallPlans(new Map())
|
||||
}
|
||||
|
||||
async function installProjectToServer(project: InstallableSearchResult) {
|
||||
const contentType = getCurrentServerInstallType()
|
||||
const sid = serverIdQuery.value
|
||||
const wid = effectiveServerWorldId.value
|
||||
if (!sid || !wid) {
|
||||
throw new Error('No server world is available for install.')
|
||||
}
|
||||
function removeQueuedServerInstall(projectId: string) {
|
||||
const nextPlans = new Map(queuedServerInstalls.value)
|
||||
nextPlans.delete(projectId)
|
||||
setQueuedServerInstallPlans(nextPlans)
|
||||
}
|
||||
|
||||
if (contentType === 'modpack') {
|
||||
const versions = await client.labrinth.versions_v2.getProjectVersions(project.project_id, {
|
||||
include_changelog: false,
|
||||
})
|
||||
const versionId = versions[0]?.id ?? project.version_id
|
||||
if (!versionId) {
|
||||
throw new Error('No version found for this modpack')
|
||||
}
|
||||
async function flushQueuedServerInstalls(
|
||||
serverId: string | null = serverIdQuery.value,
|
||||
worldId: string | null = effectiveServerWorldId.value,
|
||||
) {
|
||||
if (queuedServerInstalls.value.size === 0) return true
|
||||
if (isInstallingQueuedServerInstalls.value) return false
|
||||
|
||||
await openServerModpackInstallFlow({
|
||||
projectId: project.project_id,
|
||||
versionId,
|
||||
name: project.name,
|
||||
iconUrl: project.icon_url ?? undefined,
|
||||
})
|
||||
if (!serverId || !worldId) {
|
||||
handleError(new Error('No server world is available for install.'))
|
||||
return false
|
||||
}
|
||||
|
||||
const versions = await client.labrinth.versions_v2.getProjectVersions(project.project_id, {
|
||||
include_changelog: false,
|
||||
})
|
||||
const serverLoader = (serverContextServerData.value?.loader ?? '').toLowerCase()
|
||||
const serverGameVersion = (serverContextServerData.value?.mc_version ?? '').trim()
|
||||
const compatibleLoaders = getCompatibleLoaders(serverLoader)
|
||||
|
||||
const hasGameVersionMatch = (version: Labrinth.Versions.v2.Version) =>
|
||||
!serverGameVersion || version.game_versions.includes(serverGameVersion)
|
||||
const hasLoaderMatch = (version: Labrinth.Versions.v2.Version) => {
|
||||
if (contentType === 'datapack') return true
|
||||
if (compatibleLoaders.size === 0) return true
|
||||
return version.loaders.some((loader) => compatibleLoaders.has(normalizeLoader(loader)))
|
||||
const installedProjectIds = new Set<string>()
|
||||
isInstallingQueuedServerInstalls.value = true
|
||||
queuedInstallProgress.value = {
|
||||
completed: 0,
|
||||
total: queuedServerInstalls.value.size,
|
||||
}
|
||||
|
||||
let matchingVersion = versions.find(
|
||||
(version) => hasGameVersionMatch(version) && hasLoaderMatch(version),
|
||||
)
|
||||
if (!matchingVersion) {
|
||||
matchingVersion = versions.find((version) => hasLoaderMatch(version))
|
||||
try {
|
||||
const result = await flushInstallQueue({
|
||||
queue: {
|
||||
get: () => queuedServerInstalls.value,
|
||||
set: (plans: Map<string, BrowseInstallPlan<InstallableSearchResult>>) => {
|
||||
queuedServerInstalls.value = plans
|
||||
writeStoredQueue(serverId, worldId, plans)
|
||||
},
|
||||
},
|
||||
install: async (plan) => {
|
||||
await client.archon.content_v1.addAddon(serverId, worldId, {
|
||||
project_id: plan.projectId,
|
||||
version_id: plan.versionId,
|
||||
})
|
||||
installedProjectIds.add(plan.projectId)
|
||||
},
|
||||
onError: (error, plan) => {
|
||||
removePendingServerContentInstall(serverId, worldId, plan.projectId)
|
||||
handleError(error as Error)
|
||||
},
|
||||
onProgress: (completed, total) => {
|
||||
queuedInstallProgress.value = { completed, total }
|
||||
},
|
||||
})
|
||||
|
||||
if (installedProjectIds.size > 0) {
|
||||
serverContentProjectIds.value = new Set([
|
||||
...serverContentProjectIds.value,
|
||||
...installedProjectIds,
|
||||
])
|
||||
serverContentInstallKeys.value = new Set([
|
||||
...serverContentInstallKeys.value,
|
||||
...installedProjectIds,
|
||||
])
|
||||
}
|
||||
|
||||
return result.ok
|
||||
} finally {
|
||||
isInstallingQueuedServerInstalls.value = false
|
||||
queuedInstallProgress.value = { completed: 0, total: 0 }
|
||||
}
|
||||
if (!matchingVersion) {
|
||||
matchingVersion = versions.find((version) => hasGameVersionMatch(version))
|
||||
}
|
||||
|
||||
async function discardQueuedServerInstallsAndBack() {
|
||||
clearQueuedServerInstalls()
|
||||
await router.push(serverBackUrl.value)
|
||||
}
|
||||
|
||||
async function installQueuedServerInstallsAndBack() {
|
||||
const sid = serverIdQuery.value
|
||||
const wid = effectiveServerWorldId.value
|
||||
const backUrl = serverBackUrl.value
|
||||
const plans = new Map(queuedServerInstalls.value)
|
||||
|
||||
if (sid && wid) {
|
||||
writePendingServerContentInstallBaseline(sid, wid, serverContentInstallKeys.value)
|
||||
addPendingServerContentInstalls(sid, wid, getQueuedInstallPlaceholderFallbacks(plans))
|
||||
void getQueuedInstallPlaceholders(client, plans)
|
||||
.then((items) => {
|
||||
const pendingProjectIds = new Set(
|
||||
readPendingServerContentInstalls(sid, wid).map((item) => item.projectId),
|
||||
)
|
||||
addPendingServerContentInstalls(
|
||||
sid,
|
||||
wid,
|
||||
items.filter((item) => pendingProjectIds.has(item.projectId)),
|
||||
)
|
||||
})
|
||||
.catch((err) => handleError(err as Error))
|
||||
}
|
||||
if (!matchingVersion) {
|
||||
matchingVersion = versions[0]
|
||||
}
|
||||
if (!matchingVersion) {
|
||||
throw new Error('No installable version was found for this project.')
|
||||
await router.push(backUrl)
|
||||
|
||||
const ok = await flushQueuedServerInstalls(sid, wid)
|
||||
if (!ok) {
|
||||
queuedServerInstalls.value = new Map()
|
||||
writeStoredQueue(sid, wid, new Map())
|
||||
addNotification({
|
||||
type: 'error',
|
||||
title: 'Some projects failed to install',
|
||||
text: 'Failed projects were not added. You can try installing them again.',
|
||||
})
|
||||
}
|
||||
|
||||
await client.archon.content_v1.addAddon(sid, wid, {
|
||||
project_id: matchingVersion.project_id,
|
||||
version_id: matchingVersion.id,
|
||||
})
|
||||
|
||||
serverContentProjectIds.value = new Set([...serverContentProjectIds.value, project.project_id])
|
||||
return true
|
||||
}
|
||||
|
||||
function getQueuedServerInstallPlans() {
|
||||
return queuedServerInstalls.value
|
||||
}
|
||||
|
||||
function setQueuedServerInstallPlans(
|
||||
plans: Map<string, BrowseInstallPlan<InstallableSearchResult>>,
|
||||
) {
|
||||
queuedServerInstalls.value = plans
|
||||
writeStoredQueue(serverIdQuery.value, effectiveServerWorldId.value, plans)
|
||||
}
|
||||
|
||||
function onServerFlowBack() {
|
||||
serverSetupModalRef.value?.hide()
|
||||
}
|
||||
@@ -377,15 +601,27 @@ export function createServerInstallContent(opts: {
|
||||
effectiveServerWorldId,
|
||||
serverContextServerData,
|
||||
serverContentProjectIds,
|
||||
queuedServerInstallProjectIds,
|
||||
queuedServerInstallCount,
|
||||
selectedServerInstallProjects,
|
||||
isInstallingQueuedServerInstalls,
|
||||
queuedInstallProgress,
|
||||
serverBackUrl,
|
||||
serverBackLabel,
|
||||
serverBrowseHeading,
|
||||
clearQueuedServerInstalls,
|
||||
removeQueuedServerInstall,
|
||||
flushQueuedServerInstalls,
|
||||
discardQueuedServerInstallsAndBack,
|
||||
installQueuedServerInstallsAndBack,
|
||||
initServerContext,
|
||||
watchServerContextChanges,
|
||||
searchServerModpacks,
|
||||
getServerProjectVersions,
|
||||
enforceSetupModpackRoute,
|
||||
installProjectToServer,
|
||||
getQueuedServerInstallPlans,
|
||||
setQueuedServerInstallPlans,
|
||||
openServerModpackInstallFlow,
|
||||
onServerFlowBack,
|
||||
handleServerModpackFlowCreate,
|
||||
markServerProjectInstalled,
|
||||
|
||||
@@ -48,7 +48,7 @@ export const isVersionCompatible = (version, project, instance) => {
|
||||
)
|
||||
}
|
||||
|
||||
export const installVersionDependencies = async (profile, version, onDepInstalling) => {
|
||||
export const installVersionDependencies = async (profile, version, reason, onDepInstalling) => {
|
||||
const projectNames = new Map()
|
||||
const storeProjectName = (p) => {
|
||||
if (p?.id && p.title) projectNames.set(p.id, p.title)
|
||||
@@ -177,7 +177,7 @@ export const installVersionDependencies = async (profile, version, onDepInstalli
|
||||
const batch = queuedInstalls.slice(i, i + batchSize)
|
||||
await Promise.all(
|
||||
batch.map(async ({ versionId }) => {
|
||||
await add_project_from_version(profile.path, versionId, 'dependency')
|
||||
await add_project_from_version(profile.path, versionId, reason)
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 284 KiB |
+253
-19
@@ -1,4 +1,6 @@
|
||||
use std::collections::HashSet;
|
||||
use std::sync::Arc;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::time::{Duration, Instant};
|
||||
use tauri::plugin::TauriPlugin;
|
||||
use tauri::{Manager, PhysicalPosition, PhysicalSize, Runtime};
|
||||
@@ -14,6 +16,148 @@ pub struct AdsState {
|
||||
}
|
||||
|
||||
const AD_LINK: &str = "https://modrinth.com/wrapper/app-ads-cookie";
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
const ADS_USER_AGENT: &str = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36";
|
||||
|
||||
#[cfg(windows)]
|
||||
fn ads_user_agent_override_params() -> String {
|
||||
serde_json::json!({
|
||||
"userAgent": ADS_USER_AGENT,
|
||||
"platform": "Win32",
|
||||
"userAgentMetadata": {
|
||||
"brands": [
|
||||
{ "brand": "Chromium", "version": "128" },
|
||||
{ "brand": "Google Chrome", "version": "128" },
|
||||
{ "brand": "Not=A?Brand", "version": "99" },
|
||||
],
|
||||
"fullVersion": "128.0.0.0",
|
||||
"fullVersionList": [
|
||||
{ "brand": "Chromium", "version": "128.0.0.0" },
|
||||
{ "brand": "Google Chrome", "version": "128.0.0.0" },
|
||||
{ "brand": "Not=A?Brand", "version": "99.0.0.0" },
|
||||
],
|
||||
"platform": "Windows",
|
||||
"platformVersion": "10.0.0",
|
||||
"architecture": "x86",
|
||||
"bitness": "64",
|
||||
"model": "",
|
||||
"mobile": false,
|
||||
},
|
||||
})
|
||||
.to_string()
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
fn configure_ads_cookie_settings(
|
||||
core_webview2: &webview2_com::Microsoft::Web::WebView2::Win32::ICoreWebView2,
|
||||
) {
|
||||
use webview2_com::Microsoft::Web::WebView2::Win32::{
|
||||
COREWEBVIEW2_TRACKING_PREVENTION_LEVEL_NONE, ICoreWebView2,
|
||||
ICoreWebView2_13, ICoreWebView2Profile3,
|
||||
};
|
||||
use windows_core::Interface;
|
||||
|
||||
match core_webview2
|
||||
.cast::<ICoreWebView2_13>()
|
||||
.and_then(|core_webview2| unsafe { core_webview2.Profile() })
|
||||
.and_then(|profile| profile.cast::<ICoreWebView2Profile3>())
|
||||
{
|
||||
Ok(profile) => {
|
||||
if let Err(error) = unsafe {
|
||||
profile.SetPreferredTrackingPreventionLevel(
|
||||
COREWEBVIEW2_TRACKING_PREVENTION_LEVEL_NONE,
|
||||
)
|
||||
} {
|
||||
tracing::warn!(
|
||||
?error,
|
||||
"Failed to disable ads WebView2 tracking prevention"
|
||||
);
|
||||
}
|
||||
}
|
||||
Err(error) => {
|
||||
tracing::warn!(
|
||||
?error,
|
||||
"Failed to access ads WebView2 profile tracking prevention settings"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn set_webview_visible<R: Runtime>(
|
||||
webview: &tauri::Webview<R>,
|
||||
_visible: bool,
|
||||
) {
|
||||
webview
|
||||
.with_webview(
|
||||
#[allow(unused_variables)]
|
||||
move |wv| {
|
||||
#[cfg(windows)]
|
||||
{
|
||||
let controller = wv.controller();
|
||||
unsafe { controller.SetIsVisible(_visible) }.ok();
|
||||
}
|
||||
},
|
||||
)
|
||||
.ok();
|
||||
}
|
||||
|
||||
fn set_webview_visible_for_window<R: Runtime>(
|
||||
app: &tauri::AppHandle<R>,
|
||||
webview: &tauri::Webview<R>,
|
||||
visible: bool,
|
||||
) {
|
||||
let is_minimized = app
|
||||
.get_window("main")
|
||||
.and_then(|window| window.is_minimized().ok())
|
||||
.unwrap_or(false);
|
||||
|
||||
set_webview_visible(webview, visible && !is_minimized);
|
||||
}
|
||||
|
||||
fn sync_webview_visibility_for_main_window<R: Runtime>(
|
||||
app: &tauri::AppHandle<R>,
|
||||
main_window: &tauri::Window<R>,
|
||||
was_minimized: &AtomicBool,
|
||||
) {
|
||||
let is_minimized = main_window.is_minimized().unwrap_or(false);
|
||||
let was = was_minimized.load(Ordering::SeqCst);
|
||||
|
||||
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,
|
||||
Err(_) => false,
|
||||
}
|
||||
};
|
||||
|
||||
let mut webviews = Vec::new();
|
||||
let mut seen_webviews = HashSet::new();
|
||||
|
||||
for webview in main_window.webviews() {
|
||||
seen_webviews.insert(webview.label().to_string());
|
||||
webviews.push(webview);
|
||||
}
|
||||
|
||||
for webview in app.webviews().into_values() {
|
||||
if seen_webviews.insert(webview.label().to_string()) {
|
||||
webviews.push(webview);
|
||||
}
|
||||
}
|
||||
|
||||
for webview in webviews {
|
||||
let visible =
|
||||
!is_minimized && (webview.label() != "ads-window" || ads_visible);
|
||||
|
||||
set_webview_visible(&webview, visible);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init<R: Runtime>() -> TauriPlugin<R> {
|
||||
tauri::plugin::Builder::<R>::new("ads")
|
||||
@@ -30,10 +174,11 @@ pub fn init<R: Runtime>() -> TauriPlugin<R> {
|
||||
// visible when we refresh, the Aditude wrapper will not make any ad requests
|
||||
// unless Chromium reports the page as visible. The refresh does not reset the
|
||||
// visibility state.
|
||||
let app = app.clone();
|
||||
let refresh_app = app.clone();
|
||||
tauri::async_runtime::spawn(async move {
|
||||
loop {
|
||||
if let Some(webview) = app.webviews().get_mut("ads-window")
|
||||
if let Some(webview) =
|
||||
refresh_app.webviews().get_mut("ads-window")
|
||||
{
|
||||
let _ = webview.navigate(AD_LINK.parse().unwrap());
|
||||
}
|
||||
@@ -43,6 +188,34 @@ pub fn init<R: Runtime>() -> TauriPlugin<R> {
|
||||
}
|
||||
});
|
||||
|
||||
if let Some(main_window) = app.get_window("main") {
|
||||
let app_handle = app.clone();
|
||||
let event_window = main_window.clone();
|
||||
let was_minimized = Arc::new(AtomicBool::new(false));
|
||||
|
||||
main_window.on_window_event(move |_| {
|
||||
sync_webview_visibility_for_main_window(
|
||||
&app_handle,
|
||||
&event_window,
|
||||
&was_minimized,
|
||||
);
|
||||
|
||||
let delayed_app_handle = app_handle.clone();
|
||||
let delayed_event_window = event_window.clone();
|
||||
let delayed_was_minimized = was_minimized.clone();
|
||||
|
||||
tauri::async_runtime::spawn(async move {
|
||||
tokio::time::sleep(Duration::from_millis(100)).await;
|
||||
|
||||
sync_webview_visibility_for_main_window(
|
||||
&delayed_app_handle,
|
||||
&delayed_event_window,
|
||||
&delayed_was_minimized,
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Ok(())
|
||||
})
|
||||
.invoke_handler(tauri::generate_handler![
|
||||
@@ -103,31 +276,38 @@ pub async fn init_ads_window<R: Runtime>(
|
||||
webview.show().ok();
|
||||
webview.set_position(position).ok();
|
||||
webview.set_size(size).ok();
|
||||
set_webview_visible_for_window(&app, webview, true);
|
||||
} else {
|
||||
webview.hide().ok();
|
||||
webview
|
||||
.set_position(PhysicalPosition::new(-1000, -1000))
|
||||
.ok();
|
||||
set_webview_visible(webview, false);
|
||||
}
|
||||
|
||||
Some(webview.clone())
|
||||
} else if let Some(window) = app.get_window("main") {
|
||||
#[cfg(windows)]
|
||||
let webview_url =
|
||||
WebviewUrl::External("about:blank".parse().unwrap());
|
||||
#[cfg(not(windows))]
|
||||
let webview_url = WebviewUrl::External(AD_LINK.parse().unwrap());
|
||||
|
||||
let webview = window.add_child(
|
||||
tauri::webview::WebviewBuilder::new(
|
||||
"ads-window",
|
||||
WebviewUrl::External(
|
||||
AD_LINK.parse().unwrap(),
|
||||
),
|
||||
)
|
||||
.initialization_script_for_all_frames(include_str!("ads-init.js"))
|
||||
// We use a standard Chrome user agent for compatibility with our ad provider,
|
||||
// since Tauri is not recognized by ad providers by default.
|
||||
// Aditude has separately informed SSPs and IVT vendors that this traffic
|
||||
// originates from a desktop app.
|
||||
.user_agent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36")
|
||||
.zoom_hotkeys_enabled(false)
|
||||
.transparent(true)
|
||||
.on_new_window(|_, _| tauri::webview::NewWindowResponse::Deny),
|
||||
tauri::webview::WebviewBuilder::new("ads-window", webview_url)
|
||||
.initialization_script_for_all_frames(include_str!(
|
||||
"ads-init.js"
|
||||
))
|
||||
// We use a standard Chrome user agent for compatibility with our ad provider,
|
||||
// since Tauri is not recognized by ad providers by default.
|
||||
// Aditude has separately informed SSPs and IVT vendors that this traffic
|
||||
// originates from a desktop app.
|
||||
.user_agent(ADS_USER_AGENT)
|
||||
.zoom_hotkeys_enabled(false)
|
||||
.transparent(true)
|
||||
.on_new_window(|_, _| {
|
||||
tauri::webview::NewWindowResponse::Deny
|
||||
}),
|
||||
// set both the `hide`/`show` state and `position`,
|
||||
// to ensure that the webview is actually shown/hidden
|
||||
if state.shown {
|
||||
@@ -140,15 +320,68 @@ pub async fn init_ads_window<R: Runtime>(
|
||||
|
||||
if state.shown {
|
||||
webview.show().ok();
|
||||
set_webview_visible_for_window(&app, &webview, true);
|
||||
} else {
|
||||
webview.hide().ok();
|
||||
set_webview_visible(&webview, false);
|
||||
}
|
||||
|
||||
webview.with_webview(#[allow(unused_variables)] |webview2| {
|
||||
#[cfg(windows)]
|
||||
{
|
||||
use webview2_com::CallDevToolsProtocolMethodCompletedHandler;
|
||||
use webview2_com::Microsoft::Web::WebView2::Win32::ICoreWebView2_8;
|
||||
use windows_core::Interface;
|
||||
use windows_core::HSTRING;
|
||||
|
||||
let core_webview2 =
|
||||
unsafe { webview2.controller().CoreWebView2() };
|
||||
|
||||
if let Ok(core_webview2) = core_webview2 {
|
||||
configure_ads_cookie_settings(&core_webview2);
|
||||
|
||||
let navigate_webview = core_webview2.clone();
|
||||
let handler =
|
||||
CallDevToolsProtocolMethodCompletedHandler::create(
|
||||
Box::new(move |result: windows_core::Result<()>, _| {
|
||||
if let Err(error) = result {
|
||||
tracing::error!(
|
||||
?error,
|
||||
"Failed to override ads user-agent client hints"
|
||||
);
|
||||
}
|
||||
|
||||
unsafe {
|
||||
navigate_webview
|
||||
.Navigate(&HSTRING::from(AD_LINK))
|
||||
.ok();
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}) as Box<_>,
|
||||
);
|
||||
|
||||
unsafe {
|
||||
if let Err(error) = core_webview2
|
||||
.CallDevToolsProtocolMethod(
|
||||
&HSTRING::from(
|
||||
"Emulation.setUserAgentOverride",
|
||||
),
|
||||
&HSTRING::from(
|
||||
ads_user_agent_override_params(),
|
||||
),
|
||||
&handler,
|
||||
)
|
||||
{
|
||||
tracing::error!(
|
||||
?error,
|
||||
"Failed to install ads user-agent client hints override"
|
||||
);
|
||||
|
||||
core_webview2.Navigate(&HSTRING::from(AD_LINK)).ok();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let webview2_controller = webview2.controller();
|
||||
let Ok(webview2_8) = unsafe { webview2_controller.CoreWebView2() }
|
||||
@@ -166,9 +399,9 @@ pub async fn init_ads_window<R: Runtime>(
|
||||
None
|
||||
};
|
||||
|
||||
let Some(webview) = webview.clone() else {
|
||||
if webview.is_none() {
|
||||
return Ok(());
|
||||
};
|
||||
}
|
||||
|
||||
// tauri::async_runtime::spawn(async move {
|
||||
// loop {
|
||||
@@ -249,6 +482,7 @@ pub async fn show_ads_window<R: Runtime>(
|
||||
webview.set_size(size).ok();
|
||||
webview.set_position(position).ok();
|
||||
webview.show().ok();
|
||||
set_webview_visible_for_window(&app, webview, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -30,7 +30,22 @@
|
||||
"exceptionDomain": "",
|
||||
"frameworks": [],
|
||||
"providerShortName": null,
|
||||
"signingIdentity": null
|
||||
"signingIdentity": null,
|
||||
"dmg": {
|
||||
"background": "./dmg/dmg-background.png",
|
||||
"windowSize": {
|
||||
"width": 661,
|
||||
"height": 432
|
||||
},
|
||||
"appPosition": {
|
||||
"x": 188,
|
||||
"y": 212
|
||||
},
|
||||
"applicationFolderPosition": {
|
||||
"x": 475,
|
||||
"y": 212
|
||||
}
|
||||
}
|
||||
},
|
||||
"shortDescription": "",
|
||||
"linux": {
|
||||
|
||||
@@ -17,74 +17,60 @@
|
||||
</label>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { fileIsValid } from '~/helpers/fileUtils.js'
|
||||
<script setup lang="ts">
|
||||
import { useFormatBytes } from '@modrinth/ui'
|
||||
import { fileIsValid } from '@modrinth/utils'
|
||||
import { ref } from 'vue'
|
||||
|
||||
export default {
|
||||
components: {},
|
||||
props: {
|
||||
prompt: {
|
||||
type: String,
|
||||
default: 'Select file',
|
||||
},
|
||||
multiple: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
accept: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
prompt?: string
|
||||
multiple?: boolean
|
||||
accept?: string
|
||||
/**
|
||||
* The max file size in bytes
|
||||
*/
|
||||
maxSize: {
|
||||
type: Number,
|
||||
default: null,
|
||||
},
|
||||
showIcon: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
shouldAlwaysReset: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
longStyle: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
maxSize?: number | null
|
||||
showIcon?: boolean
|
||||
shouldAlwaysReset?: boolean
|
||||
longStyle?: boolean
|
||||
disabled?: boolean
|
||||
}>(),
|
||||
{
|
||||
prompt: 'Select file',
|
||||
multiple: false,
|
||||
showIcon: true,
|
||||
shouldAlwaysReset: false,
|
||||
longStyle: false,
|
||||
disabled: false,
|
||||
},
|
||||
emits: ['change'],
|
||||
data() {
|
||||
return {
|
||||
files: [],
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
addFiles(files, shouldNotReset) {
|
||||
if (!shouldNotReset || this.shouldAlwaysReset) {
|
||||
this.files = files
|
||||
}
|
||||
)
|
||||
|
||||
const validationOptions = { maxSize: this.maxSize, alertOnInvalid: true }
|
||||
this.files = [...this.files].filter((file) => fileIsValid(file, validationOptions))
|
||||
const emit = defineEmits<{ change: [files: File[]] }>()
|
||||
|
||||
if (this.files.length > 0) {
|
||||
this.$emit('change', this.files)
|
||||
}
|
||||
},
|
||||
handleDrop(e) {
|
||||
this.addFiles(e.dataTransfer.files)
|
||||
},
|
||||
handleChange(e) {
|
||||
this.addFiles(e.target.files)
|
||||
},
|
||||
},
|
||||
const formatBytes = useFormatBytes()
|
||||
|
||||
const files = ref<File[]>([])
|
||||
|
||||
function addFiles(incoming: FileList, shouldNotReset = false) {
|
||||
if (!shouldNotReset || props.shouldAlwaysReset) {
|
||||
files.value = Array.from(incoming)
|
||||
}
|
||||
const validationOptions = { maxSize: props.maxSize, alertOnInvalid: true }
|
||||
files.value = files.value.filter((file) => fileIsValid(file, validationOptions, formatBytes))
|
||||
if (files.value.length > 0) {
|
||||
emit('change', files.value)
|
||||
}
|
||||
}
|
||||
|
||||
function handleDrop(e: DragEvent) {
|
||||
addFiles(e.dataTransfer!.files)
|
||||
}
|
||||
|
||||
function handleChange(e: Event) {
|
||||
const input = e.target as HTMLInputElement
|
||||
if (!input.files) return
|
||||
addFiles(input.files)
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -1,12 +1,19 @@
|
||||
<template>
|
||||
<div class="shadow-card rounded-2xl border border-surface-5 bg-surface-3 p-4">
|
||||
<div class="shadow-card rounded-2xl border border-solid border-surface-5 bg-surface-3 p-4">
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center gap-4">
|
||||
<Avatar
|
||||
:src="queueEntry.project.icon_url"
|
||||
size="4rem"
|
||||
class="rounded-2xl border border-surface-5 bg-surface-4 !shadow-none"
|
||||
/>
|
||||
<NuxtLink
|
||||
:to="`/project/${queueEntry.project.slug}`"
|
||||
target="_blank"
|
||||
tabindex="-1"
|
||||
class="flex"
|
||||
>
|
||||
<Avatar
|
||||
:src="queueEntry.project.icon_url"
|
||||
size="4rem"
|
||||
class="rounded-2xl border border-surface-5 bg-surface-4 !shadow-none"
|
||||
/>
|
||||
</NuxtLink>
|
||||
<div class="flex flex-col gap-1.5">
|
||||
<div class="flex items-center gap-2">
|
||||
<NuxtLink
|
||||
@@ -22,11 +29,15 @@
|
||||
<component
|
||||
:is="getProjectTypeIcon(queueEntry.project.project_types[0] as any)"
|
||||
aria-hidden="true"
|
||||
class="h-4 w-4"
|
||||
class="size-4"
|
||||
/>
|
||||
<span class="text-sm font-medium text-secondary">
|
||||
{{
|
||||
queueEntry.project.project_types.map((t) => formatProjectType(t, true)).join(', ')
|
||||
queueEntry.project.project_types.length === 0
|
||||
? '???'
|
||||
: queueEntry.project.project_types
|
||||
.map((t) => formatProjectType(t, true))
|
||||
.join(', ')
|
||||
}}
|
||||
</span>
|
||||
</div>
|
||||
@@ -35,37 +46,39 @@
|
||||
class="flex items-center gap-2 rounded-full border border-solid border-surface-5 bg-surface-4 px-2.5 py-1"
|
||||
>
|
||||
<span class="text-sm text-secondary">Requesting</span>
|
||||
<Badge :type="queueEntry.project.requested_status" class="status" />
|
||||
<Badge :type="queueEntry.project.requested_status" class="text-sm" />
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="queueEntry.owner" class="flex items-center gap-1">
|
||||
<Avatar
|
||||
:src="queueEntry.owner.user.avatar_url"
|
||||
size="1.5rem"
|
||||
circle
|
||||
class="border border-surface-5 bg-surface-4 !shadow-none"
|
||||
/>
|
||||
<div v-if="queueEntry.ownership?.kind === 'user'">
|
||||
<NuxtLink
|
||||
:to="`/user/${queueEntry.owner.user.username}`"
|
||||
:to="`/user/${queueEntry.ownership.id}`"
|
||||
target="_blank"
|
||||
class="text-sm font-medium text-secondary hover:underline"
|
||||
class="flex w-fit min-w-40 items-center gap-1 text-sm font-medium text-secondary hover:underline"
|
||||
>
|
||||
{{ queueEntry.owner.user.username }}
|
||||
<Avatar
|
||||
:src="queueEntry.ownership.icon_url"
|
||||
size="1.5rem"
|
||||
circle
|
||||
class="border border-surface-5 bg-surface-4 !shadow-none"
|
||||
/>
|
||||
{{ queueEntry.ownership.name }}
|
||||
</NuxtLink>
|
||||
</div>
|
||||
<div v-else-if="queueEntry.org" class="flex items-center gap-1">
|
||||
<div
|
||||
v-else-if="queueEntry.ownership?.kind === 'organization'"
|
||||
class="flex items-center gap-1"
|
||||
>
|
||||
<Avatar
|
||||
:src="queueEntry.org.icon_url"
|
||||
:src="queueEntry.ownership.icon_url"
|
||||
size="1.5rem"
|
||||
circle
|
||||
class="border border-surface-5 bg-surface-4 !shadow-none"
|
||||
/>
|
||||
<NuxtLink
|
||||
:to="`/organization/${queueEntry.org.slug}`"
|
||||
:to="`/organization/${queueEntry.ownership.id}`"
|
||||
target="_blank"
|
||||
class="text-sm font-medium text-secondary hover:underline"
|
||||
>
|
||||
{{ queueEntry.org.name }}
|
||||
{{ queueEntry.ownership.name }}
|
||||
</NuxtLink>
|
||||
</div>
|
||||
</div>
|
||||
@@ -84,25 +97,20 @@
|
||||
</span>
|
||||
|
||||
<div class="flex items-center gap-2">
|
||||
<ButtonStyled circular color="orange">
|
||||
<button @click="openProjectForReview">
|
||||
<ScaleIcon class="size-5" />
|
||||
<ButtonStyled circular>
|
||||
<button v-tooltip="'Copy ID'" @click="copyId">
|
||||
<ClipboardCopyIcon />
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled circular>
|
||||
<OverflowMenu :options="quickActions" :dropdown-id="`${baseId}-quick-actions`">
|
||||
<template #default>
|
||||
<EllipsisVerticalIcon class="size-4" />
|
||||
</template>
|
||||
<template #copy-id>
|
||||
<ClipboardCopyIcon />
|
||||
<span class="hidden sm:inline">Copy ID</span>
|
||||
</template>
|
||||
<template #copy-link>
|
||||
<LinkIcon />
|
||||
<span class="hidden sm:inline">Copy link</span>
|
||||
</template>
|
||||
</OverflowMenu>
|
||||
<button v-tooltip="'Copy link'" @click="copyLink">
|
||||
<LinkIcon />
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled v-tooltip="'Begin review'" circular color="orange">
|
||||
<button @click="openProjectForReview">
|
||||
<ScaleIcon />
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
</div>
|
||||
</div>
|
||||
@@ -111,15 +119,13 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ClipboardCopyIcon, EllipsisVerticalIcon, LinkIcon, ScaleIcon } from '@modrinth/assets'
|
||||
import { ClipboardCopyIcon, LinkIcon, ScaleIcon } from '@modrinth/assets'
|
||||
import {
|
||||
Avatar,
|
||||
Badge,
|
||||
ButtonStyled,
|
||||
getProjectTypeIcon,
|
||||
injectNotificationManager,
|
||||
OverflowMenu,
|
||||
type OverflowMenuOption,
|
||||
useFormatDateTime,
|
||||
useRelativeTime,
|
||||
} from '@modrinth/ui'
|
||||
@@ -143,8 +149,6 @@ const formatDateTimeFull = useFormatDateTime({
|
||||
timeZone: 'UTC',
|
||||
})
|
||||
|
||||
const baseId = useId()
|
||||
|
||||
const props = defineProps<{
|
||||
queueEntry: ModerationProject
|
||||
}>()
|
||||
@@ -185,34 +189,27 @@ const formattedDate = computed(() => {
|
||||
}
|
||||
})
|
||||
|
||||
const quickActions: OverflowMenuOption[] = [
|
||||
{
|
||||
id: 'copy-link',
|
||||
action: () => {
|
||||
const base = window.location.origin
|
||||
const projectUrl = `${base}/project/${props.queueEntry.project.slug}`
|
||||
navigator.clipboard.writeText(projectUrl).then(() => {
|
||||
addNotification({
|
||||
type: 'success',
|
||||
title: 'Project link copied',
|
||||
text: 'The link to this project has been copied to your clipboard.',
|
||||
})
|
||||
})
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'copy-id',
|
||||
action: () => {
|
||||
navigator.clipboard.writeText(props.queueEntry.project.id).then(() => {
|
||||
addNotification({
|
||||
type: 'success',
|
||||
title: 'Project ID copied',
|
||||
text: 'The ID of this project has been copied to your clipboard.',
|
||||
})
|
||||
})
|
||||
},
|
||||
},
|
||||
]
|
||||
function copyLink() {
|
||||
const base = window.location.origin
|
||||
const projectUrl = `${base}/project/${props.queueEntry.project.slug}`
|
||||
navigator.clipboard.writeText(projectUrl).then(() => {
|
||||
addNotification({
|
||||
type: 'success',
|
||||
title: 'Project link copied',
|
||||
text: 'The link to this project has been copied to your clipboard.',
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
function copyId() {
|
||||
navigator.clipboard.writeText(props.queueEntry.project.id).then(() => {
|
||||
addNotification({
|
||||
type: 'success',
|
||||
title: 'Project ID copied',
|
||||
text: 'The ID of this project has been copied to your clipboard.',
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
function openProjectForReview() {
|
||||
emit('startFromProject', props.queueEntry.project.id)
|
||||
|
||||
@@ -28,6 +28,7 @@ import {
|
||||
injectNotificationManager,
|
||||
OverflowMenu,
|
||||
type OverflowMenuOption,
|
||||
useFormatBytes,
|
||||
useFormatDateTime,
|
||||
} from '@modrinth/ui'
|
||||
import { NavTabs } from '@modrinth/ui'
|
||||
@@ -56,6 +57,7 @@ const formatDateTimeUtc = useFormatDateTime({
|
||||
timeZoneName: 'short',
|
||||
timeZone: 'UTC',
|
||||
})
|
||||
const formatBytes = useFormatBytes()
|
||||
|
||||
type FlattenedFileReport = Labrinth.TechReview.Internal.FileReport & {
|
||||
id: string
|
||||
@@ -362,12 +364,6 @@ const formattedDate = computed(() => {
|
||||
return `${diffDays} days ago`
|
||||
})
|
||||
|
||||
function formatFileSize(bytes: number): string {
|
||||
if (bytes < 1024) return `${bytes} B`
|
||||
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(2)} KiB`
|
||||
return `${(bytes / (1024 * 1024)).toFixed(2)} MiB`
|
||||
}
|
||||
|
||||
function viewFileFlags(file: FlattenedFileReport) {
|
||||
selectedFileId.value = file.id
|
||||
currentTab.value = 'File'
|
||||
@@ -851,7 +847,7 @@ const reviewSummaryPreview = computed(() => {
|
||||
const fileVerdict = fileUnsafe > 0 ? 'Unsafe' : 'Safe'
|
||||
|
||||
markdown += `### ${fileData.fileName}\n`
|
||||
markdown += `> ${formatFileSize(fileData.fileSize)} • ${fileData.decisions.length} issues • Max severity: ${fileData.maxSeverity} • **Verdict:** ${fileVerdict}\n\n`
|
||||
markdown += `> ${formatBytes(fileData.fileSize)} • ${fileData.decisions.length} issues • Max severity: ${fileData.maxSeverity} • **Verdict:** ${fileVerdict}\n\n`
|
||||
markdown += `<details>\n<summary>Issues (${fileSafe} safe, ${fileUnsafe} unsafe)</summary>\n\n`
|
||||
markdown += `| Class | Issue Type | Severity | Decision |\n`
|
||||
markdown += `|-------|------------|----------|----------|\n`
|
||||
@@ -1150,7 +1146,7 @@ async function handleSubmitReview(verdict: 'safe' | 'unsafe') {
|
||||
</span>
|
||||
<div class="rounded-full border border-solid border-surface-5 bg-surface-3 px-2.5 py-1">
|
||||
<span class="text-sm font-medium text-secondary">{{
|
||||
formatFileSize(file.file_size)
|
||||
formatBytes(file.file_size)
|
||||
}}</span>
|
||||
</div>
|
||||
<div
|
||||
|
||||
@@ -24,8 +24,16 @@
|
||||
</Checkbox>
|
||||
<div class="input-group push-right">
|
||||
<ButtonStyled color="orange">
|
||||
<button :disabled="!submissionConfirmation" @click="resubmit()">
|
||||
<ScaleIcon aria-hidden="true" />
|
||||
<button
|
||||
:disabled="!submissionConfirmation || isLoading"
|
||||
@click="runBlockingAction('resubmit-modal', resubmit)"
|
||||
>
|
||||
<SpinnerIcon
|
||||
v-if="loadingAction === 'resubmit-modal'"
|
||||
class="animate-spin"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<ScaleIcon v-else aria-hidden="true" />
|
||||
Resubmit for review
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
@@ -54,8 +62,16 @@
|
||||
</Checkbox>
|
||||
<div class="input-group push-right">
|
||||
<ButtonStyled color="brand">
|
||||
<button :disabled="!replyConfirmation" @click="sendReplyFromModal()">
|
||||
<ReplyIcon aria-hidden="true" />
|
||||
<button
|
||||
:disabled="!replyConfirmation || isLoading"
|
||||
@click="runBlockingAction('reply-modal', () => sendReplyFromModal())"
|
||||
>
|
||||
<SpinnerIcon
|
||||
v-if="loadingAction === 'reply-modal'"
|
||||
class="animate-spin"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<ReplyIcon v-else aria-hidden="true" />
|
||||
Reply to thread
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
@@ -82,8 +98,9 @@
|
||||
<template v-if="report && report.closed">
|
||||
<p>This thread is closed and new messages cannot be sent to it.</p>
|
||||
<ButtonStyled v-if="isStaff(auth.user)">
|
||||
<button @click="reopenReport()">
|
||||
<CheckCircleIcon aria-hidden="true" />
|
||||
<button :disabled="isLoading" @click="runBlockingAction('reopen', () => reopenReport())">
|
||||
<SpinnerIcon v-if="loadingAction === 'reopen'" class="animate-spin" aria-hidden="true" />
|
||||
<CheckCircleIcon v-else aria-hidden="true" />
|
||||
Reopen thread
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
@@ -100,35 +117,53 @@
|
||||
<ButtonStyled color="brand">
|
||||
<button
|
||||
v-if="sortedMessages.length > 0"
|
||||
:disabled="!replyBody"
|
||||
@click="isApproved(project) && !isStaff(auth.user) ? openReplyModal() : sendReply()"
|
||||
:disabled="!replyBody || isLoading"
|
||||
@click="
|
||||
isApproved(project) && !isStaff(auth.user)
|
||||
? openReplyModal()
|
||||
: runBlockingAction('reply', () => sendReply())
|
||||
"
|
||||
>
|
||||
<ReplyIcon aria-hidden="true" />
|
||||
<SpinnerIcon v-if="loadingAction === 'reply'" class="animate-spin" aria-hidden="true" />
|
||||
<ReplyIcon v-else aria-hidden="true" />
|
||||
Reply
|
||||
</button>
|
||||
<button
|
||||
v-else
|
||||
:disabled="!replyBody"
|
||||
@click="isApproved(project) && !isStaff(auth.user) ? openReplyModal() : sendReply()"
|
||||
:disabled="!replyBody || isLoading"
|
||||
@click="
|
||||
isApproved(project) && !isStaff(auth.user)
|
||||
? openReplyModal()
|
||||
: runBlockingAction('send', () => sendReply())
|
||||
"
|
||||
>
|
||||
<SendIcon aria-hidden="true" />
|
||||
<SpinnerIcon v-if="loadingAction === 'send'" class="animate-spin" aria-hidden="true" />
|
||||
<SendIcon v-else aria-hidden="true" />
|
||||
Send
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled v-if="isStaff(auth.user)">
|
||||
<button :disabled="!replyBody" @click="sendReply(null, true)">
|
||||
<ScaleIcon aria-hidden="true" />
|
||||
<button
|
||||
:disabled="!replyBody || isLoading"
|
||||
@click="runBlockingAction('private-note', () => sendReply(null, true))"
|
||||
>
|
||||
<SpinnerIcon
|
||||
v-if="loadingAction === 'private-note'"
|
||||
class="animate-spin"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<ScaleIcon v-else aria-hidden="true" />
|
||||
Add private note
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<template v-if="currentMember && !isStaff(auth.user)">
|
||||
<template v-if="isRejected(project)">
|
||||
<ButtonStyled color="orange">
|
||||
<button v-if="replyBody" @click="openResubmitModal(true)">
|
||||
<button v-if="replyBody" :disabled="isLoading" @click="openResubmitModal(true)">
|
||||
<ScaleIcon aria-hidden="true" />
|
||||
Resubmit for review with reply
|
||||
</button>
|
||||
<button v-else @click="openResubmitModal(false)">
|
||||
<button v-else :disabled="isLoading" @click="openResubmitModal(false)">
|
||||
<ScaleIcon aria-hidden="true" />
|
||||
Resubmit for review
|
||||
</button>
|
||||
@@ -140,12 +175,30 @@
|
||||
<template v-if="report">
|
||||
<template v-if="isStaff(auth.user)">
|
||||
<ButtonStyled color="red">
|
||||
<button v-if="replyBody" @click="closeReport(true)">
|
||||
<CheckCircleIcon aria-hidden="true" />
|
||||
<button
|
||||
v-if="replyBody"
|
||||
:disabled="isLoading"
|
||||
@click="runBlockingAction('close-with-reply', () => closeReport(true))"
|
||||
>
|
||||
<SpinnerIcon
|
||||
v-if="loadingAction === 'close-with-reply'"
|
||||
class="animate-spin"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<CheckCircleIcon v-else aria-hidden="true" />
|
||||
Close with reply
|
||||
</button>
|
||||
<button v-else @click="closeReport()">
|
||||
<CheckCircleIcon aria-hidden="true" />
|
||||
<button
|
||||
v-else
|
||||
:disabled="isLoading"
|
||||
@click="runBlockingAction('close', () => closeReport())"
|
||||
>
|
||||
<SpinnerIcon
|
||||
v-if="loadingAction === 'close'"
|
||||
class="animate-spin"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<CheckCircleIcon v-else aria-hidden="true" />
|
||||
Close thread
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
@@ -154,92 +207,122 @@
|
||||
<template v-if="project">
|
||||
<template v-if="isStaff(auth.user)">
|
||||
<ButtonStyled v-if="replyBody" color="green">
|
||||
<button :disabled="isApproved(project)" @click="sendReply(requestedStatus)">
|
||||
<CheckIcon aria-hidden="true" />
|
||||
<button
|
||||
:disabled="isApproved(project) || isLoading"
|
||||
@click="runBlockingAction('approve-with-reply', () => sendReply(requestedStatus))"
|
||||
>
|
||||
<SpinnerIcon
|
||||
v-if="loadingAction === 'approve-with-reply'"
|
||||
class="animate-spin"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<CheckIcon v-else aria-hidden="true" />
|
||||
Approve with reply
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled v-else color="green">
|
||||
<button :disabled="isApproved(project)" @click="setStatus(requestedStatus)">
|
||||
<CheckIcon aria-hidden="true" />
|
||||
<button
|
||||
:disabled="isApproved(project) || isLoading"
|
||||
@click="runBlockingAction('approve', () => setStatus(requestedStatus))"
|
||||
>
|
||||
<SpinnerIcon
|
||||
v-if="loadingAction === 'approve'"
|
||||
class="animate-spin"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<CheckIcon v-else aria-hidden="true" />
|
||||
Approve
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<div class="joined-buttons">
|
||||
<ButtonStyled v-if="replyBody" color="red">
|
||||
<button :disabled="project.status === 'rejected'" @click="sendReply('rejected')">
|
||||
<XIcon aria-hidden="true" />
|
||||
<button
|
||||
:disabled="project.status === 'rejected' || isLoading"
|
||||
@click="runBlockingAction('reject-with-reply', () => sendReply('rejected'))"
|
||||
>
|
||||
<SpinnerIcon
|
||||
v-if="loadingAction === 'reject-with-reply'"
|
||||
class="animate-spin"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<XIcon v-else aria-hidden="true" />
|
||||
Reject with reply
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled v-else color="red">
|
||||
<button :disabled="project.status === 'rejected'" @click="setStatus('rejected')">
|
||||
<XIcon aria-hidden="true" />
|
||||
<button
|
||||
:disabled="project.status === 'rejected' || isLoading"
|
||||
@click="runBlockingAction('reject', () => setStatus('rejected'))"
|
||||
>
|
||||
<SpinnerIcon
|
||||
v-if="loadingAction === 'reject'"
|
||||
class="animate-spin"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<XIcon v-else aria-hidden="true" />
|
||||
Reject
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled color="red">
|
||||
<OverflowMenu
|
||||
class="btn-dropdown-animation"
|
||||
:disabled="isLoading"
|
||||
:options="
|
||||
replyBody
|
||||
? [
|
||||
{
|
||||
id: 'withhold-reply',
|
||||
color: 'danger',
|
||||
action: () => {
|
||||
sendReply('withheld')
|
||||
},
|
||||
action: () =>
|
||||
runBlockingAction('withhold-reply', () => sendReply('withheld')),
|
||||
hoverFilled: true,
|
||||
disabled: project.status === 'withheld',
|
||||
disabled: project.status === 'withheld' || isLoading,
|
||||
},
|
||||
{
|
||||
id: 'set-to-draft-reply',
|
||||
action: () => {
|
||||
sendReply('draft')
|
||||
},
|
||||
action: () =>
|
||||
runBlockingAction('set-to-draft-reply', () => sendReply('draft')),
|
||||
hoverFilled: true,
|
||||
disabled: project.status === 'draft',
|
||||
disabled: project.status === 'draft' || isLoading,
|
||||
},
|
||||
{
|
||||
id: 'send-to-review-reply',
|
||||
action: () => {
|
||||
sendReply('processing', true)
|
||||
},
|
||||
action: () =>
|
||||
runBlockingAction('send-to-review-reply', () =>
|
||||
sendReply('processing', true),
|
||||
),
|
||||
hoverFilled: true,
|
||||
disabled: project.status === 'processing',
|
||||
disabled: project.status === 'processing' || isLoading,
|
||||
},
|
||||
]
|
||||
: [
|
||||
{
|
||||
id: 'withhold',
|
||||
color: 'danger',
|
||||
action: () => {
|
||||
setStatus('withheld')
|
||||
},
|
||||
action: () =>
|
||||
runBlockingAction('withhold', () => setStatus('withheld')),
|
||||
hoverFilled: true,
|
||||
disabled: project.status === 'withheld',
|
||||
disabled: project.status === 'withheld' || isLoading,
|
||||
},
|
||||
{
|
||||
id: 'set-to-draft',
|
||||
action: () => {
|
||||
setStatus('draft')
|
||||
},
|
||||
action: () =>
|
||||
runBlockingAction('set-to-draft', () => setStatus('draft')),
|
||||
hoverFilled: true,
|
||||
disabled: project.status === 'draft',
|
||||
disabled: project.status === 'draft' || isLoading,
|
||||
},
|
||||
{
|
||||
id: 'send-to-review',
|
||||
action: () => {
|
||||
setStatus('processing')
|
||||
},
|
||||
action: () =>
|
||||
runBlockingAction('send-to-review', () => setStatus('processing')),
|
||||
hoverFilled: true,
|
||||
disabled: project.status === 'processing',
|
||||
disabled: project.status === 'processing' || isLoading,
|
||||
},
|
||||
]
|
||||
"
|
||||
>
|
||||
<DropdownIcon aria-hidden="true" />
|
||||
<SpinnerIcon v-if="isDropdownLoading" class="animate-spin" aria-hidden="true" />
|
||||
<DropdownIcon v-else aria-hidden="true" />
|
||||
<template #withhold-reply>
|
||||
<EyeOffIcon aria-hidden="true" />
|
||||
Withhold with reply
|
||||
@@ -285,6 +368,7 @@ import {
|
||||
ReplyIcon,
|
||||
ScaleIcon,
|
||||
SendIcon,
|
||||
SpinnerIcon,
|
||||
XIcon,
|
||||
} from '@modrinth/assets'
|
||||
import {
|
||||
@@ -363,6 +447,30 @@ const sortedMessages = computed(() => {
|
||||
const modalSubmit = ref(null)
|
||||
const modalReply = ref(null)
|
||||
|
||||
const loadingAction = ref(null)
|
||||
const isLoading = computed(() => loadingAction.value !== null)
|
||||
const dropdownActionIds = [
|
||||
'withhold',
|
||||
'withhold-reply',
|
||||
'set-to-draft',
|
||||
'set-to-draft-reply',
|
||||
'send-to-review',
|
||||
'send-to-review-reply',
|
||||
]
|
||||
const isDropdownLoading = computed(() => dropdownActionIds.includes(loadingAction.value))
|
||||
|
||||
async function runBlockingAction(actionId, action) {
|
||||
if (loadingAction.value !== null) {
|
||||
return
|
||||
}
|
||||
loadingAction.value = actionId
|
||||
try {
|
||||
await action()
|
||||
} finally {
|
||||
loadingAction.value = null
|
||||
}
|
||||
}
|
||||
|
||||
async function updateThreadLocal() {
|
||||
let threadId = null
|
||||
if (props.project) {
|
||||
@@ -390,8 +498,8 @@ async function onUploadImage(file) {
|
||||
}
|
||||
|
||||
async function sendReplyFromModal(status = null, privateMessage = false) {
|
||||
modalReply.value.hide()
|
||||
await sendReply(status, privateMessage)
|
||||
modalReply.value.hide()
|
||||
}
|
||||
|
||||
async function sendReply(status = null, privateMessage = false) {
|
||||
|
||||
@@ -47,6 +47,8 @@ export const DEFAULT_FEATURE_FLAGS = validateValues({
|
||||
showDiscoverProjectButtons: false,
|
||||
useV1ContentTabAPI: true,
|
||||
labrinthApiCanary: false,
|
||||
dismissedExternalProjectsInfo: false,
|
||||
modpackPermissionsPage: false,
|
||||
} as const)
|
||||
|
||||
export type FeatureFlag = keyof typeof DEFAULT_FEATURE_FLAGS
|
||||
|
||||
@@ -0,0 +1,140 @@
|
||||
import type { FilterValue } from '@modrinth/ui'
|
||||
import { LOADER_FILTER_TYPES } from '@modrinth/ui'
|
||||
|
||||
const TEN_MINUTES = 600
|
||||
|
||||
export type DownloadContext = {
|
||||
gameVersion?: string
|
||||
loader?: string
|
||||
reason?: 'standalone' | 'dependency' | 'modpack' | 'update'
|
||||
}
|
||||
|
||||
export type FilterSelection = {
|
||||
gameVersion?: string
|
||||
loader?: string
|
||||
}
|
||||
|
||||
const cookieDefaults = {
|
||||
maxAge: TEN_MINUTES,
|
||||
sameSite: 'lax' as const,
|
||||
secure: true,
|
||||
path: '/',
|
||||
httpOnly: false,
|
||||
}
|
||||
|
||||
function readCookieValue(value: string | null | undefined): string | undefined {
|
||||
if (typeof value !== 'string' || !value) {
|
||||
return undefined
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
function newFilterSelection(
|
||||
gameVersion: string | undefined,
|
||||
loader: string | undefined,
|
||||
): FilterSelection | null {
|
||||
if (!gameVersion && !loader) {
|
||||
return null
|
||||
} else if (!gameVersion) {
|
||||
return {
|
||||
loader,
|
||||
}
|
||||
} else if (!loader) {
|
||||
return {
|
||||
gameVersion,
|
||||
}
|
||||
} else {
|
||||
return {
|
||||
gameVersion,
|
||||
loader,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function useCdnDownloadContext() {
|
||||
const filterGameVersionCookie = useCookie<string | null>('mr_download_filter_game_version', {
|
||||
...cookieDefaults,
|
||||
default: () => null,
|
||||
})
|
||||
|
||||
const filterLoaderCookie = useCookie<string | null>('mr_download_filter_loader', {
|
||||
...cookieDefaults,
|
||||
default: () => null,
|
||||
})
|
||||
|
||||
function createProjectDownloadUrl(originalUrl: string, context?: DownloadContext): string {
|
||||
if (!originalUrl.startsWith('https://cdn.modrinth.com')) {
|
||||
return originalUrl
|
||||
}
|
||||
|
||||
const reason = context?.reason
|
||||
const gameVersion = context?.gameVersion ?? readCookieValue(filterGameVersionCookie.value)
|
||||
const loader = context?.loader ?? readCookieValue(filterLoaderCookie.value)
|
||||
|
||||
try {
|
||||
const url = new URL(originalUrl)
|
||||
|
||||
if (reason) {
|
||||
url.searchParams.set('mr_download_reason', reason)
|
||||
}
|
||||
if (gameVersion) {
|
||||
url.searchParams.set('mr_game_version', gameVersion)
|
||||
} else {
|
||||
url.searchParams.delete('mr_game_version')
|
||||
}
|
||||
|
||||
if (loader) {
|
||||
url.searchParams.set('mr_loader', loader)
|
||||
} else {
|
||||
url.searchParams.delete('mr_loader')
|
||||
}
|
||||
return url.toString()
|
||||
} catch {
|
||||
return originalUrl
|
||||
}
|
||||
}
|
||||
|
||||
function persistFilterSelection(selection: FilterSelection | null) {
|
||||
if (!selection) {
|
||||
filterGameVersionCookie.value = null
|
||||
filterLoaderCookie.value = null
|
||||
return
|
||||
}
|
||||
filterGameVersionCookie.value = selection.gameVersion ?? null
|
||||
filterLoaderCookie.value = selection.loader ?? null
|
||||
}
|
||||
|
||||
function updateDiscoverFilterContext(filters: FilterValue[]) {
|
||||
if (!import.meta.client) {
|
||||
return
|
||||
}
|
||||
const versionFilters = [
|
||||
...new Set(filters.filter((f) => f.type === 'game_version').map((f) => f.option)),
|
||||
]
|
||||
const loaderFilters = [
|
||||
...new Set(
|
||||
filters
|
||||
.filter((f) => (LOADER_FILTER_TYPES as readonly string[]).includes(f.type))
|
||||
.map((f) => f.option),
|
||||
),
|
||||
]
|
||||
const gameVersion = versionFilters.length === 1 ? versionFilters[0] : undefined
|
||||
const loader = loaderFilters.length === 1 ? loaderFilters[0] : undefined
|
||||
persistFilterSelection(newFilterSelection(gameVersion, loader))
|
||||
}
|
||||
|
||||
function updateVersionsFilterContext(gameVersions: string[], loaders: string[]) {
|
||||
if (!import.meta.client) {
|
||||
return
|
||||
}
|
||||
const gameVersion = gameVersions.length === 1 ? gameVersions[0] : undefined
|
||||
const loader = loaders.length === 1 ? loaders[0] : undefined
|
||||
persistFilterSelection(newFilterSelection(gameVersion, loader))
|
||||
}
|
||||
|
||||
return {
|
||||
createProjectDownloadUrl,
|
||||
updateDiscoverFilterContext,
|
||||
updateVersionsFilterContext,
|
||||
}
|
||||
}
|
||||
+72
-11
@@ -7,7 +7,18 @@
|
||||
<Logo404 />
|
||||
</div>
|
||||
<div class="error-box" :class="{ 'has-bot': !is404 }">
|
||||
<img v-if="!is404" :src="SadRinthbot" alt="Sad Modrinth bot" class="error-box__sad-bot" />
|
||||
<img
|
||||
v-if="is401"
|
||||
:src="AnnoyedRinthbot"
|
||||
alt="Annoyed Modrinth bot"
|
||||
class="error-box__sad-bot"
|
||||
/>
|
||||
<img
|
||||
v-else-if="!is404"
|
||||
:src="SadRinthbot"
|
||||
alt="Sad Modrinth bot"
|
||||
class="error-box__sad-bot"
|
||||
/>
|
||||
<div v-if="!is404" class="error-box__top-glow" />
|
||||
<div class="error-box__body">
|
||||
<h1 class="error-box__title">{{ formatMessage(errorMessages.title) }}</h1>
|
||||
@@ -15,6 +26,33 @@
|
||||
{{ formatMessage(errorMessages.subtitle) }}
|
||||
</p>
|
||||
</div>
|
||||
<div v-if="is401" class="flex flex-col gap-4">
|
||||
<template v-if="auth.user">
|
||||
<p class="m-0">
|
||||
{{ formatMessage(unauthorizedMessages.signedInAsLabel) }}
|
||||
</p>
|
||||
<div
|
||||
class="flex items-center gap-2 rounded-2xl border border-solid border-surface-5 bg-surface-4 p-4"
|
||||
>
|
||||
<Avatar :src="auth.user.avatar_url" size="32px" />
|
||||
<span class="font-medium text-contrast">{{ auth.user.username }}</span>
|
||||
|
||||
<ButtonStyled color="red" type="transparent">
|
||||
<button type="button" class="ml-auto" @click="logout">
|
||||
{{ formatMessage(commonMessages.signOutButton) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<ButtonStyled color="brand">
|
||||
<nuxt-link class="button-like w-fit" :to="signInRoute">
|
||||
<LogInIcon />
|
||||
{{ formatMessage(commonMessages.signInButton) }}
|
||||
</nuxt-link>
|
||||
</ButtonStyled>
|
||||
</template>
|
||||
</div>
|
||||
<div class="error-box__body">
|
||||
<p v-if="errorMessages.list_title" class="error-box__list-title">
|
||||
{{ formatMessage(errorMessages.list_title) }}
|
||||
@@ -51,9 +89,13 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { SadRinthbot } from '@modrinth/assets'
|
||||
import { AnnoyedRinthbot, LogInIcon, SadRinthbot } from '@modrinth/assets'
|
||||
import {
|
||||
Avatar,
|
||||
ButtonStyled,
|
||||
commonMessages,
|
||||
defineMessage,
|
||||
defineMessages,
|
||||
IntlFormatted,
|
||||
LoadingBar,
|
||||
normalizeChildren,
|
||||
@@ -65,6 +107,8 @@ import {
|
||||
} from '@modrinth/ui'
|
||||
|
||||
import Logo404 from '~/assets/images/404.svg'
|
||||
import { getSignInRouteObj } from '~/composables/auth.js'
|
||||
import { logout } from '~/composables/user.js'
|
||||
|
||||
import { createModrinthClient } from './helpers/api.ts'
|
||||
import { FrontendNotificationManager } from './providers/frontend-notifications.ts'
|
||||
@@ -103,6 +147,17 @@ const props = defineProps({
|
||||
})
|
||||
|
||||
const is404 = computed(() => props.error.statusCode === 404)
|
||||
const is401 = computed(() => props.error.statusCode === 401)
|
||||
|
||||
const unauthorizedMessages = defineMessages({
|
||||
signedInAsLabel: {
|
||||
id: 'error.generic.401.signed-in-as',
|
||||
defaultMessage: "You're currently signed in as:",
|
||||
},
|
||||
})
|
||||
|
||||
const signInRoute = computed(() => getSignInRouteObj(route))
|
||||
|
||||
const errorMessages = computed(
|
||||
() =>
|
||||
routeMessages.find((x) => x.match(route))?.messages[props.error.statusCode] ??
|
||||
@@ -112,10 +167,6 @@ const errorMessages = computed(
|
||||
|
||||
const route = useRoute()
|
||||
|
||||
watch(route, () => {
|
||||
console.log(route)
|
||||
})
|
||||
|
||||
const messages = {
|
||||
404: {
|
||||
title: defineMessage({
|
||||
@@ -138,6 +189,12 @@ const messages = {
|
||||
'This page has been blocked for legal reasons, such as government censorship or ongoing legal proceedings.',
|
||||
}),
|
||||
},
|
||||
401: {
|
||||
title: defineMessage({
|
||||
id: 'error.generic.401.title',
|
||||
defaultMessage: `You don't have access to this page`,
|
||||
}),
|
||||
},
|
||||
default: {
|
||||
title: defineMessage({
|
||||
id: 'error.generic.default.title',
|
||||
@@ -345,7 +402,7 @@ const routeMessages = [
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
a {
|
||||
a:not(.button-like) {
|
||||
color: var(--color-brand);
|
||||
font-weight: 600;
|
||||
|
||||
@@ -387,20 +444,24 @@ const routeMessages = [
|
||||
}
|
||||
|
||||
&__title {
|
||||
font-size: 2rem;
|
||||
font-weight: 900;
|
||||
font-size: 1.5rem;
|
||||
font-weight: 600;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
&__subtitle {
|
||||
font-size: 1.25rem;
|
||||
font-weight: 600;
|
||||
font-size: 1rem;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
&__body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.75rem;
|
||||
|
||||
&:empty {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
&__list-title {
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
import { formatBytes } from '@modrinth/utils'
|
||||
|
||||
export const fileIsValid = (file, validationOptions) => {
|
||||
const { maxSize, alertOnInvalid } = validationOptions
|
||||
if (maxSize !== null && maxSize !== undefined && file.size > maxSize) {
|
||||
if (alertOnInvalid) {
|
||||
alert(`File ${file.name} is too big! Must be less than ${formatBytes(maxSize)}`)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
export const acceptFileFromProjectType = (projectType) => {
|
||||
switch (projectType) {
|
||||
case 'mod':
|
||||
return '.jar,.zip,.litemod,application/java-archive,application/x-java-archive,application/zip'
|
||||
case 'plugin':
|
||||
return '.jar,.zip,application/java-archive,application/x-java-archive,application/zip'
|
||||
case 'resourcepack':
|
||||
return '.zip,application/zip'
|
||||
case 'shader':
|
||||
return '.zip,application/zip'
|
||||
case 'datapack':
|
||||
return '.zip,application/zip'
|
||||
case 'modpack':
|
||||
return '.mrpack,application/x-modrinth-modpack+zip,application/zip'
|
||||
default:
|
||||
return '*'
|
||||
}
|
||||
}
|
||||
@@ -181,56 +181,35 @@ export async function enrichReportBatch(reports: Report[]): Promise<ExtendedRepo
|
||||
}
|
||||
|
||||
// Doesn't need to be in @modrinth/moderation because it is specific to the frontend.
|
||||
export interface ModerationOwnershipUser {
|
||||
kind: 'user'
|
||||
id: string
|
||||
name: string
|
||||
icon_url: string | null
|
||||
}
|
||||
|
||||
export interface ModerationOwnershipOrganization {
|
||||
kind: 'organization'
|
||||
id: string
|
||||
name: string
|
||||
icon_url: string | null
|
||||
}
|
||||
|
||||
export type ModerationOwnership = ModerationOwnershipUser | ModerationOwnershipOrganization
|
||||
|
||||
export interface ProjectWithOwnership {
|
||||
ownership: ModerationOwnership
|
||||
[key: string]: any
|
||||
}
|
||||
|
||||
export interface ModerationProject {
|
||||
project: any
|
||||
owner: TeamMember | null
|
||||
org: Organization | null
|
||||
ownership: ModerationOwnership | null
|
||||
}
|
||||
|
||||
export async function enrichProjectBatch(projects: any[]): Promise<ModerationProject[]> {
|
||||
const teamIds = [...new Set(projects.map((p) => p.team_id).filter(Boolean))]
|
||||
const orgIds = [...new Set(projects.map((p) => p.organization).filter(Boolean))]
|
||||
|
||||
const [teamsData, orgsData]: [TeamMember[][], Organization[]] = await Promise.all([
|
||||
teamIds.length > 0
|
||||
? fetchSegmented(teamIds, (ids) => `teams?ids=${asEncodedJsonArray(ids)}`)
|
||||
: Promise.resolve([]),
|
||||
orgIds.length > 0
|
||||
? fetchSegmented(orgIds, (ids) => `organizations?ids=${asEncodedJsonArray(ids)}`, {
|
||||
apiVersion: 3,
|
||||
})
|
||||
: Promise.resolve([]),
|
||||
])
|
||||
|
||||
const cache = useModerationCache()
|
||||
|
||||
teamsData.forEach((team) => {
|
||||
if (team.length > 0) cache.teams.value.set(team[0].team_id, team)
|
||||
})
|
||||
|
||||
orgsData.forEach((org: Organization) => {
|
||||
cache.orgs.value.set(org.id, org)
|
||||
})
|
||||
|
||||
return projects.map((project) => {
|
||||
let owner: TeamMember | null = null
|
||||
let org: Organization | null = null
|
||||
|
||||
if (project.team_id) {
|
||||
const teamMembers = cache.teams.value.get(project.team_id)
|
||||
if (teamMembers) {
|
||||
owner = teamMembers.find((member) => member.role === 'Owner') || null
|
||||
}
|
||||
}
|
||||
|
||||
if (project.organization) {
|
||||
org = cache.orgs.value.get(project.organization) || null
|
||||
}
|
||||
|
||||
return {
|
||||
project,
|
||||
owner,
|
||||
org,
|
||||
} as ModerationProject
|
||||
})
|
||||
export function toModerationProjects(projects: ProjectWithOwnership[]): ModerationProject[] {
|
||||
return projects.map(({ ownership, ...project }) => ({
|
||||
project,
|
||||
ownership: ownership ?? null,
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -311,7 +311,7 @@
|
||||
{
|
||||
id: 'review-projects',
|
||||
color: 'orange',
|
||||
link: '/moderation/',
|
||||
link: '/moderation',
|
||||
},
|
||||
{
|
||||
id: 'tech-review',
|
||||
@@ -323,6 +323,11 @@
|
||||
color: 'orange',
|
||||
link: '/moderation/reports',
|
||||
},
|
||||
{
|
||||
id: 'external-projects',
|
||||
color: 'orange',
|
||||
link: '/moderation/external-projects',
|
||||
},
|
||||
{
|
||||
divider: true,
|
||||
},
|
||||
@@ -377,6 +382,9 @@
|
||||
<template #review-reports>
|
||||
<ReportIcon aria-hidden="true" /> {{ formatMessage(messages.reports) }}
|
||||
</template>
|
||||
<template #external-projects>
|
||||
<GlobeIcon aria-hidden="true" /> {{ formatMessage(messages.externalProjects) }}
|
||||
</template>
|
||||
<template #user-lookup>
|
||||
<UserSearchIcon aria-hidden="true" /> {{ formatMessage(messages.lookupByEmail) }}
|
||||
</template>
|
||||
@@ -705,6 +713,7 @@ import {
|
||||
DropdownIcon,
|
||||
FileIcon,
|
||||
GlassesIcon,
|
||||
GlobeIcon,
|
||||
HamburgerIcon,
|
||||
HomeIcon,
|
||||
IssuesIcon,
|
||||
@@ -880,6 +889,10 @@ const messages = defineMessages({
|
||||
id: 'layout.action.reports',
|
||||
defaultMessage: 'Review reports',
|
||||
},
|
||||
externalProjects: {
|
||||
id: 'layout.action.external-projects',
|
||||
defaultMessage: 'External projects',
|
||||
},
|
||||
lookupByEmail: {
|
||||
id: 'layout.action.lookup-by-email',
|
||||
defaultMessage: 'Lookup by email',
|
||||
|
||||
@@ -848,6 +848,30 @@
|
||||
"dashboard.creator-withdraw-modal.withdraw-limit-used": {
|
||||
"message": "لقد استنفدت رصيدك <b>{withdrawLimit}</b> حد السحب. يجب عليك تعبئة نموذج ضريبي لسحب مبلغ أكبر."
|
||||
},
|
||||
"dashboard.projects.notification.bulk-edit-success": {
|
||||
"message": "قم بتحرير روابط المشاريع المحددة دفعة واحدة."
|
||||
},
|
||||
"dashboard.projects.project.icon-alt": {
|
||||
"message": "أيقونة {title}"
|
||||
},
|
||||
"dashboard.projects.project.moderator-message-aria": {
|
||||
"message": "يحتوي المشروع على رسالة من المشرفين. اطلع على المشروع للمزيد."
|
||||
},
|
||||
"dashboard.projects.project.review-environment-metadata": {
|
||||
"message": "يرجى مراجعة بيانات تعريف البيئة"
|
||||
},
|
||||
"dashboard.projects.sort.ascending": {
|
||||
"message": "صعوداً"
|
||||
},
|
||||
"dashboard.projects.sort.descending": {
|
||||
"message": "نزولا"
|
||||
},
|
||||
"dashboard.projects.sort.option.name": {
|
||||
"message": "الإسم"
|
||||
},
|
||||
"dashboard.projects.sort.option.status": {
|
||||
"message": "الحالة"
|
||||
},
|
||||
"dashboard.revenue.available-now": {
|
||||
"message": "متاح الآن"
|
||||
},
|
||||
@@ -1444,5 +1468,41 @@
|
||||
},
|
||||
"layout.footer.resources.help-center": {
|
||||
"message": "مركز المساعدة"
|
||||
},
|
||||
"layout.nav.modrinth-home-page": {
|
||||
"message": "الصفحة الرئيسة لمودرينث"
|
||||
},
|
||||
"layout.nav.my-servers": {
|
||||
"message": "خوادمي"
|
||||
},
|
||||
"layout.nav.saved-projects": {
|
||||
"message": "المشاريع المحفوظة"
|
||||
},
|
||||
"muralpay.field.account-number-type": {
|
||||
"message": "نوع رقم الحساب"
|
||||
},
|
||||
"muralpay.field.wallet-address": {
|
||||
"message": "عنوان المحفظة"
|
||||
},
|
||||
"muralpay.placeholder.cuit-cuil": {
|
||||
"message": "أدخل CUIT أو CUIL"
|
||||
},
|
||||
"profile.user-id": {
|
||||
"message": "معرّف المستخدم: {id}"
|
||||
},
|
||||
"project-member-header.success-decline": {
|
||||
"message": "لقد رفضت دعوة الفريق"
|
||||
},
|
||||
"project-type.resourcepack.plural": {
|
||||
"message": "حزم ال"
|
||||
},
|
||||
"project.environment.migration.message": {
|
||||
"message": "لقد قمنا بتحديث نظام البيئات في مودرينث، وأصبحت الخيارات الجديدة متاحة الآن. يرجى التأكد من صحة البيانات الوصفية."
|
||||
},
|
||||
"project.environment.migration.review-button": {
|
||||
"message": "مراجعة إعدادات البيئة"
|
||||
},
|
||||
"project.environment.migration.title": {
|
||||
"message": "يرجى مراجعة بيانات تعريف البيئة"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1949,12 +1949,18 @@
|
||||
"report.could-not-find": {
|
||||
"message": "Kunne ikke finde {item, select, project {projekt} version {version} user {bruger} other {indhold}}"
|
||||
},
|
||||
"report.for.violation.description": {
|
||||
"message": "Eksempler inkluderer ondsindet, spam, stødende, vildledende, misvisende og ulovligt indhold."
|
||||
},
|
||||
"report.not-for.dmca": {
|
||||
"message": "DMCA-fjernelser"
|
||||
},
|
||||
"report.not-for.dmca.description": {
|
||||
"message": "Se vores <policy-link>Politik Om Pphavsret</policy-link>."
|
||||
},
|
||||
"report.note.copyright.1": {
|
||||
"message": "Bemærk venligst, at du *ikke* indsender en anmodning om fjernelse i henhold til DMCA, men derimod en anmeldelse af genuploadet indhold."
|
||||
},
|
||||
"report.please-report": {
|
||||
"message": "Venligst anmeld:"
|
||||
},
|
||||
@@ -2042,6 +2048,9 @@
|
||||
"settings.display.project-list-layouts.shader": {
|
||||
"message": "Side til shaders"
|
||||
},
|
||||
"settings.display.sidebar.right-aligned-content-sidebar.description": {
|
||||
"message": "Flytter sidebjælken til venstre for sidens indhold."
|
||||
},
|
||||
"settings.pats.modal.create.name.label": {
|
||||
"message": "Navn"
|
||||
},
|
||||
|
||||
@@ -554,6 +554,9 @@
|
||||
"dashboard.affiliate-links.create.button": {
|
||||
"message": "Partnerlink erstellen"
|
||||
},
|
||||
"dashboard.affiliate-links.empty.no-codes": {
|
||||
"message": "Keine Partnercodes gefunden."
|
||||
},
|
||||
"dashboard.affiliate-links.error.title": {
|
||||
"message": "Fehler beim Laden von Partnerlinks"
|
||||
},
|
||||
@@ -572,6 +575,15 @@
|
||||
"dashboard.affiliate-links.search": {
|
||||
"message": "Partnerlinks durchsuchen..."
|
||||
},
|
||||
"dashboard.analytics.from-projects": {
|
||||
"message": "von {count} {count, plural, one {Projekt} other {Projekten}}"
|
||||
},
|
||||
"dashboard.analytics.total-downloads": {
|
||||
"message": "Anzahl der Downloads"
|
||||
},
|
||||
"dashboard.analytics.total-followers": {
|
||||
"message": "Anzahl der Follower"
|
||||
},
|
||||
"dashboard.collections.button.create-new": {
|
||||
"message": "Erstellen"
|
||||
},
|
||||
@@ -596,9 +608,18 @@
|
||||
"dashboard.collections.long-title": {
|
||||
"message": "Deine Kollektionen"
|
||||
},
|
||||
"dashboard.collections.placeholder.search": {
|
||||
"message": "Kollektionen durchsuchen..."
|
||||
},
|
||||
"dashboard.collections.sort.name-ascending": {
|
||||
"message": "Name (A-Z)"
|
||||
},
|
||||
"dashboard.collections.sort.recently-created": {
|
||||
"message": "Kürzlich erstellt"
|
||||
},
|
||||
"dashboard.collections.sort.recently-updated": {
|
||||
"message": "Kürzlich aktualisiert"
|
||||
},
|
||||
"dashboard.creator-tax-form-modal.confirmation.download-button": {
|
||||
"message": "Lade {formType} herunter"
|
||||
},
|
||||
@@ -857,21 +878,129 @@
|
||||
"dashboard.notifications.button.mark-all-as-read": {
|
||||
"message": "Alle als gelesen markieren"
|
||||
},
|
||||
"dashboard.notifications.button.view-history": {
|
||||
"message": "Verlauf ansehen"
|
||||
},
|
||||
"dashboard.notifications.empty.no-unread": {
|
||||
"message": "Du hast keine ungelesenen Benachrichtigungen."
|
||||
},
|
||||
"dashboard.notifications.error.loading": {
|
||||
"message": "Fehler beim Laden von Benachrichtigungen:"
|
||||
},
|
||||
"dashboard.notifications.history.label": {
|
||||
"message": "Verlauf"
|
||||
},
|
||||
"dashboard.notifications.history.title": {
|
||||
"message": "Benachrichtigungsverlauf"
|
||||
},
|
||||
"dashboard.notifications.link.see-all": {
|
||||
"message": "Alle ansehen"
|
||||
},
|
||||
"dashboard.notifications.link.view-history": {
|
||||
"message": "Benachrichtigungsverlauf ansehen"
|
||||
},
|
||||
"dashboard.notifications.link.view-more": {
|
||||
"message": "Siehe {extraNotifs} weitere {extraNotifs, plural, one {Benachrichtigung} other {Benachrichtigungen}} an"
|
||||
},
|
||||
"dashboard.notifications.loading": {
|
||||
"message": "Benachrichtigungen werden geladen..."
|
||||
},
|
||||
"dashboard.organizations.button.create": {
|
||||
"message": "Organisation erstellen"
|
||||
},
|
||||
"dashboard.organizations.empty.cta": {
|
||||
"message": "Erstelle eine Organisation!"
|
||||
},
|
||||
"dashboard.organizations.error.fetch": {
|
||||
"message": "Fehler beim Laden der Organisationen"
|
||||
},
|
||||
"dashboard.organizations.member-count": {
|
||||
"message": "{count} {count, plural, one {Mitglied} other {Mitglieder}}"
|
||||
},
|
||||
"dashboard.organizations.title": {
|
||||
"message": "Organisationen"
|
||||
},
|
||||
"dashboard.projects.bulk-edit-hint": {
|
||||
"message": "Du kannst mehrere Projekte gleichzeitig bearbeiten, indem du sie unten auswählst."
|
||||
},
|
||||
"dashboard.projects.bulk-edit.server-disabled": {
|
||||
"message": "Serverprojekte unterstützen Massenbearbeitung nicht"
|
||||
},
|
||||
"dashboard.projects.empty": {
|
||||
"message": "Du hast noch keine Projekte. Klicke auf den grünen Knopf oben um loszulegen."
|
||||
},
|
||||
"dashboard.projects.head-title": {
|
||||
"message": "Projekte"
|
||||
},
|
||||
"dashboard.projects.links.and-more": {
|
||||
"message": "und {count} weitere..."
|
||||
},
|
||||
"dashboard.projects.links.button.clear-link": {
|
||||
"message": "Link entfernen"
|
||||
},
|
||||
"dashboard.projects.links.button.edit": {
|
||||
"message": "Links bearbeiten"
|
||||
},
|
||||
"dashboard.projects.links.changes-applied": {
|
||||
"message": "Änderungen werden auf <strong>{count}</strong> {count, plural, one {Projekt} other {Projekte}} angewendet."
|
||||
},
|
||||
"dashboard.projects.links.description": {
|
||||
"message": "Alle Links, die du unten angibst, werden in jedem der ausgewählten Projekte überschrieben. Alle, die du leer lässt, werden ignoriert. Du kannst einen Link mithilfe der Papierkorb-Schaltfläche aus allen ausgewählten Projekten entfernen."
|
||||
},
|
||||
"dashboard.projects.links.discord-invite.description": {
|
||||
"message": "Ein Einladungslink zu deinem Discord-Server."
|
||||
},
|
||||
"dashboard.projects.links.discord-invite.label": {
|
||||
"message": "Discord-Einladung"
|
||||
},
|
||||
"dashboard.projects.links.issue-tracker.description": {
|
||||
"message": "Ein Ort, an dem Nutzer Fehler, Probleme und Bedenken zu deinem Projekt melden können."
|
||||
},
|
||||
"dashboard.projects.links.issue-tracker.label": {
|
||||
"message": "Issue-Tracker"
|
||||
},
|
||||
"dashboard.projects.links.placeholder.cleared": {
|
||||
"message": "Der vorhandene Link wird gelöscht"
|
||||
},
|
||||
"dashboard.projects.links.placeholder.valid-discord-url": {
|
||||
"message": "Gib eine gültige Discordeinladungs-URL ein"
|
||||
},
|
||||
"dashboard.projects.links.placeholder.valid-url": {
|
||||
"message": "Gib eine gültige URL ein"
|
||||
},
|
||||
"dashboard.projects.links.show-all-projects": {
|
||||
"message": "Alle Projekte anzeigen"
|
||||
},
|
||||
"dashboard.projects.links.source-code.description": {
|
||||
"message": "Ein Seite/ein Repository mit dem Quellcode deines Projekts"
|
||||
},
|
||||
"dashboard.projects.links.source-code.label": {
|
||||
"message": "Quellcode"
|
||||
},
|
||||
"dashboard.projects.links.wiki-page.description": {
|
||||
"message": "Eine Seite mit Informationen, Dokumentation und Hilfe zum Projekt."
|
||||
},
|
||||
"dashboard.projects.links.wiki-page.label": {
|
||||
"message": "Wikiseite"
|
||||
},
|
||||
"dashboard.projects.notification.bulk-edit-success": {
|
||||
"message": "Links von Projekten wurden massenbearbeitet."
|
||||
},
|
||||
"dashboard.projects.project.icon-alt": {
|
||||
"message": "Icon für {title}"
|
||||
},
|
||||
"dashboard.projects.project.moderator-message-aria": {
|
||||
"message": "Projekt hat eine Nachricht von den Moderatoren. Rufe das Projekt auf, um mehr zu erfahren."
|
||||
},
|
||||
"dashboard.projects.project.review-environment-metadata": {
|
||||
"message": "Bitte überprüfe die Umgebungsmetadaten"
|
||||
},
|
||||
"dashboard.projects.sort.ascending": {
|
||||
"message": "Aufsteigend"
|
||||
},
|
||||
"dashboard.projects.sort.descending": {
|
||||
"message": "Absteigend"
|
||||
},
|
||||
"dashboard.projects.sort.option.name": {
|
||||
"message": "Name"
|
||||
},
|
||||
@@ -896,6 +1025,9 @@
|
||||
"dashboard.projects.table.type": {
|
||||
"message": "Typ"
|
||||
},
|
||||
"dashboard.report.title": {
|
||||
"message": "Meldung {id}"
|
||||
},
|
||||
"dashboard.reports.active-title": {
|
||||
"message": "Aktive Meldungen"
|
||||
},
|
||||
|
||||
@@ -1229,6 +1229,39 @@
|
||||
"dashboard.withdraw.error.tax-form.title": {
|
||||
"message": "Please complete tax form"
|
||||
},
|
||||
"discover.install.back-to-server": {
|
||||
"message": "Back to server"
|
||||
},
|
||||
"discover.install.back-to-setup": {
|
||||
"message": "Back to setup"
|
||||
},
|
||||
"discover.install.cancel-reset": {
|
||||
"message": "Cancel reset"
|
||||
},
|
||||
"discover.install.error.no-server-world": {
|
||||
"message": "No server world is available for install."
|
||||
},
|
||||
"discover.install.error.some-projects-failed.description": {
|
||||
"message": "Failed projects were not added. You can try installing them again."
|
||||
},
|
||||
"discover.install.error.some-projects-failed.title": {
|
||||
"message": "Some projects failed to install"
|
||||
},
|
||||
"discover.install.error.unsupported-content-type": {
|
||||
"message": "This content type cannot be installed to a server from browse."
|
||||
},
|
||||
"discover.install.heading.reset-modpack": {
|
||||
"message": "Selecting modpack to install after reset"
|
||||
},
|
||||
"discover.seo.description": {
|
||||
"message": "Search and browse thousands of Minecraft {projectType, select, mod {mods} modpack {modpacks} resourcepack {resource packs} shader {shaders} plugin {plugins} datapack {datapacks} other {projects}} on Modrinth with instant, accurate search results. Our filters help you quickly find the best Minecraft {projectType, select, mod {mods} modpack {modpacks} resourcepack {resource packs} shader {shaders} plugin {plugins} datapack {datapacks} other {projects}}."
|
||||
},
|
||||
"discover.seo.title": {
|
||||
"message": "Search {projectType, select, mod {mods} modpack {modpacks} resourcepack {resource packs} shader {shaders} plugin {plugins} datapack {datapacks} other {projects}}"
|
||||
},
|
||||
"discover.seo.title-with-query": {
|
||||
"message": "Search {projectType, select, mod {mods} modpack {modpacks} resourcepack {resource packs} shader {shaders} plugin {plugins} datapack {datapacks} other {projects}} | {query}"
|
||||
},
|
||||
"error.collection.404.list_item.1": {
|
||||
"message": "You may have mistyped the collection's URL."
|
||||
},
|
||||
@@ -1244,6 +1277,12 @@
|
||||
"error.collection.404.title": {
|
||||
"message": "Collection not found"
|
||||
},
|
||||
"error.generic.401.signed-in-as": {
|
||||
"message": "You're currently signed in as:"
|
||||
},
|
||||
"error.generic.401.title": {
|
||||
"message": "You don't have access to this page"
|
||||
},
|
||||
"error.generic.404.subtitle": {
|
||||
"message": "The page you were looking for doesn't seem to exist."
|
||||
},
|
||||
@@ -1670,6 +1709,9 @@
|
||||
"layout.action.create-new": {
|
||||
"message": "Create new..."
|
||||
},
|
||||
"layout.action.external-projects": {
|
||||
"message": "External projects"
|
||||
},
|
||||
"layout.action.file-lookup": {
|
||||
"message": "File lookup"
|
||||
},
|
||||
@@ -1922,6 +1964,9 @@
|
||||
"moderation.moderate": {
|
||||
"message": "Moderate"
|
||||
},
|
||||
"moderation.page.external-projects": {
|
||||
"message": "External projects"
|
||||
},
|
||||
"moderation.page.projects": {
|
||||
"message": "Projects"
|
||||
},
|
||||
@@ -1929,7 +1974,7 @@
|
||||
"message": "Reports"
|
||||
},
|
||||
"moderation.page.technicalReview": {
|
||||
"message": "Technical Review"
|
||||
"message": "Tech review"
|
||||
},
|
||||
"muralpay.account-type.checking": {
|
||||
"message": "Checking"
|
||||
@@ -2615,6 +2660,45 @@
|
||||
"project.settings.general.url.title": {
|
||||
"message": "URL"
|
||||
},
|
||||
"project.settings.permissions.attention-needed.description.proj-approved": {
|
||||
"message": "Please provide proof that you have permission to redistribute all of the following files and any withheld versions will be automatically published."
|
||||
},
|
||||
"project.settings.permissions.attention-needed.description.proj-draft": {
|
||||
"message": "Please provide proof that you have permission to redistribute all of the following files before you can submit your project for review."
|
||||
},
|
||||
"project.settings.permissions.attention-needed.title": {
|
||||
"message": "Unknown embedded content"
|
||||
},
|
||||
"project.settings.permissions.completed.description": {
|
||||
"message": "All external content has attributions provided."
|
||||
},
|
||||
"project.settings.permissions.completed.title": {
|
||||
"message": "Attributions completed!"
|
||||
},
|
||||
"project.settings.permissions.empty-state.description": {
|
||||
"message": "None of your versions contain external content, so you don't need to worry about obtaining permissions."
|
||||
},
|
||||
"project.settings.permissions.empty-state.heading": {
|
||||
"message": "You're all set!"
|
||||
},
|
||||
"project.settings.permissions.fail.description": {
|
||||
"message": "You don't have permission to redistribute some of the external content you've added. In order to publish on Modrinth, remove the infringing content."
|
||||
},
|
||||
"project.settings.permissions.fail.title": {
|
||||
"message": "Some content can't be included"
|
||||
},
|
||||
"project.settings.permissions.info-banner.description": {
|
||||
"message": "If you include content that isn’t hosted on Modrinth, you need to let us know where it’s from and verify that you have permission to distribute the files. Check out <link>our guide</link> to learn about how to do this properly!"
|
||||
},
|
||||
"project.settings.permissions.info-banner.title": {
|
||||
"message": "Learn how attributions work"
|
||||
},
|
||||
"project.settings.permissions.learn-more": {
|
||||
"message": "Learn more"
|
||||
},
|
||||
"project.settings.permissions.search-placeholder": {
|
||||
"message": "Search {count} {count, plural, one {external project} other {external projects}}..."
|
||||
},
|
||||
"project.settings.title": {
|
||||
"message": "Settings"
|
||||
},
|
||||
@@ -2633,6 +2717,15 @@
|
||||
"project.versions.title": {
|
||||
"message": "Versions"
|
||||
},
|
||||
"project.versions.withheld-versions-warning.description": {
|
||||
"message": "{count, plural, one {This version is} other {These versions are}} currently withheld and not publicly listed. Please provide proof that you have permission to redistribute certain files included in the modpack {count, plural, one {version} other {versions}}."
|
||||
},
|
||||
"project.versions.withheld-versions-warning.resolve-button": {
|
||||
"message": "Resolve"
|
||||
},
|
||||
"project.versions.withheld-versions-warning.title": {
|
||||
"message": "{count, plural, one {Version {version_name}} other {Versions}} withheld due to unknown embedded content"
|
||||
},
|
||||
"report.already-reported": {
|
||||
"message": "You've already reported {title}"
|
||||
},
|
||||
@@ -3002,6 +3095,9 @@
|
||||
"search.filter.locked.server.sync": {
|
||||
"message": "Sync with server"
|
||||
},
|
||||
"servers.manage.content.title": {
|
||||
"message": "Content - {serverName} - Modrinth"
|
||||
},
|
||||
"servers.notice.actions": {
|
||||
"message": "Actions"
|
||||
},
|
||||
|
||||
@@ -579,10 +579,10 @@
|
||||
"message": "de {count} {count, plural,one {proyecto} other {proyectos}}"
|
||||
},
|
||||
"dashboard.analytics.total-downloads": {
|
||||
"message": "Descargas totales"
|
||||
"message": "Todas las descargas"
|
||||
},
|
||||
"dashboard.analytics.total-followers": {
|
||||
"message": "Total de seguidores"
|
||||
"message": "Todos los seguidores"
|
||||
},
|
||||
"dashboard.collections.button.create-new": {
|
||||
"message": "Crear nuevo"
|
||||
@@ -608,6 +608,18 @@
|
||||
"dashboard.collections.long-title": {
|
||||
"message": "Tus colecciones"
|
||||
},
|
||||
"dashboard.collections.placeholder.search": {
|
||||
"message": "Buscar en colecciones..."
|
||||
},
|
||||
"dashboard.collections.sort.name-ascending": {
|
||||
"message": "Nombre (A-Z)"
|
||||
},
|
||||
"dashboard.collections.sort.recently-created": {
|
||||
"message": "Creado recientemente"
|
||||
},
|
||||
"dashboard.collections.sort.recently-updated": {
|
||||
"message": "Actualizado recientemente"
|
||||
},
|
||||
"dashboard.creator-tax-form-modal.confirmation.download-button": {
|
||||
"message": "Descargar {formType}"
|
||||
},
|
||||
@@ -860,6 +872,168 @@
|
||||
"dashboard.creator-withdraw-modal.withdraw-limit-used": {
|
||||
"message": "Has agotado tu límite de retiro <b>{withdrawLimit}</b>. Debes completar un formulario de impuestos para retirar más."
|
||||
},
|
||||
"dashboard.head-title": {
|
||||
"message": "Panel de control"
|
||||
},
|
||||
"dashboard.notifications.button.mark-all-as-read": {
|
||||
"message": "Marcar todas cómo leídas"
|
||||
},
|
||||
"dashboard.notifications.button.view-history": {
|
||||
"message": "Ver historial"
|
||||
},
|
||||
"dashboard.notifications.empty.no-unread": {
|
||||
"message": "No tienes ningúna notificación sin leer."
|
||||
},
|
||||
"dashboard.notifications.error.loading": {
|
||||
"message": "Error al cargar las notificaciones:"
|
||||
},
|
||||
"dashboard.notifications.history.label": {
|
||||
"message": "Historial"
|
||||
},
|
||||
"dashboard.notifications.history.title": {
|
||||
"message": "Historial de notificaciones"
|
||||
},
|
||||
"dashboard.notifications.link.see-all": {
|
||||
"message": "Ver todas"
|
||||
},
|
||||
"dashboard.notifications.link.view-history": {
|
||||
"message": "Ver historial de notificaciones"
|
||||
},
|
||||
"dashboard.notifications.link.view-more": {
|
||||
"message": "Ver {extraNotifs} {extraNotifs, plural,one {notificación} other {notificaciones}} más"
|
||||
},
|
||||
"dashboard.notifications.loading": {
|
||||
"message": "Cargando notificaciones..."
|
||||
},
|
||||
"dashboard.organizations.button.create": {
|
||||
"message": "Crear organización"
|
||||
},
|
||||
"dashboard.organizations.empty.cta": {
|
||||
"message": "¡Haz una organización!"
|
||||
},
|
||||
"dashboard.organizations.error.fetch": {
|
||||
"message": "No se pudo obtener las organizaciones"
|
||||
},
|
||||
"dashboard.organizations.member-count": {
|
||||
"message": "{count} {count, plural,one {miembro} other {miembros}}"
|
||||
},
|
||||
"dashboard.organizations.title": {
|
||||
"message": "Organizaciones"
|
||||
},
|
||||
"dashboard.projects.bulk-edit-hint": {
|
||||
"message": "Puedes editar varios proyectos a la vez seleccionándolos a continuación."
|
||||
},
|
||||
"dashboard.projects.bulk-edit.server-disabled": {
|
||||
"message": "Proyectos de servidor no soportan edición masiva"
|
||||
},
|
||||
"dashboard.projects.empty": {
|
||||
"message": "Aún no tienes ningún proyecto. Haz clic en el botón verde de arriba para empezar."
|
||||
},
|
||||
"dashboard.projects.head-title": {
|
||||
"message": "Proyectos"
|
||||
},
|
||||
"dashboard.projects.links.and-more": {
|
||||
"message": "y {count} más..."
|
||||
},
|
||||
"dashboard.projects.links.button.clear-link": {
|
||||
"message": "Borrar enlace"
|
||||
},
|
||||
"dashboard.projects.links.button.edit": {
|
||||
"message": "Editar enlaces"
|
||||
},
|
||||
"dashboard.projects.links.changes-applied": {
|
||||
"message": "Cambios seran a aplicados a {count} {count, plural,one {proyecto} other {proyectos}}."
|
||||
},
|
||||
"dashboard.projects.links.description": {
|
||||
"message": "Los enlaces que especifique a continuación se sobrescribirán en cada uno de los proyectos seleccionados. Los que deje en blanco se ignorarán. Puede eliminar un enlace de todos los proyectos seleccionados mediante el botón de la papelera."
|
||||
},
|
||||
"dashboard.projects.links.discord-invite.description": {
|
||||
"message": "Un link de invitación a tu servidor de Discord."
|
||||
},
|
||||
"dashboard.projects.links.discord-invite.label": {
|
||||
"message": "Invitación de Discord"
|
||||
},
|
||||
"dashboard.projects.links.issue-tracker.description": {
|
||||
"message": "Un lugar para usuarios para reportar bugs, errores, y preocupaciones sobre tu proyecto."
|
||||
},
|
||||
"dashboard.projects.links.issue-tracker.label": {
|
||||
"message": "Registro de errores"
|
||||
},
|
||||
"dashboard.projects.links.placeholder.cleared": {
|
||||
"message": "Se borrarán los enlaces existentes"
|
||||
},
|
||||
"dashboard.projects.links.placeholder.valid-discord-url": {
|
||||
"message": "Introduce una URL de invitación de Discord válida"
|
||||
},
|
||||
"dashboard.projects.links.placeholder.valid-url": {
|
||||
"message": "Introduce una URL válida"
|
||||
},
|
||||
"dashboard.projects.links.show-all-projects": {
|
||||
"message": "Mostrar todos los proyectos"
|
||||
},
|
||||
"dashboard.projects.links.source-code.description": {
|
||||
"message": "Una página/repositorio que contenga el código fuente de tu proyecto"
|
||||
},
|
||||
"dashboard.projects.links.source-code.label": {
|
||||
"message": "Código fuente"
|
||||
},
|
||||
"dashboard.projects.links.wiki-page.description": {
|
||||
"message": "Una página que contenga información y documentación que ayude al proyecto."
|
||||
},
|
||||
"dashboard.projects.links.wiki-page.label": {
|
||||
"message": "Página wiki"
|
||||
},
|
||||
"dashboard.projects.notification.bulk-edit-success": {
|
||||
"message": "Edición masiva seleccionada de tus links de proyecto."
|
||||
},
|
||||
"dashboard.projects.project.icon-alt": {
|
||||
"message": "Icono de {title}"
|
||||
},
|
||||
"dashboard.projects.project.moderator-message-aria": {
|
||||
"message": "El proyecto tiene un mensaje de los moderadores. Entra al proyecto para obtener más información."
|
||||
},
|
||||
"dashboard.projects.project.review-environment-metadata": {
|
||||
"message": "Por favor revisa los metadatos de entorno"
|
||||
},
|
||||
"dashboard.projects.sort.ascending": {
|
||||
"message": "Ascendente"
|
||||
},
|
||||
"dashboard.projects.sort.descending": {
|
||||
"message": "Descendente"
|
||||
},
|
||||
"dashboard.projects.sort.option.name": {
|
||||
"message": "Nombre"
|
||||
},
|
||||
"dashboard.projects.sort.option.status": {
|
||||
"message": "Estado"
|
||||
},
|
||||
"dashboard.projects.sort.option.type": {
|
||||
"message": "Tipo"
|
||||
},
|
||||
"dashboard.projects.table.icon": {
|
||||
"message": "Icono"
|
||||
},
|
||||
"dashboard.projects.table.id": {
|
||||
"message": "ID"
|
||||
},
|
||||
"dashboard.projects.table.name": {
|
||||
"message": "Nombre"
|
||||
},
|
||||
"dashboard.projects.table.status": {
|
||||
"message": "Estado"
|
||||
},
|
||||
"dashboard.projects.table.type": {
|
||||
"message": "Tipo"
|
||||
},
|
||||
"dashboard.report.title": {
|
||||
"message": "Reporte {id}"
|
||||
},
|
||||
"dashboard.reports.active-title": {
|
||||
"message": "Reportes activos"
|
||||
},
|
||||
"dashboard.reports.title": {
|
||||
"message": "Reportes"
|
||||
},
|
||||
"dashboard.revenue.available-now": {
|
||||
"message": "Disponible ahora"
|
||||
},
|
||||
@@ -896,6 +1070,9 @@
|
||||
"dashboard.revenue.transactions.btn.download-csv": {
|
||||
"message": "Descargar como CSV"
|
||||
},
|
||||
"dashboard.revenue.transactions.head-title": {
|
||||
"message": "Historial de transacciones"
|
||||
},
|
||||
"dashboard.revenue.transactions.header": {
|
||||
"message": "Transacciones"
|
||||
},
|
||||
@@ -905,9 +1082,18 @@
|
||||
"dashboard.revenue.transactions.none.desc": {
|
||||
"message": "Tus pagos y retiros aparecerán aquí."
|
||||
},
|
||||
"dashboard.revenue.transactions.period.last-month": {
|
||||
"message": "Mes anterior"
|
||||
},
|
||||
"dashboard.revenue.transactions.period.this-month": {
|
||||
"message": "Este mes"
|
||||
},
|
||||
"dashboard.revenue.transactions.see-all": {
|
||||
"message": "Ver todo"
|
||||
},
|
||||
"dashboard.revenue.transactions.year.all": {
|
||||
"message": "Todos los años"
|
||||
},
|
||||
"dashboard.revenue.withdraw.blocked-tin-mismatch": {
|
||||
"message": "Tus retiros están temporalmente bloqueados por que tu TIN o SSN no coinciden con los registros del IRS. Conte en contacto con el equipo de soporte para restablecerlos y reenviar tu formulario fiscal."
|
||||
},
|
||||
@@ -920,6 +1106,33 @@
|
||||
"dashboard.revenue.withdraw.header": {
|
||||
"message": "Retirar"
|
||||
},
|
||||
"dashboard.sidebar.label.activeReports": {
|
||||
"message": "Reportes activos"
|
||||
},
|
||||
"dashboard.sidebar.label.analytics": {
|
||||
"message": "Estadísticas"
|
||||
},
|
||||
"dashboard.sidebar.label.creators": {
|
||||
"message": "Creadores"
|
||||
},
|
||||
"dashboard.sidebar.label.dashboard": {
|
||||
"message": "Panel de control"
|
||||
},
|
||||
"dashboard.sidebar.label.notifications": {
|
||||
"message": "Notificaciones"
|
||||
},
|
||||
"dashboard.sidebar.label.organizations": {
|
||||
"message": "Organizaciones"
|
||||
},
|
||||
"dashboard.sidebar.label.overview": {
|
||||
"message": "Resumen"
|
||||
},
|
||||
"dashboard.sidebar.label.projects": {
|
||||
"message": "Proyectos"
|
||||
},
|
||||
"dashboard.sidebar.label.revenue": {
|
||||
"message": "Ingresos"
|
||||
},
|
||||
"dashboard.withdraw.completion.account": {
|
||||
"message": "Cuenta"
|
||||
},
|
||||
|
||||
@@ -608,6 +608,18 @@
|
||||
"dashboard.collections.long-title": {
|
||||
"message": "Tus colecciones"
|
||||
},
|
||||
"dashboard.collections.placeholder.search": {
|
||||
"message": "Buscar colecciones..."
|
||||
},
|
||||
"dashboard.collections.sort.name-ascending": {
|
||||
"message": "Nombre (A-Z)"
|
||||
},
|
||||
"dashboard.collections.sort.recently-created": {
|
||||
"message": "Creados recientemente"
|
||||
},
|
||||
"dashboard.collections.sort.recently-updated": {
|
||||
"message": "Actualizados recientemente"
|
||||
},
|
||||
"dashboard.creator-tax-form-modal.confirmation.download-button": {
|
||||
"message": "Descarga {formType}"
|
||||
},
|
||||
@@ -860,6 +872,168 @@
|
||||
"dashboard.creator-withdraw-modal.withdraw-limit-used": {
|
||||
"message": "Has agotado tu límite de retiro de <b>{withdrawLimit}</b>. Debes completar un formulario de impuestos para retirar más."
|
||||
},
|
||||
"dashboard.head-title": {
|
||||
"message": "Panel de control"
|
||||
},
|
||||
"dashboard.notifications.button.mark-all-as-read": {
|
||||
"message": "Marcar todo como leido"
|
||||
},
|
||||
"dashboard.notifications.button.view-history": {
|
||||
"message": "Ver historial"
|
||||
},
|
||||
"dashboard.notifications.empty.no-unread": {
|
||||
"message": "No tienes ninguna notificación sin leer."
|
||||
},
|
||||
"dashboard.notifications.error.loading": {
|
||||
"message": "Error al cargar las notificaciones:"
|
||||
},
|
||||
"dashboard.notifications.history.label": {
|
||||
"message": "Historial"
|
||||
},
|
||||
"dashboard.notifications.history.title": {
|
||||
"message": "Historial de notificaciones"
|
||||
},
|
||||
"dashboard.notifications.link.see-all": {
|
||||
"message": "Ver todos"
|
||||
},
|
||||
"dashboard.notifications.link.view-history": {
|
||||
"message": "Mostrar historial de notificaciones"
|
||||
},
|
||||
"dashboard.notifications.link.view-more": {
|
||||
"message": "Mostrar {extraNotifs} {extraNotifs, plural,one {notificación}other {otras notificaciones}}"
|
||||
},
|
||||
"dashboard.notifications.loading": {
|
||||
"message": "Cargando notificaciones..."
|
||||
},
|
||||
"dashboard.organizations.button.create": {
|
||||
"message": "Crear organización"
|
||||
},
|
||||
"dashboard.organizations.empty.cta": {
|
||||
"message": "¡Crea una organicación!"
|
||||
},
|
||||
"dashboard.organizations.error.fetch": {
|
||||
"message": "No se pudo obtener las organizaciones"
|
||||
},
|
||||
"dashboard.organizations.member-count": {
|
||||
"message": "{count} {count, plural,one {miembro}other {miembros}}"
|
||||
},
|
||||
"dashboard.organizations.title": {
|
||||
"message": "Organizaciones"
|
||||
},
|
||||
"dashboard.projects.bulk-edit-hint": {
|
||||
"message": "Puedes editar múltiples proyectos a la vez seleccionándolos abajo."
|
||||
},
|
||||
"dashboard.projects.bulk-edit.server-disabled": {
|
||||
"message": "Proyectos de servidor no soportan edición masiva"
|
||||
},
|
||||
"dashboard.projects.empty": {
|
||||
"message": "No tienes ningún proyecto todavía. Haz clic en el botón verte de arriba para comenzar."
|
||||
},
|
||||
"dashboard.projects.head-title": {
|
||||
"message": "Proyectos"
|
||||
},
|
||||
"dashboard.projects.links.and-more": {
|
||||
"message": "y {count} más..."
|
||||
},
|
||||
"dashboard.projects.links.button.clear-link": {
|
||||
"message": "Borrar link"
|
||||
},
|
||||
"dashboard.projects.links.button.edit": {
|
||||
"message": "Editar links"
|
||||
},
|
||||
"dashboard.projects.links.changes-applied": {
|
||||
"message": "Cambios seran a aplicados a {count} {count, plural,one {proyecto} other {proyectos}}."
|
||||
},
|
||||
"dashboard.projects.links.description": {
|
||||
"message": "Los enlaces que especifique a continuación se sobrescribirán en cada uno de los proyectos seleccionados. Los que deje en blanco se ignorarán. Puede eliminar un enlace de todos los proyectos seleccionados mediante el botón de la papelera."
|
||||
},
|
||||
"dashboard.projects.links.discord-invite.description": {
|
||||
"message": "Un link de invitación a tu servidor de Discord."
|
||||
},
|
||||
"dashboard.projects.links.discord-invite.label": {
|
||||
"message": "Invitación de Discord"
|
||||
},
|
||||
"dashboard.projects.links.issue-tracker.description": {
|
||||
"message": "Un lugar para usuarios para reportar bugs, errores, y preocupaciones sobre tu proyecto."
|
||||
},
|
||||
"dashboard.projects.links.issue-tracker.label": {
|
||||
"message": "Seguimientos de errores"
|
||||
},
|
||||
"dashboard.projects.links.placeholder.cleared": {
|
||||
"message": "Link existente será borrado"
|
||||
},
|
||||
"dashboard.projects.links.placeholder.valid-discord-url": {
|
||||
"message": "Ingrese una URL de invitación de Discord válida"
|
||||
},
|
||||
"dashboard.projects.links.placeholder.valid-url": {
|
||||
"message": "Ingrese una URL válida"
|
||||
},
|
||||
"dashboard.projects.links.show-all-projects": {
|
||||
"message": "Mostrar todos los proyectos"
|
||||
},
|
||||
"dashboard.projects.links.source-code.description": {
|
||||
"message": "Una página/repostorio que contiene él código base de tu proyecto"
|
||||
},
|
||||
"dashboard.projects.links.source-code.label": {
|
||||
"message": "Código base"
|
||||
},
|
||||
"dashboard.projects.links.wiki-page.description": {
|
||||
"message": "Una página que contiene información, documentación, y ayuda sobre el proyecto."
|
||||
},
|
||||
"dashboard.projects.links.wiki-page.label": {
|
||||
"message": "Página de wiki"
|
||||
},
|
||||
"dashboard.projects.notification.bulk-edit-success": {
|
||||
"message": "Edición masiva seleccionada de tus links de proyecto."
|
||||
},
|
||||
"dashboard.projects.project.icon-alt": {
|
||||
"message": "Icono para {title}"
|
||||
},
|
||||
"dashboard.projects.project.moderator-message-aria": {
|
||||
"message": "El proyecto tiene un mensaje de los moderadores. Mire el proyecto para ver más."
|
||||
},
|
||||
"dashboard.projects.project.review-environment-metadata": {
|
||||
"message": "Por favor revise el entorno del metadata"
|
||||
},
|
||||
"dashboard.projects.sort.ascending": {
|
||||
"message": "Ascendiendo"
|
||||
},
|
||||
"dashboard.projects.sort.descending": {
|
||||
"message": "Descendiendo"
|
||||
},
|
||||
"dashboard.projects.sort.option.name": {
|
||||
"message": "Nombre"
|
||||
},
|
||||
"dashboard.projects.sort.option.status": {
|
||||
"message": "Estado"
|
||||
},
|
||||
"dashboard.projects.sort.option.type": {
|
||||
"message": "Tipo"
|
||||
},
|
||||
"dashboard.projects.table.icon": {
|
||||
"message": "Icono"
|
||||
},
|
||||
"dashboard.projects.table.id": {
|
||||
"message": "ID"
|
||||
},
|
||||
"dashboard.projects.table.name": {
|
||||
"message": "Nombre"
|
||||
},
|
||||
"dashboard.projects.table.status": {
|
||||
"message": "Estado"
|
||||
},
|
||||
"dashboard.projects.table.type": {
|
||||
"message": "Tipo"
|
||||
},
|
||||
"dashboard.report.title": {
|
||||
"message": "Reporte {id}"
|
||||
},
|
||||
"dashboard.reports.active-title": {
|
||||
"message": "Informes activos"
|
||||
},
|
||||
"dashboard.reports.title": {
|
||||
"message": "Reportes"
|
||||
},
|
||||
"dashboard.revenue.available-now": {
|
||||
"message": "Ya disponible"
|
||||
},
|
||||
@@ -896,6 +1070,9 @@
|
||||
"dashboard.revenue.transactions.btn.download-csv": {
|
||||
"message": "Descargar como CSV"
|
||||
},
|
||||
"dashboard.revenue.transactions.head-title": {
|
||||
"message": "Historia de transacciones"
|
||||
},
|
||||
"dashboard.revenue.transactions.header": {
|
||||
"message": "Transacciones"
|
||||
},
|
||||
@@ -905,9 +1082,18 @@
|
||||
"dashboard.revenue.transactions.none.desc": {
|
||||
"message": "Tus pagos y retiros aparecerán aquí."
|
||||
},
|
||||
"dashboard.revenue.transactions.period.last-month": {
|
||||
"message": "Último mes"
|
||||
},
|
||||
"dashboard.revenue.transactions.period.this-month": {
|
||||
"message": "Este mes"
|
||||
},
|
||||
"dashboard.revenue.transactions.see-all": {
|
||||
"message": "Ver todos"
|
||||
},
|
||||
"dashboard.revenue.transactions.year.all": {
|
||||
"message": "De todos los años"
|
||||
},
|
||||
"dashboard.revenue.withdraw.blocked-tin-mismatch": {
|
||||
"message": "Tus retiros están temporalmente bloqueados porque tú NIF o NUSS no coincidió con tus registros del IRS. Por favor ponte en contacto con el soporte técnico para resetear y reenviar tu formulario de impuestos."
|
||||
},
|
||||
@@ -920,6 +1106,33 @@
|
||||
"dashboard.revenue.withdraw.header": {
|
||||
"message": "Retirar"
|
||||
},
|
||||
"dashboard.sidebar.label.activeReports": {
|
||||
"message": "Reportes activos"
|
||||
},
|
||||
"dashboard.sidebar.label.analytics": {
|
||||
"message": "Analíticas"
|
||||
},
|
||||
"dashboard.sidebar.label.creators": {
|
||||
"message": "Creadores"
|
||||
},
|
||||
"dashboard.sidebar.label.dashboard": {
|
||||
"message": "Panel de control"
|
||||
},
|
||||
"dashboard.sidebar.label.notifications": {
|
||||
"message": "Notificaciones"
|
||||
},
|
||||
"dashboard.sidebar.label.organizations": {
|
||||
"message": "Organizaciones"
|
||||
},
|
||||
"dashboard.sidebar.label.overview": {
|
||||
"message": "Descripción general"
|
||||
},
|
||||
"dashboard.sidebar.label.projects": {
|
||||
"message": "Proyectos"
|
||||
},
|
||||
"dashboard.sidebar.label.revenue": {
|
||||
"message": "Ingresos"
|
||||
},
|
||||
"dashboard.withdraw.completion.account": {
|
||||
"message": "Cuenta"
|
||||
},
|
||||
|
||||
@@ -108,7 +108,7 @@
|
||||
"message": "Pourcentage du processeur"
|
||||
},
|
||||
"app-marketing.features.performance.description": {
|
||||
"message": "Modrinth App est plus performante que la plupart des autres gestionnaires de mods, tout en n'utilisant que 150 Mo de RAM !"
|
||||
"message": "Modrinth App est plus performante que la plupart des autres gestionnaires de mods, tout en n'utilisant que 150 Mo de mémoire vive !"
|
||||
},
|
||||
"app-marketing.features.performance.discord": {
|
||||
"message": "Discord"
|
||||
|
||||
@@ -177,7 +177,7 @@
|
||||
"message": "amit eddig használtál"
|
||||
},
|
||||
"app-marketing.hero.app-screenshot-alt": {
|
||||
"message": "A Modrinth alkalmazás képernyőképe, amelyen a Cobblemon profil a „Tartalom” oldalon van megnyitva."
|
||||
"message": "A Modrinth App képernyőképe, amelyen a Cobblemon profil a „Tartalom” oldalon van megnyitva."
|
||||
},
|
||||
"app-marketing.hero.description": {
|
||||
"message": "A Modrinth App egy egyedülálló, nyílt forráskódú kliens, amely lehetővé teszi, hogy kedvenc modjaidat játssz, és naprakészen tartsd őket, mindezt egy kis, praktikus csomagban."
|
||||
@@ -228,7 +228,7 @@
|
||||
"message": "Visszaállítási e-mail küldése"
|
||||
},
|
||||
"auth.reset-password.method-choice.description": {
|
||||
"message": "Írja be az e-mail címét az alábbi mezőbe, és küldünk Önnek egy hivatkozást, amivel vissza tudja állítani a fiókját."
|
||||
"message": "Írd be az e-mail címedet az alábbi mezőbe, és küldünk neked egy hivatkozást, amivel vissza tudja állítani a fiókod."
|
||||
},
|
||||
"auth.reset-password.notification.email-sent.text": {
|
||||
"message": "Egy e-mail el lett küldve önnek az email-címére amennyiben az email-cím el volt mente a fiókjában."
|
||||
@@ -267,7 +267,7 @@
|
||||
"message": "Kód beírása..."
|
||||
},
|
||||
"auth.sign-in.additional-options": {
|
||||
"message": "<forgot-password-link>Elfelejtted a jelszavad?</forgot-password-link> • <create-account-link>Hozz létre egy fiókót</create-account-link>"
|
||||
"message": "<forgot-password-link>Elfelejtetted a jelszavad?</forgot-password-link> • <create-account-link>Hozz létre egy fiókot</create-account-link>"
|
||||
},
|
||||
"auth.sign-in.sign-in-with": {
|
||||
"message": "Bejelentkezés ezzel"
|
||||
@@ -282,7 +282,7 @@
|
||||
"message": "Fiók létrehozása"
|
||||
},
|
||||
"auth.sign-up.legal-dislaimer": {
|
||||
"message": "Fiók létrehozásával Ön elfogadja a Modrinth <terms-link>Felhasználási feltételeit</terms-link> és <privacy-policy-link>Adatvédelmi irányelveit</privacy-policy-link>."
|
||||
"message": "Fiók létrehozásával elfogadod a Modrinth <terms-link>Felhasználási feltételeit</terms-link> és <privacy-policy-link>Adatvédelmi irányelveit</privacy-policy-link>."
|
||||
},
|
||||
"auth.sign-up.notification.password-mismatch.text": {
|
||||
"message": "A jelszavak nem jegyeznek!"
|
||||
@@ -339,7 +339,7 @@
|
||||
"message": "Most már része vagy annak a fantasztikus fejlesztők és felfedezők közösségének, akik már építenek, letöltenek és naprakészek maradnak a csodálatos modokkal kapcsolatban."
|
||||
},
|
||||
"auth.welcome.label.tos": {
|
||||
"message": "Fiók létrehozásával Ön elfogadja a Modrinth <terms-link>felhasználási feltételeit</terms-link> és <privacy-policy-link>adatvédelmi irányelveit</privacy-policy-link>."
|
||||
"message": "Fiók létrehozásával elfogadod a Modrinth <terms-link>Felhasználási feltételeit</terms-link> és <privacy-policy-link>Adatvédelmi irányelveit</privacy-policy-link>."
|
||||
},
|
||||
"auth.welcome.long-title": {
|
||||
"message": "Üdvözlünk a Modrinth-onl!"
|
||||
@@ -572,6 +572,15 @@
|
||||
"dashboard.affiliate-links.search": {
|
||||
"message": "Társulati linkek keresése..."
|
||||
},
|
||||
"dashboard.analytics.from-projects": {
|
||||
"message": "{count} projektből"
|
||||
},
|
||||
"dashboard.analytics.total-downloads": {
|
||||
"message": "Összes letöltés"
|
||||
},
|
||||
"dashboard.analytics.total-followers": {
|
||||
"message": "Összes követő"
|
||||
},
|
||||
"dashboard.collections.button.create-new": {
|
||||
"message": "Új létrehozása"
|
||||
},
|
||||
@@ -596,6 +605,15 @@
|
||||
"dashboard.collections.long-title": {
|
||||
"message": "Gyűjteményeid"
|
||||
},
|
||||
"dashboard.collections.sort.name-ascending": {
|
||||
"message": "Név (A-Z)"
|
||||
},
|
||||
"dashboard.collections.sort.recently-created": {
|
||||
"message": "Nemrég létrehozott"
|
||||
},
|
||||
"dashboard.collections.sort.recently-updated": {
|
||||
"message": "Nemrég frissített"
|
||||
},
|
||||
"dashboard.creator-tax-form-modal.confirmation.download-button": {
|
||||
"message": "Letöltés {formType}"
|
||||
},
|
||||
@@ -663,7 +681,7 @@
|
||||
"message": "Üzleti szervezet"
|
||||
},
|
||||
"dashboard.creator-withdraw-modal.kyc.entity-description": {
|
||||
"message": "Az \"Üzleti szervezet\" egy olyan regisztrált szervezetre utal, mint egy vállalat, partnerség vagy LLC."
|
||||
"message": "Az „üzleti szervezet” egy olyan regisztrált szervezetre utal, mint egy vállalat, partnerség vagy Kft."
|
||||
},
|
||||
"dashboard.creator-withdraw-modal.kyc.entity-question": {
|
||||
"message": "Szervezetként vagy személyként veszel fel?"
|
||||
@@ -848,6 +866,150 @@
|
||||
"dashboard.creator-withdraw-modal.withdraw-limit-used": {
|
||||
"message": "Elérted a(z) <b>{withdrawLimit}</b> kifizetési limitet. További kifizetésekhez ki kell töltened egy adózási űrlapot."
|
||||
},
|
||||
"dashboard.head-title": {
|
||||
"message": "Irányítópult"
|
||||
},
|
||||
"dashboard.notifications.button.mark-all-as-read": {
|
||||
"message": "Összes megjelölése olvasottként"
|
||||
},
|
||||
"dashboard.notifications.button.view-history": {
|
||||
"message": "Előzmények megtekintése"
|
||||
},
|
||||
"dashboard.notifications.empty.no-unread": {
|
||||
"message": "Nincsenek olvasatlan értesítéseid."
|
||||
},
|
||||
"dashboard.notifications.error.loading": {
|
||||
"message": "Hiba az értesítések betöltése során:"
|
||||
},
|
||||
"dashboard.notifications.history.label": {
|
||||
"message": "Előzmények"
|
||||
},
|
||||
"dashboard.notifications.history.title": {
|
||||
"message": "Értesítési előzmények"
|
||||
},
|
||||
"dashboard.notifications.link.see-all": {
|
||||
"message": "Összes megtekintése"
|
||||
},
|
||||
"dashboard.notifications.link.view-history": {
|
||||
"message": "Értesítési előzmények megtekintése"
|
||||
},
|
||||
"dashboard.notifications.link.view-more": {
|
||||
"message": "További {extraNotifs} értesítés megtekintése"
|
||||
},
|
||||
"dashboard.notifications.loading": {
|
||||
"message": "Értesítések betöltése"
|
||||
},
|
||||
"dashboard.organizations.button.create": {
|
||||
"message": "Szervezet létrehozása"
|
||||
},
|
||||
"dashboard.organizations.empty.cta": {
|
||||
"message": "Hozz létre egy szervezetet!"
|
||||
},
|
||||
"dashboard.organizations.member-count": {
|
||||
"message": "{count} tag"
|
||||
},
|
||||
"dashboard.organizations.title": {
|
||||
"message": "Szervezetek"
|
||||
},
|
||||
"dashboard.projects.bulk-edit-hint": {
|
||||
"message": "Több projektet is szerkeszthetsz egyszerre, ha kiválasztod őket alább."
|
||||
},
|
||||
"dashboard.projects.empty": {
|
||||
"message": "Még nincsenek projektjeid. Kattints a fenti zöld gombra a kezdéshez."
|
||||
},
|
||||
"dashboard.projects.head-title": {
|
||||
"message": "Projektek"
|
||||
},
|
||||
"dashboard.projects.links.and-more": {
|
||||
"message": "és még {count} további..."
|
||||
},
|
||||
"dashboard.projects.links.button.clear-link": {
|
||||
"message": "Link törlése"
|
||||
},
|
||||
"dashboard.projects.links.button.edit": {
|
||||
"message": "Linkek szerkesztése"
|
||||
},
|
||||
"dashboard.projects.links.changes-applied": {
|
||||
"message": "A változtatások <strong>{count}</strong> projektre vonatkoznak."
|
||||
},
|
||||
"dashboard.projects.links.discord-invite.description": {
|
||||
"message": "Meghívólink a Discord szerveredre."
|
||||
},
|
||||
"dashboard.projects.links.discord-invite.label": {
|
||||
"message": "Discord meghívó"
|
||||
},
|
||||
"dashboard.projects.links.issue-tracker.description": {
|
||||
"message": "Egy hely, ahol a felhasználók hibákat, problémákat és aggályokat jelenthetnek a projekteddel kapcsolatban."
|
||||
},
|
||||
"dashboard.projects.links.issue-tracker.label": {
|
||||
"message": "Hibakereső"
|
||||
},
|
||||
"dashboard.projects.links.placeholder.valid-discord-url": {
|
||||
"message": "Adj meg egy érvényes Discord-meghívólinket"
|
||||
},
|
||||
"dashboard.projects.links.placeholder.valid-url": {
|
||||
"message": "Adj meg egy érvényes linket"
|
||||
},
|
||||
"dashboard.projects.links.show-all-projects": {
|
||||
"message": "Összes projekt megjelenítése"
|
||||
},
|
||||
"dashboard.projects.links.source-code.description": {
|
||||
"message": "Egy oldal/kódtár, amely a projekt forráskódját tartalmazza"
|
||||
},
|
||||
"dashboard.projects.links.source-code.label": {
|
||||
"message": "Forráskód"
|
||||
},
|
||||
"dashboard.projects.links.wiki-page.description": {
|
||||
"message": "Egy oldal, amely információkat, dokumentációt és segítséget tartalmaz a projekthez."
|
||||
},
|
||||
"dashboard.projects.links.wiki-page.label": {
|
||||
"message": "Wiki oldal"
|
||||
},
|
||||
"dashboard.projects.project.icon-alt": {
|
||||
"message": "{title} ikonja"
|
||||
},
|
||||
"dashboard.projects.project.moderator-message-aria": {
|
||||
"message": "A projekt üzenetet kapott a moderátoroktól. További információkért nézd meg a projektet."
|
||||
},
|
||||
"dashboard.projects.sort.ascending": {
|
||||
"message": "Növekvő"
|
||||
},
|
||||
"dashboard.projects.sort.descending": {
|
||||
"message": "Csökkenő"
|
||||
},
|
||||
"dashboard.projects.sort.option.name": {
|
||||
"message": "Név"
|
||||
},
|
||||
"dashboard.projects.sort.option.status": {
|
||||
"message": "Állapot"
|
||||
},
|
||||
"dashboard.projects.sort.option.type": {
|
||||
"message": "Típus"
|
||||
},
|
||||
"dashboard.projects.table.icon": {
|
||||
"message": "Ikon"
|
||||
},
|
||||
"dashboard.projects.table.id": {
|
||||
"message": "Azonosító"
|
||||
},
|
||||
"dashboard.projects.table.name": {
|
||||
"message": "Név"
|
||||
},
|
||||
"dashboard.projects.table.status": {
|
||||
"message": "Állapot"
|
||||
},
|
||||
"dashboard.projects.table.type": {
|
||||
"message": "Típus"
|
||||
},
|
||||
"dashboard.report.title": {
|
||||
"message": "{id} jelentése"
|
||||
},
|
||||
"dashboard.reports.active-title": {
|
||||
"message": "Aktív jelentések"
|
||||
},
|
||||
"dashboard.reports.title": {
|
||||
"message": "Jelentések"
|
||||
},
|
||||
"dashboard.revenue.available-now": {
|
||||
"message": "Elérhető most"
|
||||
},
|
||||
@@ -884,6 +1046,9 @@
|
||||
"dashboard.revenue.transactions.btn.download-csv": {
|
||||
"message": "Letöltés CSV formátumban"
|
||||
},
|
||||
"dashboard.revenue.transactions.head-title": {
|
||||
"message": "Tranzakcióelőzmények"
|
||||
},
|
||||
"dashboard.revenue.transactions.header": {
|
||||
"message": "Tranzakciók"
|
||||
},
|
||||
@@ -893,9 +1058,18 @@
|
||||
"dashboard.revenue.transactions.none.desc": {
|
||||
"message": "A kifizetések és a felvételek itt jelennek meg."
|
||||
},
|
||||
"dashboard.revenue.transactions.period.last-month": {
|
||||
"message": "Múlt hónapban"
|
||||
},
|
||||
"dashboard.revenue.transactions.period.this-month": {
|
||||
"message": "Ebben a hónapban"
|
||||
},
|
||||
"dashboard.revenue.transactions.see-all": {
|
||||
"message": "Összes megtekintése"
|
||||
},
|
||||
"dashboard.revenue.transactions.year.all": {
|
||||
"message": "Minden év"
|
||||
},
|
||||
"dashboard.revenue.withdraw.blocked-tin-mismatch": {
|
||||
"message": "A kifizetéseid ideiglenesen zárolva vannak, mert az adóazonosító számod vagy a társadalombiztosítási számod nem egyezik az adóhatóság (IRS) nyilvántartásában szereplő adatokkal. Kérjük, vedd fel a kapcsolatot az ügyfélszolgálattal az adóbevallásod visszaállításához és újbóli benyújtásához."
|
||||
},
|
||||
@@ -908,6 +1082,33 @@
|
||||
"dashboard.revenue.withdraw.header": {
|
||||
"message": "Pénzfelvétel"
|
||||
},
|
||||
"dashboard.sidebar.label.activeReports": {
|
||||
"message": "Aktív jelentések"
|
||||
},
|
||||
"dashboard.sidebar.label.analytics": {
|
||||
"message": "Statisztika"
|
||||
},
|
||||
"dashboard.sidebar.label.creators": {
|
||||
"message": "Fejlesztők"
|
||||
},
|
||||
"dashboard.sidebar.label.dashboard": {
|
||||
"message": "Irányítópult"
|
||||
},
|
||||
"dashboard.sidebar.label.notifications": {
|
||||
"message": "Értesítések"
|
||||
},
|
||||
"dashboard.sidebar.label.organizations": {
|
||||
"message": "Szervezetek"
|
||||
},
|
||||
"dashboard.sidebar.label.overview": {
|
||||
"message": "Áttekintés"
|
||||
},
|
||||
"dashboard.sidebar.label.projects": {
|
||||
"message": "Projektek"
|
||||
},
|
||||
"dashboard.sidebar.label.revenue": {
|
||||
"message": "Bevétel"
|
||||
},
|
||||
"dashboard.withdraw.completion.account": {
|
||||
"message": "Fiók"
|
||||
},
|
||||
@@ -1200,7 +1401,7 @@
|
||||
"message": "Add hozzá saját domainedet a szerveredhez, foglalj le akár 15 portot azokhoz a modokhoz, amelyeknek szükségük van rá, és még sok más."
|
||||
},
|
||||
"hosting-marketing.included.backups-included": {
|
||||
"message": "Tartalmaz biztonsági másolatokat"
|
||||
"message": "Tartalmaz biztonsági mentéseket"
|
||||
},
|
||||
"hosting-marketing.included.backups-included.description": {
|
||||
"message": "Minden szerverhez 15 biztonságosan tárolt, külső helyszínen tárolt biztonsági másolat tartozik."
|
||||
@@ -1248,7 +1449,7 @@
|
||||
"message": "Pontosan tudod, mire van szükséged?"
|
||||
},
|
||||
"hosting-marketing.medal.info": {
|
||||
"message": "Próbáld ki 5 napig ingyen egy <orange>3 GB-os szervert</orange>, amelyet a <orange>Medal</orange> biztosít"
|
||||
"message": "Próbálj ki 5 napig ingyen egy <orange>3 GB-os szervert</orange>, amelyet a <orange>Medal</orange> biztosít"
|
||||
},
|
||||
"hosting-marketing.medal.learn-more": {
|
||||
"message": "További információk"
|
||||
@@ -1461,7 +1662,7 @@
|
||||
"message": "Új gyűjtemény"
|
||||
},
|
||||
"layout.action.new-organization": {
|
||||
"message": "Új Szervezet"
|
||||
"message": "Új szervezet"
|
||||
},
|
||||
"layout.action.new-project": {
|
||||
"message": "Új projekt"
|
||||
@@ -2979,7 +3180,7 @@
|
||||
"message": "Kétlépcsős hitelesítés"
|
||||
},
|
||||
"settings.account.two-factor.backup.intro": {
|
||||
"message": "Töltse le ezeket a biztonsági kódokat, és őrizze meg őket biztonságos helyen. Ha valaha is elveszítené a hozzáférését az eszközéhez, ezeket a kódokat használhatja a kétlépcsős hitelesítés kódja helyett! Ezeket a kódokat ugyanúgy óvnia kell, mint a jelszavát."
|
||||
"message": "Töltsd le ezeket a biztonsági kódokat, és őrizd meg őket biztonságos helyen. Ha valaha is elveszítenéd a hozzáférésedet az eszközödhöz, ezeket a kódokat használhatod a kétlépcsős hitelesítés kódja helyett! Ezeket a kódokat ugyanúgy óvnod kell, mint a jelszavadat."
|
||||
},
|
||||
"settings.account.two-factor.backup.single-use": {
|
||||
"message": "A biztonsági kódok csak egyszer használhatók fel."
|
||||
|
||||
@@ -342,7 +342,7 @@
|
||||
"message": "Creando un''account, hai accettato i <terms-link>termini d''utilizzo</terms-link> e l''<privacy-policy-link>informativa sulla privacy</privacy-policy-link> di Modrinth."
|
||||
},
|
||||
"auth.welcome.long-title": {
|
||||
"message": "Ti diamo il benvenuto su Modrinth!"
|
||||
"message": "Modrinth ti dà il benvenuto!"
|
||||
},
|
||||
"auth.welcome.title": {
|
||||
"message": "Ciao"
|
||||
@@ -924,7 +924,7 @@
|
||||
"message": "Puoi modificare più progetti contemporaneamente selezionandoli qui sotto."
|
||||
},
|
||||
"dashboard.projects.bulk-edit.server-disabled": {
|
||||
"message": "Impossibile modificare più server contemporaneamente"
|
||||
"message": "Non puoi modificare più server contemporaneamente"
|
||||
},
|
||||
"dashboard.projects.empty": {
|
||||
"message": "Non hai ancora alcun progetto. Clicca sul pulsante verde in alto per iniziare."
|
||||
@@ -1125,7 +1125,7 @@
|
||||
"message": "Organizzazioni"
|
||||
},
|
||||
"dashboard.sidebar.label.overview": {
|
||||
"message": "Riepilogo"
|
||||
"message": "Panoramica"
|
||||
},
|
||||
"dashboard.sidebar.label.projects": {
|
||||
"message": "Progetti"
|
||||
|
||||
@@ -554,6 +554,9 @@
|
||||
"dashboard.affiliate-links.create.button": {
|
||||
"message": "アフィリエイトリンクを作成"
|
||||
},
|
||||
"dashboard.affiliate-links.empty.no-codes": {
|
||||
"message": "アフィリエイトコードが見つかりませんでした"
|
||||
},
|
||||
"dashboard.affiliate-links.error.title": {
|
||||
"message": "アフィリエイトリンクの読み込みエラー"
|
||||
},
|
||||
@@ -572,6 +575,15 @@
|
||||
"dashboard.affiliate-links.search": {
|
||||
"message": "アフィリエイトリンクを検索…"
|
||||
},
|
||||
"dashboard.analytics.from-projects": {
|
||||
"message": "{count} 件のプロジェクトから"
|
||||
},
|
||||
"dashboard.analytics.total-downloads": {
|
||||
"message": "総ダウンロード数"
|
||||
},
|
||||
"dashboard.analytics.total-followers": {
|
||||
"message": "総フォロワー数"
|
||||
},
|
||||
"dashboard.collections.button.create-new": {
|
||||
"message": "新規作成"
|
||||
},
|
||||
@@ -596,6 +608,18 @@
|
||||
"dashboard.collections.long-title": {
|
||||
"message": "コレクション"
|
||||
},
|
||||
"dashboard.collections.placeholder.search": {
|
||||
"message": "コレクションを検索"
|
||||
},
|
||||
"dashboard.collections.sort.name-ascending": {
|
||||
"message": "名前順(昇順)"
|
||||
},
|
||||
"dashboard.collections.sort.recently-created": {
|
||||
"message": "作成順(新しい順)"
|
||||
},
|
||||
"dashboard.collections.sort.recently-updated": {
|
||||
"message": "更新順(新しい順)"
|
||||
},
|
||||
"dashboard.creator-tax-form-modal.confirmation.download-button": {
|
||||
"message": "{formType} をダウンロード"
|
||||
},
|
||||
@@ -848,6 +872,168 @@
|
||||
"dashboard.creator-withdraw-modal.withdraw-limit-used": {
|
||||
"message": "出金限度額の <b>{withdrawLimit}</b> に達しました。これ以上の額を出金するには、納税申告書類を記入する必要があります。"
|
||||
},
|
||||
"dashboard.head-title": {
|
||||
"message": "ダッシュボード"
|
||||
},
|
||||
"dashboard.notifications.button.mark-all-as-read": {
|
||||
"message": "全て既読にする"
|
||||
},
|
||||
"dashboard.notifications.button.view-history": {
|
||||
"message": "履歴を表示"
|
||||
},
|
||||
"dashboard.notifications.empty.no-unread": {
|
||||
"message": "未読の通知はありません"
|
||||
},
|
||||
"dashboard.notifications.error.loading": {
|
||||
"message": "通知の読み込み中にエラーが発生しました:"
|
||||
},
|
||||
"dashboard.notifications.history.label": {
|
||||
"message": "履歴"
|
||||
},
|
||||
"dashboard.notifications.history.title": {
|
||||
"message": "通知履歴"
|
||||
},
|
||||
"dashboard.notifications.link.see-all": {
|
||||
"message": "すべて見る"
|
||||
},
|
||||
"dashboard.notifications.link.view-history": {
|
||||
"message": "通知履歴を表示"
|
||||
},
|
||||
"dashboard.notifications.link.view-more": {
|
||||
"message": "{extraNotifs} 件の通知をさらに表示"
|
||||
},
|
||||
"dashboard.notifications.loading": {
|
||||
"message": "通知を読み込み中..."
|
||||
},
|
||||
"dashboard.organizations.button.create": {
|
||||
"message": "組織を作成"
|
||||
},
|
||||
"dashboard.organizations.empty.cta": {
|
||||
"message": "組織を作成しましょう!"
|
||||
},
|
||||
"dashboard.organizations.error.fetch": {
|
||||
"message": "組織の取得に失敗しました"
|
||||
},
|
||||
"dashboard.organizations.member-count": {
|
||||
"message": "{count} 人のメンバー"
|
||||
},
|
||||
"dashboard.organizations.title": {
|
||||
"message": "組織"
|
||||
},
|
||||
"dashboard.projects.bulk-edit-hint": {
|
||||
"message": "以下で選択することで、複数のプロジェクトを一括で編集できます"
|
||||
},
|
||||
"dashboard.projects.bulk-edit.server-disabled": {
|
||||
"message": "サーバープロジェクトでは一括編集を利用できません"
|
||||
},
|
||||
"dashboard.projects.empty": {
|
||||
"message": "まだプロジェクトがありません。上にある緑のボタンを押して始めましょう"
|
||||
},
|
||||
"dashboard.projects.head-title": {
|
||||
"message": "プロジェクト"
|
||||
},
|
||||
"dashboard.projects.links.and-more": {
|
||||
"message": "さらに {count} 件..."
|
||||
},
|
||||
"dashboard.projects.links.button.clear-link": {
|
||||
"message": "リンクを削除"
|
||||
},
|
||||
"dashboard.projects.links.button.edit": {
|
||||
"message": "リンクを編集"
|
||||
},
|
||||
"dashboard.projects.links.changes-applied": {
|
||||
"message": "変更は <strong>{count}</strong> 件のプロジェクトに適用されます"
|
||||
},
|
||||
"dashboard.projects.links.description": {
|
||||
"message": "下記に入力したリンクは、選択した各プロジェクトに上書きされます。空欄のままにしたリンクは無視されます。ゴミ箱ボタンを使用すると、選択したすべてのプロジェクトからリンクを削除できます。"
|
||||
},
|
||||
"dashboard.projects.links.discord-invite.description": {
|
||||
"message": "Discordサーバーへの招待リンク"
|
||||
},
|
||||
"dashboard.projects.links.discord-invite.label": {
|
||||
"message": "Discordの招待"
|
||||
},
|
||||
"dashboard.projects.links.issue-tracker.description": {
|
||||
"message": "ユーザーがプロジェクトに関するバグ、問題、懸念事項を報告できる場所"
|
||||
},
|
||||
"dashboard.projects.links.issue-tracker.label": {
|
||||
"message": "問題の報告と追跡"
|
||||
},
|
||||
"dashboard.projects.links.placeholder.cleared": {
|
||||
"message": "現在のリンクは削除されます"
|
||||
},
|
||||
"dashboard.projects.links.placeholder.valid-discord-url": {
|
||||
"message": "有効なDiscordの招待URLを入力してください"
|
||||
},
|
||||
"dashboard.projects.links.placeholder.valid-url": {
|
||||
"message": "有効なURLを入力してください"
|
||||
},
|
||||
"dashboard.projects.links.show-all-projects": {
|
||||
"message": "全てのプロジェクトを表示"
|
||||
},
|
||||
"dashboard.projects.links.source-code.description": {
|
||||
"message": "プロジェクトのソースコードを含むページやリポジトリ"
|
||||
},
|
||||
"dashboard.projects.links.source-code.label": {
|
||||
"message": "ソースコード"
|
||||
},
|
||||
"dashboard.projects.links.wiki-page.description": {
|
||||
"message": "プロジェクトに関する情報、ドキュメント、ヘルプが掲載されているページ"
|
||||
},
|
||||
"dashboard.projects.links.wiki-page.label": {
|
||||
"message": "ウィキページ"
|
||||
},
|
||||
"dashboard.projects.notification.bulk-edit-success": {
|
||||
"message": "選択されたプロジェクトのリンクを一括編集しました"
|
||||
},
|
||||
"dashboard.projects.project.icon-alt": {
|
||||
"message": "{title} のアイコン"
|
||||
},
|
||||
"dashboard.projects.project.moderator-message-aria": {
|
||||
"message": "プロジェクトにモデレーターからのメッセージがあります。プロジェクトの詳細をご覧ください。"
|
||||
},
|
||||
"dashboard.projects.project.review-environment-metadata": {
|
||||
"message": "環境メタデータを確認してください"
|
||||
},
|
||||
"dashboard.projects.sort.ascending": {
|
||||
"message": "昇順"
|
||||
},
|
||||
"dashboard.projects.sort.descending": {
|
||||
"message": "降順"
|
||||
},
|
||||
"dashboard.projects.sort.option.name": {
|
||||
"message": "名前"
|
||||
},
|
||||
"dashboard.projects.sort.option.status": {
|
||||
"message": "ステータス"
|
||||
},
|
||||
"dashboard.projects.sort.option.type": {
|
||||
"message": "種類"
|
||||
},
|
||||
"dashboard.projects.table.icon": {
|
||||
"message": "アイコン"
|
||||
},
|
||||
"dashboard.projects.table.id": {
|
||||
"message": "ID"
|
||||
},
|
||||
"dashboard.projects.table.name": {
|
||||
"message": "名前"
|
||||
},
|
||||
"dashboard.projects.table.status": {
|
||||
"message": "ステータス"
|
||||
},
|
||||
"dashboard.projects.table.type": {
|
||||
"message": "種類"
|
||||
},
|
||||
"dashboard.report.title": {
|
||||
"message": "通報 {id}"
|
||||
},
|
||||
"dashboard.reports.active-title": {
|
||||
"message": "アクティブな通報"
|
||||
},
|
||||
"dashboard.reports.title": {
|
||||
"message": "通報"
|
||||
},
|
||||
"dashboard.revenue.available-now": {
|
||||
"message": "現在出金可能"
|
||||
},
|
||||
@@ -884,6 +1070,9 @@
|
||||
"dashboard.revenue.transactions.btn.download-csv": {
|
||||
"message": "CSVでダウンロード"
|
||||
},
|
||||
"dashboard.revenue.transactions.head-title": {
|
||||
"message": "取引履歴"
|
||||
},
|
||||
"dashboard.revenue.transactions.header": {
|
||||
"message": "取引"
|
||||
},
|
||||
@@ -893,9 +1082,18 @@
|
||||
"dashboard.revenue.transactions.none.desc": {
|
||||
"message": "支払いおよび出金の履歴がここに表示されます。"
|
||||
},
|
||||
"dashboard.revenue.transactions.period.last-month": {
|
||||
"message": "先月"
|
||||
},
|
||||
"dashboard.revenue.transactions.period.this-month": {
|
||||
"message": "今月"
|
||||
},
|
||||
"dashboard.revenue.transactions.see-all": {
|
||||
"message": "すべて見る"
|
||||
},
|
||||
"dashboard.revenue.transactions.year.all": {
|
||||
"message": "全ての年"
|
||||
},
|
||||
"dashboard.revenue.withdraw.blocked-tin-mismatch": {
|
||||
"message": "入力された納税者番号または社会保障番号が米国内国歳入庁の記録と一致しなかったため、出金が一時的にロックされています。サポートに連絡して、納税申告書類のリセットと再提出を行ってください。"
|
||||
},
|
||||
@@ -908,6 +1106,33 @@
|
||||
"dashboard.revenue.withdraw.header": {
|
||||
"message": "出金"
|
||||
},
|
||||
"dashboard.sidebar.label.activeReports": {
|
||||
"message": "アクティブな通報"
|
||||
},
|
||||
"dashboard.sidebar.label.analytics": {
|
||||
"message": "アナリティクス"
|
||||
},
|
||||
"dashboard.sidebar.label.creators": {
|
||||
"message": "クリエイター"
|
||||
},
|
||||
"dashboard.sidebar.label.dashboard": {
|
||||
"message": "ダッシュボード"
|
||||
},
|
||||
"dashboard.sidebar.label.notifications": {
|
||||
"message": "通知"
|
||||
},
|
||||
"dashboard.sidebar.label.organizations": {
|
||||
"message": "組織"
|
||||
},
|
||||
"dashboard.sidebar.label.overview": {
|
||||
"message": "概要"
|
||||
},
|
||||
"dashboard.sidebar.label.projects": {
|
||||
"message": "プロジェクト"
|
||||
},
|
||||
"dashboard.sidebar.label.revenue": {
|
||||
"message": "収益"
|
||||
},
|
||||
"dashboard.withdraw.completion.account": {
|
||||
"message": "アカウント"
|
||||
},
|
||||
@@ -3155,6 +3380,12 @@
|
||||
"settings.billing.interval.monthly": {
|
||||
"message": "月間"
|
||||
},
|
||||
"settings.billing.interval.quarter": {
|
||||
"message": "四半期"
|
||||
},
|
||||
"settings.billing.interval.quarterly.adjective": {
|
||||
"message": "3ヶ月ごと"
|
||||
},
|
||||
"settings.billing.interval.year": {
|
||||
"message": "年"
|
||||
},
|
||||
@@ -3269,6 +3500,9 @@
|
||||
"settings.billing.pyro.resubscribe.error.title": {
|
||||
"message": "再登録エラー"
|
||||
},
|
||||
"settings.billing.pyro.resubscribe.request-submitted.text": {
|
||||
"message": "サーバーが現在キャンセルされている場合、サーバーの設定に10~15分かかる場合があります"
|
||||
},
|
||||
"settings.billing.pyro.resubscribe.request-submitted.title": {
|
||||
"message": "再加入申請を受け付けました"
|
||||
},
|
||||
|
||||
@@ -554,6 +554,9 @@
|
||||
"dashboard.affiliate-links.create.button": {
|
||||
"message": "제휴 링크 생성"
|
||||
},
|
||||
"dashboard.affiliate-links.empty.no-codes": {
|
||||
"message": "제휴 코드가 없습니다."
|
||||
},
|
||||
"dashboard.affiliate-links.error.title": {
|
||||
"message": "제휴 링크 로드 중 오류 발생"
|
||||
},
|
||||
@@ -572,6 +575,15 @@
|
||||
"dashboard.affiliate-links.search": {
|
||||
"message": "제휴 링크 검색..."
|
||||
},
|
||||
"dashboard.analytics.from-projects": {
|
||||
"message": "프로젝트 {count}개"
|
||||
},
|
||||
"dashboard.analytics.total-downloads": {
|
||||
"message": "다운로드 수"
|
||||
},
|
||||
"dashboard.analytics.total-followers": {
|
||||
"message": "팔로워 수"
|
||||
},
|
||||
"dashboard.collections.button.create-new": {
|
||||
"message": "새로 만들기"
|
||||
},
|
||||
@@ -596,6 +608,18 @@
|
||||
"dashboard.collections.long-title": {
|
||||
"message": "내 컬렉션"
|
||||
},
|
||||
"dashboard.collections.placeholder.search": {
|
||||
"message": "컬렉션 검색..."
|
||||
},
|
||||
"dashboard.collections.sort.name-ascending": {
|
||||
"message": "이름 (A-Z)"
|
||||
},
|
||||
"dashboard.collections.sort.recently-created": {
|
||||
"message": "최근 생성"
|
||||
},
|
||||
"dashboard.collections.sort.recently-updated": {
|
||||
"message": "최근 업데이트"
|
||||
},
|
||||
"dashboard.creator-tax-form-modal.confirmation.download-button": {
|
||||
"message": "{formType} 다운로드"
|
||||
},
|
||||
@@ -848,6 +872,168 @@
|
||||
"dashboard.creator-withdraw-modal.withdraw-limit-used": {
|
||||
"message": "귀하의 <b>{withdrawLimit}</b> 출금 한도를 모두 사용하셨습니다. 추가 출금을 위해서는 세금 신고서를 작성하셔야 합니다."
|
||||
},
|
||||
"dashboard.head-title": {
|
||||
"message": "대시보드"
|
||||
},
|
||||
"dashboard.notifications.button.mark-all-as-read": {
|
||||
"message": "모두 읽음으로 표시"
|
||||
},
|
||||
"dashboard.notifications.button.view-history": {
|
||||
"message": "기록 보기"
|
||||
},
|
||||
"dashboard.notifications.empty.no-unread": {
|
||||
"message": "읽지 않은 알림이 없습니다."
|
||||
},
|
||||
"dashboard.notifications.error.loading": {
|
||||
"message": "알림 불러오기 오류:"
|
||||
},
|
||||
"dashboard.notifications.history.label": {
|
||||
"message": "기록"
|
||||
},
|
||||
"dashboard.notifications.history.title": {
|
||||
"message": "알림 기록"
|
||||
},
|
||||
"dashboard.notifications.link.see-all": {
|
||||
"message": "모두 보기"
|
||||
},
|
||||
"dashboard.notifications.link.view-history": {
|
||||
"message": "알림 기록 보기"
|
||||
},
|
||||
"dashboard.notifications.link.view-more": {
|
||||
"message": "알림 {extraNotifs}개 더 보기"
|
||||
},
|
||||
"dashboard.notifications.loading": {
|
||||
"message": "알림 불러오는 중..."
|
||||
},
|
||||
"dashboard.organizations.button.create": {
|
||||
"message": "조직 생성"
|
||||
},
|
||||
"dashboard.organizations.empty.cta": {
|
||||
"message": "조직을 만듭니다!"
|
||||
},
|
||||
"dashboard.organizations.error.fetch": {
|
||||
"message": "조직 정보 가져오기 실패"
|
||||
},
|
||||
"dashboard.organizations.member-count": {
|
||||
"message": "멤버 {count}명"
|
||||
},
|
||||
"dashboard.organizations.title": {
|
||||
"message": "조직"
|
||||
},
|
||||
"dashboard.projects.bulk-edit-hint": {
|
||||
"message": "아래에서 여러 프로젝트를 선택하면 한 번에 편집할 수 있습니다."
|
||||
},
|
||||
"dashboard.projects.bulk-edit.server-disabled": {
|
||||
"message": "서버 프로젝트는 일괄 편집을 지원하지 않습니다"
|
||||
},
|
||||
"dashboard.projects.empty": {
|
||||
"message": "아직 프로젝트가 없습니다. 시작하려면 위의 녹색 버튼을 클릭하세요."
|
||||
},
|
||||
"dashboard.projects.head-title": {
|
||||
"message": "프로젝트"
|
||||
},
|
||||
"dashboard.projects.links.and-more": {
|
||||
"message": "그 외 {count}개..."
|
||||
},
|
||||
"dashboard.projects.links.button.clear-link": {
|
||||
"message": "링크 지우기"
|
||||
},
|
||||
"dashboard.projects.links.button.edit": {
|
||||
"message": "링크 편집"
|
||||
},
|
||||
"dashboard.projects.links.changes-applied": {
|
||||
"message": "변경사항이 <strong>{count}개</strong>의 프로젝트에 적용됩니다."
|
||||
},
|
||||
"dashboard.projects.links.description": {
|
||||
"message": "아래 지정한 링크는 선택한 각 프로젝트에서 덮어쓰게 됩니다. 비워 둔 항목은 무시됩니다. 휴지통 버튼을 클릭하면 선택한 모든 프로젝트에서 링크를 삭제할 수 있습니다."
|
||||
},
|
||||
"dashboard.projects.links.discord-invite.description": {
|
||||
"message": "Discord 서버 초대 링크입니다."
|
||||
},
|
||||
"dashboard.projects.links.discord-invite.label": {
|
||||
"message": "Discord 초대"
|
||||
},
|
||||
"dashboard.projects.links.issue-tracker.description": {
|
||||
"message": "사용자들이 프로젝트와 관련된 버그, 문제점 및 우려 사항을 보고할 수 있는 공간입니다."
|
||||
},
|
||||
"dashboard.projects.links.issue-tracker.label": {
|
||||
"message": "이슈 트래커"
|
||||
},
|
||||
"dashboard.projects.links.placeholder.cleared": {
|
||||
"message": "기존 링크가 삭제됩니다"
|
||||
},
|
||||
"dashboard.projects.links.placeholder.valid-discord-url": {
|
||||
"message": "유효한 Discord 초대 URL 입력"
|
||||
},
|
||||
"dashboard.projects.links.placeholder.valid-url": {
|
||||
"message": "유효한 URL 입력"
|
||||
},
|
||||
"dashboard.projects.links.show-all-projects": {
|
||||
"message": "모든 프로젝트 보기"
|
||||
},
|
||||
"dashboard.projects.links.source-code.description": {
|
||||
"message": "프로젝트의 소스 코드가 포함된 페이지/리포지토리"
|
||||
},
|
||||
"dashboard.projects.links.source-code.label": {
|
||||
"message": "소스 코드"
|
||||
},
|
||||
"dashboard.projects.links.wiki-page.description": {
|
||||
"message": "프로젝트에 대한 정보, 문서 및 도움말을 제공하는 페이지입니다."
|
||||
},
|
||||
"dashboard.projects.links.wiki-page.label": {
|
||||
"message": "위키 페이지"
|
||||
},
|
||||
"dashboard.projects.notification.bulk-edit-success": {
|
||||
"message": "선택한 프로젝트의 링크를 일괄 편집했습니다."
|
||||
},
|
||||
"dashboard.projects.project.icon-alt": {
|
||||
"message": "{title}의 아이콘"
|
||||
},
|
||||
"dashboard.projects.project.moderator-message-aria": {
|
||||
"message": "이 프로젝트에 운영진의 안내문이 있습니다. 자세한 내용은 프로젝트를 확인해 주세요."
|
||||
},
|
||||
"dashboard.projects.project.review-environment-metadata": {
|
||||
"message": "환경 메타데이터를 검토해 주세요"
|
||||
},
|
||||
"dashboard.projects.sort.ascending": {
|
||||
"message": "오름차순"
|
||||
},
|
||||
"dashboard.projects.sort.descending": {
|
||||
"message": "내림차순"
|
||||
},
|
||||
"dashboard.projects.sort.option.name": {
|
||||
"message": "이름"
|
||||
},
|
||||
"dashboard.projects.sort.option.status": {
|
||||
"message": "상태"
|
||||
},
|
||||
"dashboard.projects.sort.option.type": {
|
||||
"message": "유형"
|
||||
},
|
||||
"dashboard.projects.table.icon": {
|
||||
"message": "아이콘"
|
||||
},
|
||||
"dashboard.projects.table.id": {
|
||||
"message": "ID"
|
||||
},
|
||||
"dashboard.projects.table.name": {
|
||||
"message": "이름"
|
||||
},
|
||||
"dashboard.projects.table.status": {
|
||||
"message": "상태"
|
||||
},
|
||||
"dashboard.projects.table.type": {
|
||||
"message": "유형"
|
||||
},
|
||||
"dashboard.report.title": {
|
||||
"message": "{id} 신고"
|
||||
},
|
||||
"dashboard.reports.active-title": {
|
||||
"message": "활성된 신고"
|
||||
},
|
||||
"dashboard.reports.title": {
|
||||
"message": "신고"
|
||||
},
|
||||
"dashboard.revenue.available-now": {
|
||||
"message": "지금 이용 가능"
|
||||
},
|
||||
@@ -884,6 +1070,9 @@
|
||||
"dashboard.revenue.transactions.btn.download-csv": {
|
||||
"message": "CSV로 다운로드"
|
||||
},
|
||||
"dashboard.revenue.transactions.head-title": {
|
||||
"message": "거래 내역"
|
||||
},
|
||||
"dashboard.revenue.transactions.header": {
|
||||
"message": "거래 기록"
|
||||
},
|
||||
@@ -893,9 +1082,18 @@
|
||||
"dashboard.revenue.transactions.none.desc": {
|
||||
"message": "귀하의 결제 및 출금 내역이 여기에 표시됩니다."
|
||||
},
|
||||
"dashboard.revenue.transactions.period.last-month": {
|
||||
"message": "지난달"
|
||||
},
|
||||
"dashboard.revenue.transactions.period.this-month": {
|
||||
"message": "이번 달"
|
||||
},
|
||||
"dashboard.revenue.transactions.see-all": {
|
||||
"message": "모두 보기"
|
||||
},
|
||||
"dashboard.revenue.transactions.year.all": {
|
||||
"message": "모든 연도"
|
||||
},
|
||||
"dashboard.revenue.withdraw.blocked-tin-mismatch": {
|
||||
"message": "귀하의 출금이 일시적으로 제한되었습니다. 귀하의 TIN 또는 SSN이 국세청(IRS) 기록과 일치하지 않았기 때문입니다. 세금 신고서를 다시 작성하고 재제출하려면 고객지원팀에 문의해 주세요."
|
||||
},
|
||||
@@ -908,6 +1106,33 @@
|
||||
"dashboard.revenue.withdraw.header": {
|
||||
"message": "출금"
|
||||
},
|
||||
"dashboard.sidebar.label.activeReports": {
|
||||
"message": "활성된 신고"
|
||||
},
|
||||
"dashboard.sidebar.label.analytics": {
|
||||
"message": "분석"
|
||||
},
|
||||
"dashboard.sidebar.label.creators": {
|
||||
"message": "크리에이터"
|
||||
},
|
||||
"dashboard.sidebar.label.dashboard": {
|
||||
"message": "대시보드"
|
||||
},
|
||||
"dashboard.sidebar.label.notifications": {
|
||||
"message": "알림"
|
||||
},
|
||||
"dashboard.sidebar.label.organizations": {
|
||||
"message": "조직"
|
||||
},
|
||||
"dashboard.sidebar.label.overview": {
|
||||
"message": "개요"
|
||||
},
|
||||
"dashboard.sidebar.label.projects": {
|
||||
"message": "프로젝트"
|
||||
},
|
||||
"dashboard.sidebar.label.revenue": {
|
||||
"message": "수익"
|
||||
},
|
||||
"dashboard.withdraw.completion.account": {
|
||||
"message": "계정"
|
||||
},
|
||||
@@ -2493,7 +2718,7 @@
|
||||
"message": "통계 읽기"
|
||||
},
|
||||
"scopes.category.analytics": {
|
||||
"message": "해석학"
|
||||
"message": "분석"
|
||||
},
|
||||
"scopes.category.collections": {
|
||||
"message": "컬렉션"
|
||||
@@ -3228,7 +3453,7 @@
|
||||
"message": "결제 수단 추가"
|
||||
},
|
||||
"settings.billing.payment_method.action.history": {
|
||||
"message": "과거 요금 보기"
|
||||
"message": "과거 결제 보기"
|
||||
},
|
||||
"settings.billing.payment_method.action.primary": {
|
||||
"message": "기본으로 설정"
|
||||
|
||||
@@ -848,6 +848,24 @@
|
||||
"dashboard.creator-withdraw-modal.withdraw-limit-used": {
|
||||
"message": "Anda telah menggunakan had pengeluaran anda sebanyak <b>{withdrawLimit}</b>. Anda mesti melengkapkan borang cukai untuk mengeluarkan lebih banyak wang."
|
||||
},
|
||||
"dashboard.projects.sort.option.name": {
|
||||
"message": "Nama"
|
||||
},
|
||||
"dashboard.projects.sort.option.type": {
|
||||
"message": "Jenis"
|
||||
},
|
||||
"dashboard.projects.table.icon": {
|
||||
"message": "Ikon"
|
||||
},
|
||||
"dashboard.projects.table.id": {
|
||||
"message": "ID"
|
||||
},
|
||||
"dashboard.projects.table.name": {
|
||||
"message": "Nama"
|
||||
},
|
||||
"dashboard.projects.table.type": {
|
||||
"message": "Jenis"
|
||||
},
|
||||
"dashboard.revenue.available-now": {
|
||||
"message": "Tersedia sekarang"
|
||||
},
|
||||
|
||||
@@ -936,7 +936,7 @@
|
||||
"message": "e {count} mais..."
|
||||
},
|
||||
"dashboard.projects.links.button.clear-link": {
|
||||
"message": "Apagar link"
|
||||
"message": "Limpar link"
|
||||
},
|
||||
"dashboard.projects.links.button.edit": {
|
||||
"message": "Editar links"
|
||||
|
||||
@@ -1269,7 +1269,7 @@
|
||||
"message": "Cria o teu servidor"
|
||||
},
|
||||
"hosting-marketing.hero.host-with-modrinth": {
|
||||
"message": "Aloja o teu próximo servidor com o Modrinth Hosting"
|
||||
"message": "Aloja o teu servidor com o Modrinth Hosting"
|
||||
},
|
||||
"hosting-marketing.hero.hosting-description": {
|
||||
"message": "O Modrinth Hosting é a maneira mais fácil de teres o teu próprio servidor de Minecraft: Java Edition. Instala e joga os teus mods e modpacks favoritos, tudo na plataforma Modrinth."
|
||||
|
||||
@@ -554,6 +554,9 @@
|
||||
"dashboard.affiliate-links.create.button": {
|
||||
"message": "Создать ссылку"
|
||||
},
|
||||
"dashboard.affiliate-links.empty.no-codes": {
|
||||
"message": "Партнёрские коды не найдены."
|
||||
},
|
||||
"dashboard.affiliate-links.error.title": {
|
||||
"message": "Ошибка загрузки партнёрских ссылок"
|
||||
},
|
||||
@@ -572,6 +575,15 @@
|
||||
"dashboard.affiliate-links.search": {
|
||||
"message": "Поиск ссылок..."
|
||||
},
|
||||
"dashboard.analytics.from-projects": {
|
||||
"message": "с {count, plural, one {# проекта} other {# проектов}}"
|
||||
},
|
||||
"dashboard.analytics.total-downloads": {
|
||||
"message": "Всего скачиваний"
|
||||
},
|
||||
"dashboard.analytics.total-followers": {
|
||||
"message": "Всего подписчиков"
|
||||
},
|
||||
"dashboard.collections.button.create-new": {
|
||||
"message": "Создать новую"
|
||||
},
|
||||
@@ -596,6 +608,18 @@
|
||||
"dashboard.collections.long-title": {
|
||||
"message": "Ваши коллекции"
|
||||
},
|
||||
"dashboard.collections.placeholder.search": {
|
||||
"message": "Поиск коллекций..."
|
||||
},
|
||||
"dashboard.collections.sort.name-ascending": {
|
||||
"message": "Название (А–Я)"
|
||||
},
|
||||
"dashboard.collections.sort.recently-created": {
|
||||
"message": "Дата создания"
|
||||
},
|
||||
"dashboard.collections.sort.recently-updated": {
|
||||
"message": "Дата обновления"
|
||||
},
|
||||
"dashboard.creator-tax-form-modal.confirmation.download-button": {
|
||||
"message": "Скачать {formType}"
|
||||
},
|
||||
@@ -678,7 +702,7 @@
|
||||
"message": "Поиск стран..."
|
||||
},
|
||||
"dashboard.creator-withdraw-modal.method-selection.error-text": {
|
||||
"message": "Не удалось получить список доступных способов вывода. Повторите попытку позже."
|
||||
"message": "Не удалось получить доступные способы вывода. Повторите попытку позже."
|
||||
},
|
||||
"dashboard.creator-withdraw-modal.method-selection.error-title": {
|
||||
"message": "Ошибка загрузки способов вывода"
|
||||
@@ -848,6 +872,168 @@
|
||||
"dashboard.creator-withdraw-modal.withdraw-limit-used": {
|
||||
"message": "Достигнут лимит вывода в <b>{withdrawLimit}</b>. Для снятия лимита заполните налоговую форму."
|
||||
},
|
||||
"dashboard.head-title": {
|
||||
"message": "Панель управления"
|
||||
},
|
||||
"dashboard.notifications.button.mark-all-as-read": {
|
||||
"message": "Отметить всё как прочитанное"
|
||||
},
|
||||
"dashboard.notifications.button.view-history": {
|
||||
"message": "Посмотреть историю"
|
||||
},
|
||||
"dashboard.notifications.empty.no-unread": {
|
||||
"message": "У вас нет непрочитанных уведомлений."
|
||||
},
|
||||
"dashboard.notifications.error.loading": {
|
||||
"message": "Ошибка загрузки уведомлений:"
|
||||
},
|
||||
"dashboard.notifications.history.label": {
|
||||
"message": "История"
|
||||
},
|
||||
"dashboard.notifications.history.title": {
|
||||
"message": "История уведомлений"
|
||||
},
|
||||
"dashboard.notifications.link.see-all": {
|
||||
"message": "Посмотреть все"
|
||||
},
|
||||
"dashboard.notifications.link.view-history": {
|
||||
"message": "История уведомлений"
|
||||
},
|
||||
"dashboard.notifications.link.view-more": {
|
||||
"message": "Ещё {extraNotifs, plural, one {# уведомление} few {# уведомления} other {# уведомлений}}"
|
||||
},
|
||||
"dashboard.notifications.loading": {
|
||||
"message": "Загрузка уведомлений..."
|
||||
},
|
||||
"dashboard.organizations.button.create": {
|
||||
"message": "Создать организацию"
|
||||
},
|
||||
"dashboard.organizations.empty.cta": {
|
||||
"message": "Создайте организацию!"
|
||||
},
|
||||
"dashboard.organizations.error.fetch": {
|
||||
"message": "Не удалось получить организации"
|
||||
},
|
||||
"dashboard.organizations.member-count": {
|
||||
"message": "{count, plural, one {# участник} few {# участника} other {# участников}}"
|
||||
},
|
||||
"dashboard.organizations.title": {
|
||||
"message": "Организации"
|
||||
},
|
||||
"dashboard.projects.bulk-edit-hint": {
|
||||
"message": "Выберите проекты ниже, чтобы настроить их вместе."
|
||||
},
|
||||
"dashboard.projects.bulk-edit.server-disabled": {
|
||||
"message": "Серверные проекты не поддерживают массовые операции"
|
||||
},
|
||||
"dashboard.projects.empty": {
|
||||
"message": "Вы не имеете никаких либо проектов. Нажмите зелёную кнопку сверху, чтобы начать."
|
||||
},
|
||||
"dashboard.projects.head-title": {
|
||||
"message": "Проекты"
|
||||
},
|
||||
"dashboard.projects.links.and-more": {
|
||||
"message": "и ещё {count}..."
|
||||
},
|
||||
"dashboard.projects.links.button.clear-link": {
|
||||
"message": "Отчистить ссылку"
|
||||
},
|
||||
"dashboard.projects.links.button.edit": {
|
||||
"message": "Изменить ссылки"
|
||||
},
|
||||
"dashboard.projects.links.changes-applied": {
|
||||
"message": "Изменения были приняты для <strong>{count}</strong> {count, plural, one {проекта} other {проектов}}."
|
||||
},
|
||||
"dashboard.projects.links.description": {
|
||||
"message": "Любые ссылки, указанные вами ниже, будут перезаписаны во всех заполненных полях. Поля, оставленные пустыми, будут проигнорированы. Вы можете удалить ссылку из всех выбранных проектов с помощью кнопки в виде корзины."
|
||||
},
|
||||
"dashboard.projects.links.discord-invite.description": {
|
||||
"message": "Ссылка приглашения на ваш Discord сервер."
|
||||
},
|
||||
"dashboard.projects.links.discord-invite.label": {
|
||||
"message": "Discord"
|
||||
},
|
||||
"dashboard.projects.links.issue-tracker.description": {
|
||||
"message": "Сайт на котором можно будет пожаловаться на баг, ошибку или написать отзыв об вашем проекте."
|
||||
},
|
||||
"dashboard.projects.links.issue-tracker.label": {
|
||||
"message": "Трекер проблем"
|
||||
},
|
||||
"dashboard.projects.links.placeholder.cleared": {
|
||||
"message": "Существующая ссылка будет отчищена"
|
||||
},
|
||||
"dashboard.projects.links.placeholder.valid-discord-url": {
|
||||
"message": "Введите ссылку приглашение на Discord"
|
||||
},
|
||||
"dashboard.projects.links.placeholder.valid-url": {
|
||||
"message": "Введите ссылку"
|
||||
},
|
||||
"dashboard.projects.links.show-all-projects": {
|
||||
"message": "Показывать все проекты"
|
||||
},
|
||||
"dashboard.projects.links.source-code.description": {
|
||||
"message": "Страница/репозиторий с открытым кодом проекта"
|
||||
},
|
||||
"dashboard.projects.links.source-code.label": {
|
||||
"message": "Исходный код"
|
||||
},
|
||||
"dashboard.projects.links.wiki-page.description": {
|
||||
"message": "Страница содержит информацию, документацию и помощь для проекта."
|
||||
},
|
||||
"dashboard.projects.links.wiki-page.label": {
|
||||
"message": "Вики"
|
||||
},
|
||||
"dashboard.projects.notification.bulk-edit-success": {
|
||||
"message": "Ссылки обновлены в выбранных проектах."
|
||||
},
|
||||
"dashboard.projects.project.icon-alt": {
|
||||
"message": "Иконка для {title}"
|
||||
},
|
||||
"dashboard.projects.project.moderator-message-aria": {
|
||||
"message": "Проект получил сообщение от модерации. Посмотрите страницу проекта чтобы увидеть больше."
|
||||
},
|
||||
"dashboard.projects.project.review-environment-metadata": {
|
||||
"message": "Пожалуйста, проверьте файл metadata"
|
||||
},
|
||||
"dashboard.projects.sort.ascending": {
|
||||
"message": "По возрастанию"
|
||||
},
|
||||
"dashboard.projects.sort.descending": {
|
||||
"message": "По убыванию"
|
||||
},
|
||||
"dashboard.projects.sort.option.name": {
|
||||
"message": "Название"
|
||||
},
|
||||
"dashboard.projects.sort.option.status": {
|
||||
"message": "Статус"
|
||||
},
|
||||
"dashboard.projects.sort.option.type": {
|
||||
"message": "Тип"
|
||||
},
|
||||
"dashboard.projects.table.icon": {
|
||||
"message": "Иконка"
|
||||
},
|
||||
"dashboard.projects.table.id": {
|
||||
"message": "ID"
|
||||
},
|
||||
"dashboard.projects.table.name": {
|
||||
"message": "Название"
|
||||
},
|
||||
"dashboard.projects.table.status": {
|
||||
"message": "Статус"
|
||||
},
|
||||
"dashboard.projects.table.type": {
|
||||
"message": "Тип"
|
||||
},
|
||||
"dashboard.report.title": {
|
||||
"message": "Жалоба {id}"
|
||||
},
|
||||
"dashboard.reports.active-title": {
|
||||
"message": "Активные жалобы"
|
||||
},
|
||||
"dashboard.reports.title": {
|
||||
"message": "Жалобы"
|
||||
},
|
||||
"dashboard.revenue.available-now": {
|
||||
"message": "Доступно"
|
||||
},
|
||||
@@ -884,6 +1070,9 @@
|
||||
"dashboard.revenue.transactions.btn.download-csv": {
|
||||
"message": "Скачать в формате CSV"
|
||||
},
|
||||
"dashboard.revenue.transactions.head-title": {
|
||||
"message": "История транзакций"
|
||||
},
|
||||
"dashboard.revenue.transactions.header": {
|
||||
"message": "Транзакции"
|
||||
},
|
||||
@@ -917,6 +1106,33 @@
|
||||
"dashboard.revenue.withdraw.header": {
|
||||
"message": "Вывод средств"
|
||||
},
|
||||
"dashboard.sidebar.label.activeReports": {
|
||||
"message": "Активные жалобы"
|
||||
},
|
||||
"dashboard.sidebar.label.analytics": {
|
||||
"message": "Аналитика"
|
||||
},
|
||||
"dashboard.sidebar.label.creators": {
|
||||
"message": "Авторам"
|
||||
},
|
||||
"dashboard.sidebar.label.dashboard": {
|
||||
"message": "Панель управления"
|
||||
},
|
||||
"dashboard.sidebar.label.notifications": {
|
||||
"message": "Уведомления"
|
||||
},
|
||||
"dashboard.sidebar.label.organizations": {
|
||||
"message": "Организации"
|
||||
},
|
||||
"dashboard.sidebar.label.overview": {
|
||||
"message": "Обзор"
|
||||
},
|
||||
"dashboard.sidebar.label.projects": {
|
||||
"message": "Проекты"
|
||||
},
|
||||
"dashboard.sidebar.label.revenue": {
|
||||
"message": "Доход"
|
||||
},
|
||||
"dashboard.withdraw.completion.account": {
|
||||
"message": "Счёт"
|
||||
},
|
||||
@@ -3408,7 +3624,7 @@
|
||||
"message": "Коллекция"
|
||||
},
|
||||
"settings.display.sidebar.advanced-rendering.description": {
|
||||
"message": "Включает продвинутые эффекты вроде размытия. Возможно снижение производительности на устройствах без аппаратного ускорения."
|
||||
"message": "Включает продвинутые эффекты вроде размытия, но может снизить производительность на устройствах без аппаратного ускорения."
|
||||
},
|
||||
"settings.display.sidebar.advanced-rendering.title": {
|
||||
"message": "Расширенные эффекты"
|
||||
@@ -3543,7 +3759,7 @@
|
||||
"message": "Аккаунт"
|
||||
},
|
||||
"settings.sidebar.label.developer": {
|
||||
"message": "Разработка"
|
||||
"message": "Разработчикам"
|
||||
},
|
||||
"settings.sidebar.label.display": {
|
||||
"message": "Отображение"
|
||||
|
||||
@@ -554,6 +554,9 @@
|
||||
"dashboard.affiliate-links.create.button": {
|
||||
"message": "Skapa affiliatelänk"
|
||||
},
|
||||
"dashboard.affiliate-links.empty.no-codes": {
|
||||
"message": "Inga affiliatekoder hittades."
|
||||
},
|
||||
"dashboard.affiliate-links.error.title": {
|
||||
"message": "Fel vid laddning av affiliatelänk"
|
||||
},
|
||||
@@ -572,6 +575,9 @@
|
||||
"dashboard.affiliate-links.search": {
|
||||
"message": "Sök affiliatelänkar..."
|
||||
},
|
||||
"dashboard.analytics.from-projects": {
|
||||
"message": "från {count} {count, plural, one {projekt} other {projekt}}"
|
||||
},
|
||||
"dashboard.analytics.total-downloads": {
|
||||
"message": "Antal nedladdningar"
|
||||
},
|
||||
@@ -860,6 +866,57 @@
|
||||
"dashboard.creator-withdraw-modal.withdraw-limit": {
|
||||
"message": "Uttagsgräns"
|
||||
},
|
||||
"dashboard.head-title": {
|
||||
"message": "Instrumentpanel"
|
||||
},
|
||||
"dashboard.notifications.button.mark-all-as-read": {
|
||||
"message": "Markera allt som läst"
|
||||
},
|
||||
"dashboard.notifications.button.view-history": {
|
||||
"message": "Visa historik"
|
||||
},
|
||||
"dashboard.notifications.empty.no-unread": {
|
||||
"message": "Du har inga olästa notifikationer."
|
||||
},
|
||||
"dashboard.notifications.error.loading": {
|
||||
"message": "Misslyckades att ladda notifikationer:"
|
||||
},
|
||||
"dashboard.notifications.history.label": {
|
||||
"message": "Historik"
|
||||
},
|
||||
"dashboard.notifications.history.title": {
|
||||
"message": "Notifikationshistorik"
|
||||
},
|
||||
"dashboard.notifications.link.see-all": {
|
||||
"message": "Visa alla"
|
||||
},
|
||||
"dashboard.notifications.link.view-history": {
|
||||
"message": "Visa notifikationshistorik"
|
||||
},
|
||||
"dashboard.notifications.link.view-more": {
|
||||
"message": "Visa {extraNotifs} ytterligare {extraNotifs, plural, one {notifikation} other {notifikationer}}"
|
||||
},
|
||||
"dashboard.notifications.loading": {
|
||||
"message": "Laddar notifikationer..."
|
||||
},
|
||||
"dashboard.organizations.button.create": {
|
||||
"message": "Skapa organization"
|
||||
},
|
||||
"dashboard.organizations.empty.cta": {
|
||||
"message": "Skapa en organisation!"
|
||||
},
|
||||
"dashboard.organizations.title": {
|
||||
"message": "Organisationer"
|
||||
},
|
||||
"dashboard.projects.head-title": {
|
||||
"message": "Projekt"
|
||||
},
|
||||
"dashboard.projects.links.button.clear-link": {
|
||||
"message": "Rensa länk"
|
||||
},
|
||||
"dashboard.projects.links.button.edit": {
|
||||
"message": "Ändra länkar"
|
||||
},
|
||||
"dashboard.revenue.available-now": {
|
||||
"message": "Tillgängligt nu"
|
||||
},
|
||||
|
||||
@@ -384,7 +384,7 @@
|
||||
"message": "Koleksiyon bulunamadı"
|
||||
},
|
||||
"collection.label.created-at": {
|
||||
"message": "{ago} tarihinde yaratıldı"
|
||||
"message": "{ago} tarihinde oluşturuldu"
|
||||
},
|
||||
"collection.label.curated-by": {
|
||||
"message": "Hazırlayan"
|
||||
@@ -554,6 +554,9 @@
|
||||
"dashboard.affiliate-links.create.button": {
|
||||
"message": "Ortaklık bağlantısı oluştur"
|
||||
},
|
||||
"dashboard.affiliate-links.empty.no-codes": {
|
||||
"message": "Yönlendirme kodu bulunamadı."
|
||||
},
|
||||
"dashboard.affiliate-links.error.title": {
|
||||
"message": "Ortaklık bağlantıları yüklenemedi"
|
||||
},
|
||||
@@ -572,6 +575,15 @@
|
||||
"dashboard.affiliate-links.search": {
|
||||
"message": "Ortaklık bağlantılarını ara..."
|
||||
},
|
||||
"dashboard.analytics.from-projects": {
|
||||
"message": "{count} {count, plural, one {project} other {projects}}'den"
|
||||
},
|
||||
"dashboard.analytics.total-downloads": {
|
||||
"message": "Toplam indirmeler"
|
||||
},
|
||||
"dashboard.analytics.total-followers": {
|
||||
"message": "Toplam takipçiler"
|
||||
},
|
||||
"dashboard.collections.button.create-new": {
|
||||
"message": "Yeni oluştur"
|
||||
},
|
||||
@@ -596,6 +608,18 @@
|
||||
"dashboard.collections.long-title": {
|
||||
"message": "Koleksiyonların"
|
||||
},
|
||||
"dashboard.collections.placeholder.search": {
|
||||
"message": "Koleksiyonlarda ara..."
|
||||
},
|
||||
"dashboard.collections.sort.name-ascending": {
|
||||
"message": "İsim (A-Z)"
|
||||
},
|
||||
"dashboard.collections.sort.recently-created": {
|
||||
"message": "Son oluşturulanlar"
|
||||
},
|
||||
"dashboard.collections.sort.recently-updated": {
|
||||
"message": "Son Güncellenen"
|
||||
},
|
||||
"dashboard.creator-tax-form-modal.confirmation.download-button": {
|
||||
"message": "{formType} İndirin"
|
||||
},
|
||||
@@ -848,6 +872,168 @@
|
||||
"dashboard.creator-withdraw-modal.withdraw-limit-used": {
|
||||
"message": "<b>{withdrawLimit}</b> çekme limitinizi kadarını kullandınız. Daha fazla çekmek için vergi formunu gerekir."
|
||||
},
|
||||
"dashboard.head-title": {
|
||||
"message": "Kontrol Paneli"
|
||||
},
|
||||
"dashboard.notifications.button.mark-all-as-read": {
|
||||
"message": "Hepsini okundu işaretle"
|
||||
},
|
||||
"dashboard.notifications.button.view-history": {
|
||||
"message": "Geçmişi görüntüle"
|
||||
},
|
||||
"dashboard.notifications.empty.no-unread": {
|
||||
"message": "Okunmamış bildiriminiz yok."
|
||||
},
|
||||
"dashboard.notifications.error.loading": {
|
||||
"message": "Bildirimler yüklenirken hata oluştu:"
|
||||
},
|
||||
"dashboard.notifications.history.label": {
|
||||
"message": "Geçmiş"
|
||||
},
|
||||
"dashboard.notifications.history.title": {
|
||||
"message": "Bildirim geçmişi"
|
||||
},
|
||||
"dashboard.notifications.link.see-all": {
|
||||
"message": "Hepsi"
|
||||
},
|
||||
"dashboard.notifications.link.view-history": {
|
||||
"message": "Bildirim geçmişini gör"
|
||||
},
|
||||
"dashboard.notifications.link.view-more": {
|
||||
"message": "{extraNotifs} daha fazla {extraNotifs, plural, one {notification} other {notifications}} görüntüle"
|
||||
},
|
||||
"dashboard.notifications.loading": {
|
||||
"message": "Bildirimler yükleniyor..."
|
||||
},
|
||||
"dashboard.organizations.button.create": {
|
||||
"message": "Organizasyon oluştur"
|
||||
},
|
||||
"dashboard.organizations.empty.cta": {
|
||||
"message": "Organizasyon oluştur!"
|
||||
},
|
||||
"dashboard.organizations.error.fetch": {
|
||||
"message": "Kuruluşlar alınamadı"
|
||||
},
|
||||
"dashboard.organizations.member-count": {
|
||||
"message": "{count} {count, plural, one {üye} other {üyeler}}"
|
||||
},
|
||||
"dashboard.organizations.title": {
|
||||
"message": "Organizasyonlar"
|
||||
},
|
||||
"dashboard.projects.bulk-edit-hint": {
|
||||
"message": "Aşağıdan seçerek birden fazla projeyi düzenleyebilirsiniz."
|
||||
},
|
||||
"dashboard.projects.bulk-edit.server-disabled": {
|
||||
"message": "Sunucu projeleri toplu düzenlemeyi desteklemez"
|
||||
},
|
||||
"dashboard.projects.empty": {
|
||||
"message": "Henüz projeniz yok. Aşağıdaki yeşil butona basarak başlayın."
|
||||
},
|
||||
"dashboard.projects.head-title": {
|
||||
"message": "Projeler"
|
||||
},
|
||||
"dashboard.projects.links.and-more": {
|
||||
"message": "ve {count} daha..."
|
||||
},
|
||||
"dashboard.projects.links.button.clear-link": {
|
||||
"message": "Bağlantıyı temizle"
|
||||
},
|
||||
"dashboard.projects.links.button.edit": {
|
||||
"message": "Bağlantıları düzenle"
|
||||
},
|
||||
"dashboard.projects.links.changes-applied": {
|
||||
"message": "Değişiklikler <strong>{count}</strong> {count, plural, one {proje} other {projeler}} için uygulanacaktır."
|
||||
},
|
||||
"dashboard.projects.links.description": {
|
||||
"message": "Aşağıda belirttiğiniz tüm bağlantılar, seçilen her projede üzerine yazılacaktır. Boş bıraktıklarınız dikkate alınmayacaktır. Çöp kutusu düğmesini kullanarak tüm seçili projelerden bir bağlantıyı silebilirsiniz."
|
||||
},
|
||||
"dashboard.projects.links.discord-invite.description": {
|
||||
"message": "Discord sunucunuzun davet bağlantısı."
|
||||
},
|
||||
"dashboard.projects.links.discord-invite.label": {
|
||||
"message": "Discord daveti"
|
||||
},
|
||||
"dashboard.projects.links.issue-tracker.description": {
|
||||
"message": "Kullanıcıların projenizle ilgili hataları, sorunları ve endişelerini bildirebilecekleri bir alan."
|
||||
},
|
||||
"dashboard.projects.links.issue-tracker.label": {
|
||||
"message": "Hata izleme"
|
||||
},
|
||||
"dashboard.projects.links.placeholder.cleared": {
|
||||
"message": "Mevcut bağlantı silinecek"
|
||||
},
|
||||
"dashboard.projects.links.placeholder.valid-discord-url": {
|
||||
"message": "Geçerli bir Discord davet URL'si girin"
|
||||
},
|
||||
"dashboard.projects.links.placeholder.valid-url": {
|
||||
"message": "Geçerli bir URL girin"
|
||||
},
|
||||
"dashboard.projects.links.show-all-projects": {
|
||||
"message": "Tüm projeleri göster"
|
||||
},
|
||||
"dashboard.projects.links.source-code.description": {
|
||||
"message": "Projenizin kaynak kodunu içeren bir sayfa/depo"
|
||||
},
|
||||
"dashboard.projects.links.source-code.label": {
|
||||
"message": "Kaynak kodu"
|
||||
},
|
||||
"dashboard.projects.links.wiki-page.description": {
|
||||
"message": "Projeyle ilgili bilgiler, belgeler ve yardım içeren bir sayfa."
|
||||
},
|
||||
"dashboard.projects.links.wiki-page.label": {
|
||||
"message": "Viki sayfası"
|
||||
},
|
||||
"dashboard.projects.notification.bulk-edit-success": {
|
||||
"message": "Seçilen projelerin bağlantılarını toplu olarak düzenlendi."
|
||||
},
|
||||
"dashboard.projects.project.icon-alt": {
|
||||
"message": "{title} için simge"
|
||||
},
|
||||
"dashboard.projects.project.moderator-message-aria": {
|
||||
"message": "Projede moderatörlerden bir mesaj var. Daha fazla bilgi için projeyi inceleyin."
|
||||
},
|
||||
"dashboard.projects.project.review-environment-metadata": {
|
||||
"message": "Lütfen ortam meta verilerini inceleyin"
|
||||
},
|
||||
"dashboard.projects.sort.ascending": {
|
||||
"message": "Artan"
|
||||
},
|
||||
"dashboard.projects.sort.descending": {
|
||||
"message": "Azalan sırada sırala"
|
||||
},
|
||||
"dashboard.projects.sort.option.name": {
|
||||
"message": "İsim"
|
||||
},
|
||||
"dashboard.projects.sort.option.status": {
|
||||
"message": "Durum"
|
||||
},
|
||||
"dashboard.projects.sort.option.type": {
|
||||
"message": "Tip"
|
||||
},
|
||||
"dashboard.projects.table.icon": {
|
||||
"message": "Simge"
|
||||
},
|
||||
"dashboard.projects.table.id": {
|
||||
"message": "Kimlik"
|
||||
},
|
||||
"dashboard.projects.table.name": {
|
||||
"message": "İsim"
|
||||
},
|
||||
"dashboard.projects.table.status": {
|
||||
"message": "Durum"
|
||||
},
|
||||
"dashboard.projects.table.type": {
|
||||
"message": "Tip"
|
||||
},
|
||||
"dashboard.report.title": {
|
||||
"message": "Rapor {id}"
|
||||
},
|
||||
"dashboard.reports.active-title": {
|
||||
"message": "Aktif raporlar"
|
||||
},
|
||||
"dashboard.reports.title": {
|
||||
"message": "Raporlar"
|
||||
},
|
||||
"dashboard.revenue.available-now": {
|
||||
"message": "Şu anda mevcut"
|
||||
},
|
||||
@@ -884,6 +1070,9 @@
|
||||
"dashboard.revenue.transactions.btn.download-csv": {
|
||||
"message": "CSV olarak indir"
|
||||
},
|
||||
"dashboard.revenue.transactions.head-title": {
|
||||
"message": "İşlem geçmişi"
|
||||
},
|
||||
"dashboard.revenue.transactions.header": {
|
||||
"message": "İşlemler"
|
||||
},
|
||||
@@ -893,9 +1082,18 @@
|
||||
"dashboard.revenue.transactions.none.desc": {
|
||||
"message": "Ödeme ve para çekimleriniz burada görünür."
|
||||
},
|
||||
"dashboard.revenue.transactions.period.last-month": {
|
||||
"message": "Geçen ay"
|
||||
},
|
||||
"dashboard.revenue.transactions.period.this-month": {
|
||||
"message": "Bu ay"
|
||||
},
|
||||
"dashboard.revenue.transactions.see-all": {
|
||||
"message": "Hepsini göster"
|
||||
},
|
||||
"dashboard.revenue.transactions.year.all": {
|
||||
"message": "Tüm zamanlar"
|
||||
},
|
||||
"dashboard.revenue.withdraw.blocked-tin-mismatch": {
|
||||
"message": "TIN veya SSN numaranız IRS kayıtlarıyla eşleşmediği için para çekme işlemleriniz geçici olarak engellendi. Vergi formunuzu sıfırlamak ve yeniden göndermek için destekle iletişime geçin."
|
||||
},
|
||||
@@ -908,6 +1106,33 @@
|
||||
"dashboard.revenue.withdraw.header": {
|
||||
"message": "Para Çek"
|
||||
},
|
||||
"dashboard.sidebar.label.activeReports": {
|
||||
"message": "Aktif raporlar"
|
||||
},
|
||||
"dashboard.sidebar.label.analytics": {
|
||||
"message": "Analitik"
|
||||
},
|
||||
"dashboard.sidebar.label.creators": {
|
||||
"message": "Yaratıcılar"
|
||||
},
|
||||
"dashboard.sidebar.label.dashboard": {
|
||||
"message": "Gösterge paneli"
|
||||
},
|
||||
"dashboard.sidebar.label.notifications": {
|
||||
"message": "Bildirimler"
|
||||
},
|
||||
"dashboard.sidebar.label.organizations": {
|
||||
"message": "Organizasyonlar"
|
||||
},
|
||||
"dashboard.sidebar.label.overview": {
|
||||
"message": "Genel Görünüm"
|
||||
},
|
||||
"dashboard.sidebar.label.projects": {
|
||||
"message": "Projeler"
|
||||
},
|
||||
"dashboard.sidebar.label.revenue": {
|
||||
"message": "Gelir"
|
||||
},
|
||||
"dashboard.withdraw.completion.account": {
|
||||
"message": "Hesap"
|
||||
},
|
||||
@@ -2954,9 +3179,15 @@
|
||||
"settings.account.security.password.title": {
|
||||
"message": "Parola"
|
||||
},
|
||||
"settings.account.security.providers.action.manage": {
|
||||
"message": "Sağlayıcıları yönet"
|
||||
},
|
||||
"settings.account.security.providers.description": {
|
||||
"message": "Hesabınıza giriş yöntemleri ekleyin veya kaldırın; GitHub, GitLab, Microsoft, Discord, Steam ve Google dahil."
|
||||
},
|
||||
"settings.account.security.providers.title": {
|
||||
"message": "Kimlik doğrulama sağlayıcılarını yönet"
|
||||
},
|
||||
"settings.account.security.title": {
|
||||
"message": "Hesap güvenliği"
|
||||
},
|
||||
@@ -2972,15 +3203,36 @@
|
||||
"settings.account.security.two-factor.title": {
|
||||
"message": "İki aşamalı doğrulama"
|
||||
},
|
||||
"settings.account.two-factor.backup.intro": {
|
||||
"message": "Bu yedek kodları indirip güvenli bir yerde saklayın. Cihazınıza erişiminizi kaybederseniz, 2FA kodu yerine bu kodları kullanabilirsiniz. Bu kodları şifreniz gibi korumalısınız."
|
||||
},
|
||||
"settings.account.two-factor.backup.single-use": {
|
||||
"message": "Yedek kodlar saece bir kere kullanılabilinir."
|
||||
},
|
||||
"settings.account.two-factor.error.incorrect-code": {
|
||||
"message": "Girilen kod yanlış!"
|
||||
},
|
||||
"settings.account.two-factor.field.code.description": {
|
||||
"message": "Devam etmek için iki aşamalı doğrulama kodunuzu girin."
|
||||
},
|
||||
"settings.account.two-factor.field.code.label": {
|
||||
"message": "Çift aşamalı kodu girin"
|
||||
},
|
||||
"settings.account.two-factor.field.code.placeholder": {
|
||||
"message": "Kodu girin..."
|
||||
},
|
||||
"settings.account.two-factor.setup.intro": {
|
||||
"message": "İki faktörlü kimlik doğrulama, oturum açmak için ikinci bir cihaza erişim gerektirerek hesabınızı güvenli tutar."
|
||||
},
|
||||
"settings.account.two-factor.setup.manual-secret": {
|
||||
"message": "QR kod taranmazsa, gizli anahtarı manuel olarak girebilirsiniz:"
|
||||
},
|
||||
"settings.account.two-factor.setup.scan": {
|
||||
"message": "QR kodunu <authy-link>Authy</authy-link>, <microsoft-authenticator-link>Microsoft Authenticator</microsoft-authenticator-link> veya başka bir 2FA uygulamasıyla tarayarak başlayın."
|
||||
},
|
||||
"settings.account.two-factor.verify.description": {
|
||||
"message": "Erişimi doğrulamak için kimlik doğrulayıcıdan tek kullanımlık kodu girin."
|
||||
},
|
||||
"settings.account.two-factor.verify.label": {
|
||||
"message": "Kodu onayla"
|
||||
},
|
||||
@@ -3065,6 +3317,9 @@
|
||||
"settings.applications.field.url.placeholder": {
|
||||
"message": "https://ornek.com"
|
||||
},
|
||||
"settings.applications.head-title": {
|
||||
"message": "Uygulamalar"
|
||||
},
|
||||
"settings.applications.modal.header": {
|
||||
"message": "Proje bilgileri"
|
||||
},
|
||||
@@ -3095,6 +3350,24 @@
|
||||
"settings.authorizations.revoke.action": {
|
||||
"message": "Kaldır"
|
||||
},
|
||||
"settings.authorizations.revoke.confirm.description": {
|
||||
"message": "Bu işlem, uygulamanın hesabınıza erişimini iptal edecektir. Daha sonra istediğiniz zaman yeniden yetkilendirebilirsiniz."
|
||||
},
|
||||
"settings.authorizations.revoke.confirm.title": {
|
||||
"message": "Bu uygulamanın yetkisini kaldırmak istediğinizden emin misiniz?"
|
||||
},
|
||||
"settings.billing.charges.description": {
|
||||
"message": "Modrinth hesabınıza geçmişte yapılan tüm ödemeleriniz burada listelenir:"
|
||||
},
|
||||
"settings.billing.charges.title": {
|
||||
"message": "Past charges"
|
||||
},
|
||||
"settings.billing.expires": {
|
||||
"message": "Son kullanma tarihi {date}"
|
||||
},
|
||||
"settings.billing.interval.monthly": {
|
||||
"message": "ay"
|
||||
},
|
||||
"settings.billing.interval.quarter": {
|
||||
"message": "çeyrek"
|
||||
},
|
||||
@@ -3117,7 +3390,10 @@
|
||||
"message": "Modrinth ve üreticilerini direkt olarak destekleyin"
|
||||
},
|
||||
"settings.billing.midas.benefits.title": {
|
||||
"message": "Yararlar"
|
||||
"message": "Ayrıcalıklar"
|
||||
},
|
||||
"settings.billing.midas.save-per-year": {
|
||||
"message": "Yıllık faturalandırmaya geçerek yılda {amount} tasarruf edin!"
|
||||
},
|
||||
"settings.billing.midas.upsell": {
|
||||
"message": "Modrinth Plus’a abone olun!"
|
||||
@@ -3176,6 +3452,12 @@
|
||||
"settings.billing.price.slash-interval": {
|
||||
"message": "/{interval}"
|
||||
},
|
||||
"settings.billing.pyro.cpu": {
|
||||
"message": "{shared} Paylaşımlı CPU ( {bursts} CPU’ya kadar ani yükselme)"
|
||||
},
|
||||
"settings.billing.pyro.linked-server.not-found": {
|
||||
"message": "Bu abonelik için bağlı bir sunucu bulunamadı. Bunun birkaç olası nedeni vardır. Sunucunuzu yeni satın aldıysanız bu normaldir. Sunucunuzun hazırlanması bir saate kadar sürebilir. Eğer sunucuyu bir süre önce satın aldıysanız, büyük ihtimalle askıya alınmıştır. Eğer bunu beklemiyorsanız, lütfen aşağıdaki bilgilerle birlikte Modrinth Destek ile iletişime geçin:"
|
||||
},
|
||||
"settings.billing.pyro.linked-server.server-id": {
|
||||
"message": "Sunucu Kimliği:{id}"
|
||||
},
|
||||
@@ -3188,6 +3470,18 @@
|
||||
"settings.billing.pyro.resubscribe.error.title": {
|
||||
"message": "Yeniden abone olurken hata"
|
||||
},
|
||||
"settings.billing.pyro.resubscribe.request-submitted.text": {
|
||||
"message": "Sunucu şu anda iptal durumundaysa, sunucunun kurulması 10-15 dakika sürebilir."
|
||||
},
|
||||
"settings.billing.pyro.resubscribe.request-submitted.title": {
|
||||
"message": "Yeniden abonelik talebi gönderildi"
|
||||
},
|
||||
"settings.billing.pyro.resubscribe.success.text": {
|
||||
"message": "Sunucu aboneliği başarıyla yeniden aktifleştirildi"
|
||||
},
|
||||
"settings.billing.pyro.status.failed": {
|
||||
"message": "Abonelik ödemen başarısız oldu. Lütfen ödeme yöntemini güncelle, ardından yeniden abonelik oluştur."
|
||||
},
|
||||
"settings.billing.pyro.status.processing": {
|
||||
"message": "Ödemeniz işleniyor. Sunucunuz ödeme tamamlandığında aktifleşecektir."
|
||||
},
|
||||
@@ -3203,6 +3497,15 @@
|
||||
"settings.billing.pyro_subscription.title": {
|
||||
"message": "Modrinth Sunucu Abonelikleri"
|
||||
},
|
||||
"settings.billing.renews": {
|
||||
"message": "{date} tarihinde yenilenir"
|
||||
},
|
||||
"settings.billing.resubscribe": {
|
||||
"message": "Yeniden abone ol"
|
||||
},
|
||||
"settings.billing.since": {
|
||||
"message": "{date} tarihinden beri"
|
||||
},
|
||||
"settings.billing.subscribe": {
|
||||
"message": "Abone ol"
|
||||
},
|
||||
@@ -3212,6 +3515,12 @@
|
||||
"settings.billing.subscription.title": {
|
||||
"message": "Abonelikler"
|
||||
},
|
||||
"settings.billing.switch.switching-to-interval": {
|
||||
"message": "{interval} planına geçiliyor"
|
||||
},
|
||||
"settings.billing.switch.to-interval": {
|
||||
"message": "{interval} planına geç"
|
||||
},
|
||||
"settings.billing.switch.tooltip.monthly-additional-per-year": {
|
||||
"message": "Aylık faturalandırma size yıllık ek olarak {amount} maliyet çıkaracaktır"
|
||||
},
|
||||
@@ -3254,6 +3563,9 @@
|
||||
"settings.display.project-list-layouts.mode.gallery": {
|
||||
"message": "Galeri"
|
||||
},
|
||||
"settings.display.project-list-layouts.mode.grid": {
|
||||
"message": "Izgara"
|
||||
},
|
||||
"settings.display.project-list-layouts.modpack": {
|
||||
"message": "Mod paketleri sayfası"
|
||||
},
|
||||
|
||||
@@ -873,7 +873,7 @@
|
||||
"message": "你已用尽<b>{withdrawLimit}</b>的提现额度。若需继续提现,请先完成税务表格填写。"
|
||||
},
|
||||
"dashboard.head-title": {
|
||||
"message": "数据面板"
|
||||
"message": "仪表盘"
|
||||
},
|
||||
"dashboard.notifications.button.mark-all-as-read": {
|
||||
"message": "标记全部已读"
|
||||
@@ -1113,10 +1113,10 @@
|
||||
"message": "分析"
|
||||
},
|
||||
"dashboard.sidebar.label.creators": {
|
||||
"message": "作者"
|
||||
"message": "创作者"
|
||||
},
|
||||
"dashboard.sidebar.label.dashboard": {
|
||||
"message": "数据面板"
|
||||
"message": "仪表盘"
|
||||
},
|
||||
"dashboard.sidebar.label.notifications": {
|
||||
"message": "通知"
|
||||
@@ -1374,7 +1374,7 @@
|
||||
"message": "Modrinth Hosting 服务器有多快?"
|
||||
},
|
||||
"hosting-marketing.faq.how-fast.answer.one": {
|
||||
"message": "Modrinth 代理服务器搭载了极为现代的高性能硬件,但要具体反映到你的服务器会有多快,还是很难预测的,因为实际性能受诸多因素影响,例如在你的服务器上运行的模组、数据包、插件,甚至用户行为模式都是影响的因素。"
|
||||
"message": "Modrinth Hosting 服务器搭载了极为现代的高性能硬件,但要具体反映到你的服务器会有多快,还是很难预测的,因为实际性能受诸多因素影响,例如在你的服务器上运行的模组、数据包、插件,甚至用户行为模式都是影响的因素。"
|
||||
},
|
||||
"hosting-marketing.faq.how-fast.answer.two": {
|
||||
"message": "大多数性能问题,往往是未优化的整合包、模组、数据包或插件导致服务器卡顿。由于我们的服务器配置非常高端,只要你针对服务器上运行的内容,选择合适的套餐方案,通常不会遇到太大问题。"
|
||||
@@ -1386,7 +1386,7 @@
|
||||
"message": "可以,你可以给你的服务器增加存储空间,而且无需额外费用。如果需要更多存储空间,请联系 Modrinth 支持团队。"
|
||||
},
|
||||
"hosting-marketing.faq.location": {
|
||||
"message": "Modrinth 代理的服务器位于哪里?我可以选择地区吗?"
|
||||
"message": "Modrinth Hosting 的服务器位于哪里?我可以选择地区吗?"
|
||||
},
|
||||
"hosting-marketing.faq.location.answer": {
|
||||
"message": "目前我们提供北美、欧洲和东南亚的服务器,供你在购买时选择。未来还会增加更多地区!如需更换服务器地区,请联系技术支持。"
|
||||
@@ -1416,7 +1416,7 @@
|
||||
"message": "你的下一个服务器,就交给 Modrinth Hosting"
|
||||
},
|
||||
"hosting-marketing.hero.hosting-description": {
|
||||
"message": "Modrinth 代理可让你以最便捷的方式,托管你专属的 Minecraft Java 版服务器。无缝安装并畅玩你心爱的模组和模组包,全在 Modrinth 平台搞定。"
|
||||
"message": "Modrinth Hosting 可让你以最便捷的方式,托管你专属的 Minecraft Java 版服务器。无缝安装并畅玩你心爱的模组和模组包,全在 Modrinth 平台搞定。"
|
||||
},
|
||||
"hosting-marketing.included.advanced-networking": {
|
||||
"message": "高级网络管理"
|
||||
@@ -1464,7 +1464,7 @@
|
||||
"message": "SFTP 访问"
|
||||
},
|
||||
"hosting-marketing.included.sftp-access.description": {
|
||||
"message": "通过 Modrinth 代理内置的 SFTP 功能,直接访问你的服务器文件。"
|
||||
"message": "通过 Modrinth Hosting 内置的 SFTP 功能,直接访问你的服务器文件。"
|
||||
},
|
||||
"hosting-marketing.included.with-your-server": {
|
||||
"message": "随服务器一并提供"
|
||||
@@ -1515,10 +1515,10 @@
|
||||
"message": "模组在哪,就在哪玩"
|
||||
},
|
||||
"hosting-marketing.why.where-mods-are.description": {
|
||||
"message": "Modrinth 代理将模组与整合包的安装流程无缝集成到你的服务器中。"
|
||||
"message": "Modrinth Hosting 将模组与整合包的安装流程无缝集成到你的服务器中。"
|
||||
},
|
||||
"hosting-marketing.why.why-modrinth-hosting": {
|
||||
"message": "为什么选择 Modrinth代理?"
|
||||
"message": "为什么选择 Modrinth Hosting?"
|
||||
},
|
||||
"hosting-marketing.why.your-favorite-mods": {
|
||||
"message": "你最喜欢的模组都在这里"
|
||||
@@ -3570,7 +3570,7 @@
|
||||
"message": "<strong>开发者模式</strong>已激活。该模式可让你查看 Modrinth 各种对象的内部 ID,对于使用 Modrinth API 的开发者会有所帮助。点击页面底部的 Modrinth 徽标 5 次可开关开发者模式。"
|
||||
},
|
||||
"settings.display.flags.description": {
|
||||
"message": "对于当前设备,开启或关闭特定功能。"
|
||||
"message": "为当前设备开启或关闭特定功能。"
|
||||
},
|
||||
"settings.display.flags.title": {
|
||||
"message": "功能开关"
|
||||
@@ -3585,7 +3585,7 @@
|
||||
"message": "数据包页面"
|
||||
},
|
||||
"settings.display.project-list-layouts.description": {
|
||||
"message": "对于当前设备,选择你喜欢的含项目列表页面的布局。"
|
||||
"message": "为当前设备选择你喜欢的含项目列表页面的布局。"
|
||||
},
|
||||
"settings.display.project-list-layouts.mod": {
|
||||
"message": "模组页面"
|
||||
@@ -3756,7 +3756,7 @@
|
||||
"message": "未知平台"
|
||||
},
|
||||
"settings.sidebar.label.account": {
|
||||
"message": "账户设置"
|
||||
"message": "账户"
|
||||
},
|
||||
"settings.sidebar.label.developer": {
|
||||
"message": "开发者设置"
|
||||
|
||||
@@ -342,7 +342,7 @@
|
||||
"message": "建立帳號即表示您已同意 Modrinth 的《<terms-link>使用條款</terms-link>》及《<privacy-policy-link>隱私權政策</privacy-policy-link>》。"
|
||||
},
|
||||
"auth.welcome.long-title": {
|
||||
"message": "歡迎來到 Modrinth!"
|
||||
"message": "歡迎使用 Modrinth!"
|
||||
},
|
||||
"auth.welcome.title": {
|
||||
"message": "歡迎"
|
||||
@@ -369,7 +369,7 @@
|
||||
"message": "這將永久刪除這個收藏。此動作無法復原。"
|
||||
},
|
||||
"collection.delete-modal.title": {
|
||||
"message": "你確定要刪除這個收藏嗎?"
|
||||
"message": "確定要刪除這個收藏嗎?"
|
||||
},
|
||||
"collection.description": {
|
||||
"message": "{description} — 前往 Modrinth 查看 {username} 的收藏「{name}」"
|
||||
@@ -555,7 +555,7 @@
|
||||
"message": "建立聯盟連結"
|
||||
},
|
||||
"dashboard.affiliate-links.empty.no-codes": {
|
||||
"message": "未找到推廣代碼。"
|
||||
"message": "找不到聯盟代碼。"
|
||||
},
|
||||
"dashboard.affiliate-links.error.title": {
|
||||
"message": "載入聯盟連結時發生錯誤"
|
||||
@@ -570,19 +570,19 @@
|
||||
"message": "撤銷"
|
||||
},
|
||||
"dashboard.affiliate-links.revoke-confirm.title": {
|
||||
"message": "你確定要撤銷「{title}」聯盟連結嗎?"
|
||||
"message": "確定要撤銷「{title}」聯盟連結嗎?"
|
||||
},
|
||||
"dashboard.affiliate-links.search": {
|
||||
"message": "搜尋聯盟連結..."
|
||||
},
|
||||
"dashboard.analytics.from-projects": {
|
||||
"message": "來自 {count} {count, plural, one {project} other {projects}}"
|
||||
"message": "來自 {count} 個專案"
|
||||
},
|
||||
"dashboard.analytics.total-downloads": {
|
||||
"message": "總下載量"
|
||||
},
|
||||
"dashboard.analytics.total-followers": {
|
||||
"message": "總關注者"
|
||||
"message": "總追蹤者人數"
|
||||
},
|
||||
"dashboard.collections.button.create-new": {
|
||||
"message": "新增"
|
||||
@@ -609,13 +609,13 @@
|
||||
"message": "你的收藏"
|
||||
},
|
||||
"dashboard.collections.placeholder.search": {
|
||||
"message": "搜尋收藏集…"
|
||||
"message": "搜尋收藏..."
|
||||
},
|
||||
"dashboard.collections.sort.name-ascending": {
|
||||
"message": "姓名(A-Z)"
|
||||
"message": "名稱(A-Z)"
|
||||
},
|
||||
"dashboard.collections.sort.recently-created": {
|
||||
"message": "最近創建"
|
||||
"message": "最近建立"
|
||||
},
|
||||
"dashboard.collections.sort.recently-updated": {
|
||||
"message": "最近更新"
|
||||
@@ -879,7 +879,7 @@
|
||||
"message": "全部標記為已讀"
|
||||
},
|
||||
"dashboard.notifications.button.view-history": {
|
||||
"message": "查看歷史記錄"
|
||||
"message": "查看紀錄"
|
||||
},
|
||||
"dashboard.notifications.empty.no-unread": {
|
||||
"message": "您沒有任何未讀通知。"
|
||||
@@ -888,22 +888,151 @@
|
||||
"message": "載入通知時發生錯誤:"
|
||||
},
|
||||
"dashboard.notifications.history.label": {
|
||||
"message": "歷史"
|
||||
"message": "紀錄"
|
||||
},
|
||||
"dashboard.notifications.history.title": {
|
||||
"message": "通知歷史記錄"
|
||||
"message": "通知紀錄"
|
||||
},
|
||||
"dashboard.notifications.link.see-all": {
|
||||
"message": "看全部"
|
||||
"message": "查看全部"
|
||||
},
|
||||
"dashboard.notifications.link.view-history": {
|
||||
"message": "查看通知記錄"
|
||||
"message": "查看通知紀錄"
|
||||
},
|
||||
"dashboard.notifications.link.view-more": {
|
||||
"message": "看更多 {extraNotifs} 條 {extraNotifs, plural, one {通知} other {通知}}"
|
||||
"message": "查看另外 {extraNotifs} 則通知"
|
||||
},
|
||||
"dashboard.notifications.loading": {
|
||||
"message": "正在載入通知…"
|
||||
"message": "正在載入通知..."
|
||||
},
|
||||
"dashboard.organizations.button.create": {
|
||||
"message": "建立組織"
|
||||
},
|
||||
"dashboard.organizations.empty.cta": {
|
||||
"message": "建立組織!"
|
||||
},
|
||||
"dashboard.organizations.error.fetch": {
|
||||
"message": "無法取得組織資訊"
|
||||
},
|
||||
"dashboard.organizations.member-count": {
|
||||
"message": "{count} {count, plural,one {成員} other {成員}}"
|
||||
},
|
||||
"dashboard.organizations.title": {
|
||||
"message": "組織"
|
||||
},
|
||||
"dashboard.projects.bulk-edit-hint": {
|
||||
"message": "您可以透過在下方選擇項目來同時編輯多個項目。"
|
||||
},
|
||||
"dashboard.projects.bulk-edit.server-disabled": {
|
||||
"message": "伺服器專案不支援大量編輯"
|
||||
},
|
||||
"dashboard.projects.empty": {
|
||||
"message": "您目前還沒有任何項目。點擊上方綠色按鈕開始。"
|
||||
},
|
||||
"dashboard.projects.head-title": {
|
||||
"message": "專案"
|
||||
},
|
||||
"dashboard.projects.links.and-more": {
|
||||
"message": "{count}還有更多…"
|
||||
},
|
||||
"dashboard.projects.links.button.clear-link": {
|
||||
"message": "清晰連結"
|
||||
},
|
||||
"dashboard.projects.links.button.edit": {
|
||||
"message": "編輯連結"
|
||||
},
|
||||
"dashboard.projects.links.changes-applied": {
|
||||
"message": "更改將應用於<strong>{count}</strong>{count, plural,one {專案} other {專案}}。"
|
||||
},
|
||||
"dashboard.projects.links.description": {
|
||||
"message": "您在下方指定的任何連結都將被覆蓋到每個選定的項目中。留空的連結將被忽略。您可以使用垃圾桶按鈕從所有選定的項目中清除連結。"
|
||||
},
|
||||
"dashboard.projects.links.discord-invite.description": {
|
||||
"message": "您的 Discord 伺服器邀請連結。"
|
||||
},
|
||||
"dashboard.projects.links.discord-invite.label": {
|
||||
"message": "Discord 邀請"
|
||||
},
|
||||
"dashboard.projects.links.issue-tracker.description": {
|
||||
"message": "供使用者回報關於你專案的錯誤、問題與疑慮之處。"
|
||||
},
|
||||
"dashboard.projects.links.issue-tracker.label": {
|
||||
"message": "問題追蹤器"
|
||||
},
|
||||
"dashboard.projects.links.placeholder.cleared": {
|
||||
"message": "將清除現有的連結"
|
||||
},
|
||||
"dashboard.projects.links.placeholder.valid-discord-url": {
|
||||
"message": "輸入有效的 Discord 邀請網址"
|
||||
},
|
||||
"dashboard.projects.links.placeholder.valid-url": {
|
||||
"message": "輸入有效的網址"
|
||||
},
|
||||
"dashboard.projects.links.show-all-projects": {
|
||||
"message": "顯示所有專案"
|
||||
},
|
||||
"dashboard.projects.links.source-code.description": {
|
||||
"message": "包含你專案原始碼的頁面或儲存庫"
|
||||
},
|
||||
"dashboard.projects.links.source-code.label": {
|
||||
"message": "原始碼"
|
||||
},
|
||||
"dashboard.projects.links.wiki-page.description": {
|
||||
"message": "包含專案資訊、文件及說明的頁面。"
|
||||
},
|
||||
"dashboard.projects.links.wiki-page.label": {
|
||||
"message": "Wiki 頁面"
|
||||
},
|
||||
"dashboard.projects.notification.bulk-edit-success": {
|
||||
"message": "已批次編輯所選專案的連結。"
|
||||
},
|
||||
"dashboard.projects.project.icon-alt": {
|
||||
"message": "{title} 的圖示"
|
||||
},
|
||||
"dashboard.projects.project.moderator-message-aria": {
|
||||
"message": "專案有一則來自管理員的訊息。請查看專案以了解更多資訊。"
|
||||
},
|
||||
"dashboard.projects.project.review-environment-metadata": {
|
||||
"message": "請審查環境詮釋資料"
|
||||
},
|
||||
"dashboard.projects.sort.ascending": {
|
||||
"message": "遞增"
|
||||
},
|
||||
"dashboard.projects.sort.descending": {
|
||||
"message": "遞減"
|
||||
},
|
||||
"dashboard.projects.sort.option.name": {
|
||||
"message": "名稱"
|
||||
},
|
||||
"dashboard.projects.sort.option.status": {
|
||||
"message": "狀態"
|
||||
},
|
||||
"dashboard.projects.sort.option.type": {
|
||||
"message": "類型"
|
||||
},
|
||||
"dashboard.projects.table.icon": {
|
||||
"message": "圖示"
|
||||
},
|
||||
"dashboard.projects.table.id": {
|
||||
"message": "ID"
|
||||
},
|
||||
"dashboard.projects.table.name": {
|
||||
"message": "名稱"
|
||||
},
|
||||
"dashboard.projects.table.status": {
|
||||
"message": "狀態"
|
||||
},
|
||||
"dashboard.projects.table.type": {
|
||||
"message": "類型"
|
||||
},
|
||||
"dashboard.report.title": {
|
||||
"message": "檢舉 {id}"
|
||||
},
|
||||
"dashboard.reports.active-title": {
|
||||
"message": "處理中的檢舉"
|
||||
},
|
||||
"dashboard.reports.title": {
|
||||
"message": "檢舉"
|
||||
},
|
||||
"dashboard.revenue.available-now": {
|
||||
"message": "可提領"
|
||||
@@ -941,6 +1070,9 @@
|
||||
"dashboard.revenue.transactions.btn.download-csv": {
|
||||
"message": "下載為 CSV 檔案"
|
||||
},
|
||||
"dashboard.revenue.transactions.head-title": {
|
||||
"message": "交易紀錄"
|
||||
},
|
||||
"dashboard.revenue.transactions.header": {
|
||||
"message": "交易"
|
||||
},
|
||||
@@ -950,9 +1082,18 @@
|
||||
"dashboard.revenue.transactions.none.desc": {
|
||||
"message": "你的付款和提領紀錄將顯示在這裡。"
|
||||
},
|
||||
"dashboard.revenue.transactions.period.last-month": {
|
||||
"message": "上個月"
|
||||
},
|
||||
"dashboard.revenue.transactions.period.this-month": {
|
||||
"message": "這個月"
|
||||
},
|
||||
"dashboard.revenue.transactions.see-all": {
|
||||
"message": "查看全部"
|
||||
},
|
||||
"dashboard.revenue.transactions.year.all": {
|
||||
"message": "所有年份"
|
||||
},
|
||||
"dashboard.revenue.withdraw.blocked-tin-mismatch": {
|
||||
"message": "由於你的稅務識別碼 (TIN) 或社會安全號碼 (SSN) 與美國國稅局 (IRS) 的紀錄不符,你的提領功能已暫時鎖定。請聯絡客服以重設並重新提交你的稅務表單。"
|
||||
},
|
||||
@@ -965,6 +1106,33 @@
|
||||
"dashboard.revenue.withdraw.header": {
|
||||
"message": "提領"
|
||||
},
|
||||
"dashboard.sidebar.label.activeReports": {
|
||||
"message": "處理中的檢舉"
|
||||
},
|
||||
"dashboard.sidebar.label.analytics": {
|
||||
"message": "數據分析"
|
||||
},
|
||||
"dashboard.sidebar.label.creators": {
|
||||
"message": "創作者"
|
||||
},
|
||||
"dashboard.sidebar.label.dashboard": {
|
||||
"message": "資訊主頁"
|
||||
},
|
||||
"dashboard.sidebar.label.notifications": {
|
||||
"message": "通知"
|
||||
},
|
||||
"dashboard.sidebar.label.organizations": {
|
||||
"message": "組織"
|
||||
},
|
||||
"dashboard.sidebar.label.overview": {
|
||||
"message": "總覽"
|
||||
},
|
||||
"dashboard.sidebar.label.projects": {
|
||||
"message": "專案"
|
||||
},
|
||||
"dashboard.sidebar.label.revenue": {
|
||||
"message": "收益"
|
||||
},
|
||||
"dashboard.withdraw.completion.account": {
|
||||
"message": "帳戶"
|
||||
},
|
||||
@@ -1539,7 +1707,7 @@
|
||||
"message": "技術審查"
|
||||
},
|
||||
"layout.avatar.alt": {
|
||||
"message": "你的頭貼"
|
||||
"message": "你的顯示圖片"
|
||||
},
|
||||
"layout.banner.account-action": {
|
||||
"message": "帳號需採取行動"
|
||||
@@ -2895,7 +3063,7 @@
|
||||
"message": "刪除這個帳號"
|
||||
},
|
||||
"settings.account.delete.confirm.title": {
|
||||
"message": "你確定要刪除你的帳號嗎?"
|
||||
"message": "確定要刪除你的帳號嗎?"
|
||||
},
|
||||
"settings.account.delete.section.action": {
|
||||
"message": "刪除帳號"
|
||||
@@ -3114,7 +3282,7 @@
|
||||
"message": "這將永久刪除這個應用程式並撤銷所有存取權杖。(永遠!)"
|
||||
},
|
||||
"settings.applications.delete.confirm.title": {
|
||||
"message": "你確定要刪除這個應用程式?"
|
||||
"message": "確定要刪除這個應用程式?"
|
||||
},
|
||||
"settings.applications.description.intro": {
|
||||
"message": "應用程式可用於在你的產品中驗證 Modrinth 使用者的身分。如需更多資訊,請參閱 <docs-link>Modrinth 的 API 文件</docs-link>。"
|
||||
@@ -3186,7 +3354,7 @@
|
||||
"message": "這將撤銷該應用程式存取你帳號的權限。你隨時可以於稍後重新授權。"
|
||||
},
|
||||
"settings.authorizations.revoke.confirm.title": {
|
||||
"message": "你確定要撤銷這個應用程式的授權嗎?"
|
||||
"message": "確定要撤銷這個應用程式的授權嗎?"
|
||||
},
|
||||
"settings.billing.charges.description": {
|
||||
"message": "你對 Modrinth 帳號的所有過往支付紀錄皆會列於這裡:"
|
||||
@@ -3261,10 +3429,10 @@
|
||||
"message": "取消訂閱"
|
||||
},
|
||||
"settings.billing.modal.cancel.description": {
|
||||
"message": "這將取消你的訂閱。你將保留你的權益,直到本期計費週期結束。"
|
||||
"message": "這會取消你的訂閱。你的福利將保留到本期計費週期結束。"
|
||||
},
|
||||
"settings.billing.modal.cancel.title": {
|
||||
"message": "你確定要取消訂閱嗎?"
|
||||
"message": "確定要取消訂閱嗎?"
|
||||
},
|
||||
"settings.billing.modal.delete.action": {
|
||||
"message": "移除這個付款方式"
|
||||
@@ -3273,7 +3441,7 @@
|
||||
"message": "這將會永遠移除這個付款方式(真的,再也回不去了)。"
|
||||
},
|
||||
"settings.billing.modal.delete.title": {
|
||||
"message": "你確定要移除這個付款方式嗎?"
|
||||
"message": "確定要移除這個付款方式嗎?"
|
||||
},
|
||||
"settings.billing.next": {
|
||||
"message": "下期:"
|
||||
@@ -3522,7 +3690,7 @@
|
||||
"message": "這將會永遠移除這個權杖(真的,再也回不去了)。"
|
||||
},
|
||||
"settings.pats.modal.delete.title": {
|
||||
"message": "你確定要刪除這個權杖嗎?"
|
||||
"message": "確定要刪除這個權杖嗎?"
|
||||
},
|
||||
"settings.pats.modal.edit.title": {
|
||||
"message": "編輯個人存取權杖"
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
import { isStaff } from '@modrinth/utils'
|
||||
|
||||
export default defineNuxtRouteMiddleware(async () => {
|
||||
const auth = await useAuth()
|
||||
|
||||
if (!auth.value.user || !isStaff(auth.value.user)) {
|
||||
throw createError({
|
||||
fatal: true,
|
||||
statusCode: 401,
|
||||
statusMessage: 'Unauthorized',
|
||||
})
|
||||
}
|
||||
})
|
||||
@@ -3,47 +3,49 @@
|
||||
<Teleport v-if="flags.projectBackground" to="#fixed-background-teleport">
|
||||
<ProjectBackgroundGradient :project="project" />
|
||||
</Teleport>
|
||||
<div v-if="route.name.startsWith('type-id-settings')" class="normal-page no-sidebar">
|
||||
<div class="normal-page__header">
|
||||
<div
|
||||
class="mb-4 flex flex-wrap items-center gap-x-2 gap-y-3 border-0 border-b-[1px] border-solid border-divider pb-4 text-lg font-semibold"
|
||||
>
|
||||
<nuxt-link
|
||||
:to="`/${project.project_type}/${project.slug ? project.slug : project.id}`"
|
||||
class="flex items-center gap-2 hover:underline hover:brightness-[--hover-brightness]"
|
||||
<template v-if="isSettings">
|
||||
<div v-if="canAccessSettings" class="normal-page no-sidebar">
|
||||
<div class="normal-page__header">
|
||||
<div
|
||||
class="mb-4 flex flex-wrap items-center gap-x-2 gap-y-3 border-0 border-b-[1px] border-solid border-divider pb-4 text-lg font-semibold"
|
||||
>
|
||||
<Avatar :src="project.icon_url" size="32px" />
|
||||
{{ project.title }}
|
||||
</nuxt-link>
|
||||
<ChevronRightIcon />
|
||||
<span class="flex grow font-extrabold text-contrast">{{
|
||||
formatMessage(messages.settingsTitle)
|
||||
}}</span>
|
||||
<div class="flex gap-2">
|
||||
<ButtonStyled>
|
||||
<nuxt-link to="/dashboard/projects"
|
||||
><ListIcon /> {{ formatMessage(messages.visitProjectsDashboard) }}
|
||||
</nuxt-link>
|
||||
</ButtonStyled>
|
||||
<nuxt-link
|
||||
:to="`/${project.project_type}/${project.slug ? project.slug : project.id}`"
|
||||
class="flex items-center gap-2 hover:underline hover:brightness-[--hover-brightness]"
|
||||
>
|
||||
<Avatar :src="project.icon_url" size="32px" />
|
||||
{{ project.title }}
|
||||
</nuxt-link>
|
||||
<ChevronRightIcon />
|
||||
<span class="flex grow font-extrabold text-contrast">{{
|
||||
formatMessage(messages.settingsTitle)
|
||||
}}</span>
|
||||
<div class="flex gap-2">
|
||||
<ButtonStyled>
|
||||
<nuxt-link to="/dashboard/projects"
|
||||
><ListIcon /> {{ formatMessage(messages.visitProjectsDashboard) }}
|
||||
</nuxt-link>
|
||||
</ButtonStyled>
|
||||
</div>
|
||||
</div>
|
||||
<ProjectMemberHeader
|
||||
v-if="currentMember && false"
|
||||
:project="project"
|
||||
:versions="versions"
|
||||
:current-member="currentMember"
|
||||
:is-settings="isSettings"
|
||||
:set-processing="setProcessing"
|
||||
:all-members="allMembers"
|
||||
:update-members="invalidateProject"
|
||||
:auth="auth"
|
||||
:tags="tags"
|
||||
/>
|
||||
</div>
|
||||
<div class="normal-page__content">
|
||||
<NuxtPage />
|
||||
</div>
|
||||
<ProjectMemberHeader
|
||||
v-if="currentMember && false"
|
||||
:project="project"
|
||||
:versions="versions"
|
||||
:current-member="currentMember"
|
||||
:is-settings="route.name.startsWith('type-id-settings')"
|
||||
:set-processing="setProcessing"
|
||||
:all-members="allMembers"
|
||||
:update-members="invalidateProject"
|
||||
:auth="auth"
|
||||
:tags="tags"
|
||||
/>
|
||||
</div>
|
||||
<div class="normal-page__content">
|
||||
<NuxtPage />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div v-else>
|
||||
<NewModal
|
||||
@@ -368,18 +370,21 @@
|
||||
<VersionSummary
|
||||
v-if="filteredRelease"
|
||||
:version="filteredRelease"
|
||||
:decorate-download-url="decorateModalDownloadUrl"
|
||||
@on-download="onDownload"
|
||||
@on-navigate="onVersionNavigate"
|
||||
/>
|
||||
<VersionSummary
|
||||
v-if="filteredBeta"
|
||||
:version="filteredBeta"
|
||||
:decorate-download-url="decorateModalDownloadUrl"
|
||||
@on-download="onDownload"
|
||||
@on-navigate="onVersionNavigate"
|
||||
/>
|
||||
<VersionSummary
|
||||
v-if="filteredAlpha"
|
||||
:version="filteredAlpha"
|
||||
:decorate-download-url="decorateModalDownloadUrl"
|
||||
@on-download="onDownload"
|
||||
@on-navigate="onVersionNavigate"
|
||||
/>
|
||||
@@ -811,7 +816,7 @@
|
||||
:project="project"
|
||||
:versions="versions"
|
||||
:current-member="currentMember"
|
||||
:is-settings="route.name.startsWith('type-id-settings')"
|
||||
:is-settings="isSettings"
|
||||
:route-name="route.name"
|
||||
:set-processing="setProcessing"
|
||||
:collapsed="collapsedChecklist"
|
||||
@@ -1080,6 +1085,7 @@ import {
|
||||
OpenInAppModal,
|
||||
OverflowMenu,
|
||||
PopoutMenu,
|
||||
PROJECT_DEP_MARKER_QUERY,
|
||||
ProjectBackgroundGradient,
|
||||
ProjectEnvironmentModal,
|
||||
ProjectHeader,
|
||||
@@ -1106,7 +1112,7 @@ import { useMutation, useQuery, useQueryClient } from '@tanstack/vue-query'
|
||||
import { useLocalStorage } from '@vueuse/core'
|
||||
import dayjs from 'dayjs'
|
||||
import { Tooltip } from 'floating-vue'
|
||||
import { nextTick, useTemplateRef, watch } from 'vue'
|
||||
import { nextTick, readonly, ref, useTemplateRef, watch } from 'vue'
|
||||
|
||||
import { navigateTo } from '#app'
|
||||
import Accordion from '~/components/ui/Accordion.vue'
|
||||
@@ -1135,6 +1141,7 @@ definePageMeta({
|
||||
|
||||
const data = useNuxtApp()
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const signInRouteObj = computed(() => getSignInRouteObj(route))
|
||||
const config = useRuntimeConfig()
|
||||
const moderationQueue = useModerationQueue()
|
||||
@@ -1144,6 +1151,23 @@ const { addNotification } = notifications
|
||||
const auth = await useAuth()
|
||||
const user = await useUser()
|
||||
|
||||
const { createProjectDownloadUrl } = useCdnDownloadContext()
|
||||
|
||||
const downloadReason = ref('standalone')
|
||||
|
||||
function absorbDepQuery() {
|
||||
if (route.query.dep === PROJECT_DEP_MARKER_QUERY.dep) {
|
||||
downloadReason.value = 'dependency'
|
||||
if (import.meta.client) {
|
||||
const newQuery = { ...route.query }
|
||||
delete newQuery.dep
|
||||
void router.replace({ path: route.path, query: newQuery, hash: route.hash })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
watch(() => route.query.dep, absorbDepQuery, { immediate: true })
|
||||
|
||||
const tags = useGeneratedState()
|
||||
const flags = useFeatureFlags()
|
||||
const cosmetics = useCosmetics()
|
||||
@@ -1208,6 +1232,14 @@ const currentPlatformText = computed(() => {
|
||||
return formatMessage(getTagMessage(currentPlatform.value, 'loader'))
|
||||
})
|
||||
|
||||
function decorateModalDownloadUrl(url) {
|
||||
return createProjectDownloadUrl(url, {
|
||||
reason: downloadReason.value,
|
||||
gameVersion: currentGameVersion.value ?? undefined,
|
||||
loader: currentPlatform.value ?? undefined,
|
||||
})
|
||||
}
|
||||
|
||||
const releaseVersions = computed(() => {
|
||||
const set = new Set()
|
||||
for (const gv of tags.value.gameVersions || []) {
|
||||
@@ -1713,15 +1745,27 @@ const serverRequiredContent = computed(() => {
|
||||
icon: content.project_icon,
|
||||
onclickName:
|
||||
content.project_id && content.project_id !== projectId.value
|
||||
? () => navigateTo(`/project/${content.project_id}`)
|
||||
? () => {
|
||||
navigateTo({
|
||||
path: `/project/${content.project_id}`,
|
||||
query: { ...PROJECT_DEP_MARKER_QUERY },
|
||||
})
|
||||
}
|
||||
: undefined,
|
||||
onclickVersion:
|
||||
content.project_id && content.project_id !== projectId.value
|
||||
? () =>
|
||||
navigateTo(`/project/${content.project_id}/version/${serverModpackVersion.value?.id}`)
|
||||
? () => {
|
||||
navigateTo({
|
||||
path: `/project/${content.project_id}/version/${serverModpackVersion.value?.id}`,
|
||||
query: { ...PROJECT_DEP_MARKER_QUERY },
|
||||
})
|
||||
}
|
||||
: undefined,
|
||||
onclickDownload: primaryFile?.url
|
||||
? () => navigateTo(primaryFile.url, { external: true })
|
||||
? () =>
|
||||
navigateTo(createProjectDownloadUrl(primaryFile.url, { reason: 'dependency' }), {
|
||||
external: true,
|
||||
})
|
||||
: undefined,
|
||||
showCustomModpackTooltip: content.project_id === projectId.value,
|
||||
}
|
||||
@@ -1826,6 +1870,8 @@ const { data: organizationRaw } = useQuery({
|
||||
// Return null when the project no longer belongs to an organization.
|
||||
const organization = computed(() => (projectRaw.value?.organization ? organizationRaw.value : null))
|
||||
|
||||
const isSettings = computed(() => route.name.startsWith('type-id-settings'))
|
||||
|
||||
// Transform versionsV3 to be same shape as versionsV2 for compatibility in project pages
|
||||
const versionsRaw = computed(() => {
|
||||
return (versionsV3.value ?? []).map((v) => {
|
||||
@@ -2262,11 +2308,27 @@ const currentMember = computed(() => {
|
||||
return val
|
||||
})
|
||||
|
||||
const canAccessSettings = computed(() => !!currentMember.value?.accepted)
|
||||
|
||||
const hasEditDetailsPermission = computed(() => {
|
||||
const EDIT_DETAILS = 1 << 2
|
||||
return (currentMember.value?.permissions & EDIT_DETAILS) === EDIT_DETAILS
|
||||
})
|
||||
|
||||
watch(
|
||||
[isSettings, allMembers, canAccessSettings],
|
||||
() => {
|
||||
if (isSettings.value && allMembers.value.length > 0 && !canAccessSettings.value) {
|
||||
showError({
|
||||
fatal: true,
|
||||
statusCode: 401,
|
||||
statusMessage: 'Unauthorized',
|
||||
})
|
||||
}
|
||||
},
|
||||
{ flush: 'sync', immediate: true },
|
||||
)
|
||||
|
||||
const projectTypeDisplay = computed(() => {
|
||||
if (!project.value) return ''
|
||||
return formatProjectType(
|
||||
@@ -2297,6 +2359,18 @@ const canCreateServerFrom = computed(() => {
|
||||
return project.value.project_type === 'modpack' && project.value.server_side !== 'unsupported'
|
||||
})
|
||||
|
||||
const createCanonicalUrl = () =>
|
||||
project.value ? `https://modrinth.com/project/${project.value.id}` : undefined
|
||||
|
||||
useHead({
|
||||
link: [
|
||||
{
|
||||
rel: 'canonical',
|
||||
href: createCanonicalUrl,
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
if (!route.name.startsWith('type-id-settings')) {
|
||||
useSeoMeta({
|
||||
title: () => title.value,
|
||||
@@ -2304,6 +2378,7 @@ if (!route.name.startsWith('type-id-settings')) {
|
||||
ogTitle: () => title.value,
|
||||
ogDescription: () => project.value?.description ?? '',
|
||||
ogImage: () => project.value?.icon_url ?? 'https://cdn.modrinth.com/placeholder.png',
|
||||
ogUrl: createCanonicalUrl,
|
||||
robots: () =>
|
||||
project.value?.status === 'approved' || project.value?.status === 'archived'
|
||||
? 'all'
|
||||
@@ -2312,6 +2387,7 @@ if (!route.name.startsWith('type-id-settings')) {
|
||||
} else {
|
||||
useSeoMeta({
|
||||
robots: 'noindex',
|
||||
ogUrl: createCanonicalUrl,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -2669,6 +2745,7 @@ provideProjectPageContext({
|
||||
// Lazy dependencies loading
|
||||
dependencies,
|
||||
dependenciesLoading: computed(() => dependenciesLoading.value),
|
||||
cdnDownloadReason: readonly(downloadReason),
|
||||
|
||||
// Invalidate all project queries (auto-refetches active ones)
|
||||
invalidate: invalidateProject,
|
||||
|
||||
@@ -56,7 +56,7 @@
|
||||
<ButtonStyled color="brand" type="transparent">
|
||||
<a
|
||||
class="ml-auto"
|
||||
:href="version.primaryFile?.url"
|
||||
:href="createDownloadUrl(version)"
|
||||
:title="`Download ${version.name}`"
|
||||
>
|
||||
<DownloadIcon aria-hidden="true" />
|
||||
@@ -98,7 +98,9 @@ import {
|
||||
import VersionFilterControl from '@modrinth/ui/src/components/version/VersionFilterControl.vue'
|
||||
import { renderHighlightedString } from '@modrinth/utils'
|
||||
import { useQuery } from '@tanstack/vue-query'
|
||||
import { onMounted } from 'vue'
|
||||
import { onMounted, watch } from 'vue'
|
||||
|
||||
const { createProjectDownloadUrl, updateVersionsFilterContext } = useCdnDownloadContext()
|
||||
|
||||
const formatDate = useFormatDateTime({
|
||||
month: 'short',
|
||||
@@ -106,7 +108,8 @@ const formatDate = useFormatDateTime({
|
||||
year: 'numeric',
|
||||
})
|
||||
|
||||
const { projectV2, versions, versionsLoading, loadVersions } = injectProjectPageContext()
|
||||
const { projectV2, versions, versionsLoading, loadVersions, cdnDownloadReason } =
|
||||
injectProjectPageContext()
|
||||
|
||||
// Load versions on mount (client-side)
|
||||
onMounted(() => {
|
||||
@@ -216,6 +219,23 @@ function updateQuery(newQueries) {
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
watch(
|
||||
() => [route.query.g, route.query.l],
|
||||
() => {
|
||||
updateVersionsFilterContext(
|
||||
queryAsStringArray(route.query.g),
|
||||
queryAsStringArray(route.query.l),
|
||||
)
|
||||
},
|
||||
{ immediate: true },
|
||||
)
|
||||
|
||||
function createDownloadUrl(version) {
|
||||
return createProjectDownloadUrl(getPrimaryFile(version).url, {
|
||||
reason: cdnDownloadReason.value,
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div>
|
||||
<div v-if="canAccess">
|
||||
<section class="universal-card">
|
||||
<h2>Project status</h2>
|
||||
<Badge :type="project.status" />
|
||||
@@ -107,7 +107,7 @@ import {
|
||||
injectProjectPageContext,
|
||||
} from '@modrinth/ui'
|
||||
import { useQuery, useQueryClient } from '@tanstack/vue-query'
|
||||
import { computed } from 'vue'
|
||||
import { computed, watch } from 'vue'
|
||||
|
||||
import ConversationThread from '~/components/ui/thread/ConversationThread.vue'
|
||||
import {
|
||||
@@ -122,6 +122,22 @@ import {
|
||||
const { addNotification } = injectNotificationManager()
|
||||
const { projectV2: project, currentMember, invalidate } = injectProjectPageContext()
|
||||
|
||||
const canAccess = computed(() => !!currentMember.value)
|
||||
|
||||
watch(
|
||||
[currentMember, project],
|
||||
() => {
|
||||
if (project.value && !canAccess.value) {
|
||||
showError({
|
||||
fatal: true,
|
||||
statusCode: 401,
|
||||
statusMessage: 'Unauthorized',
|
||||
})
|
||||
}
|
||||
},
|
||||
{ flush: 'sync', immediate: true },
|
||||
)
|
||||
|
||||
const auth = await useAuth()
|
||||
const client = injectModrinthClient()
|
||||
const queryClient = useQueryClient()
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
InfoIcon,
|
||||
LinkIcon,
|
||||
ServerIcon,
|
||||
SignatureIcon,
|
||||
TagsIcon,
|
||||
UsersIcon,
|
||||
VersionIcon,
|
||||
@@ -46,6 +47,12 @@ const navItems = computed(() => {
|
||||
projectV3.value?.project_types?.some((type) => ['mod', 'modpack'].includes(type)) &&
|
||||
isStaff(currentMember.value?.user)
|
||||
|
||||
const hasPermissionsPage = computed(
|
||||
() =>
|
||||
flags.value.modpackPermissionsPage &&
|
||||
projectV3.value?.project_types?.some((type) => ['modpack'].includes(type)),
|
||||
)
|
||||
|
||||
const items = [
|
||||
{
|
||||
link: `/${base}/settings`,
|
||||
@@ -75,6 +82,11 @@ const navItems = computed(() => {
|
||||
label: formatMessage(commonProjectSettingsMessages.description),
|
||||
icon: AlignLeftIcon,
|
||||
},
|
||||
hasPermissionsPage.value && {
|
||||
link: `/${base}/settings/permissions`,
|
||||
label: formatMessage(commonProjectSettingsMessages.permissions),
|
||||
icon: SignatureIcon,
|
||||
},
|
||||
!isServerProject.value && {
|
||||
link: `/${base}/settings/versions`,
|
||||
label: formatMessage(commonProjectSettingsMessages.versions),
|
||||
|
||||
@@ -146,7 +146,11 @@
|
||||
const input = e.target
|
||||
if (input.files?.length) {
|
||||
if (
|
||||
fileIsValid(input.files[0], { maxSize: 524288000, alertOnInvalid: true })
|
||||
fileIsValid(
|
||||
input.files[0],
|
||||
{ maxSize: 524288000, alertOnInvalid: true },
|
||||
formatBytes,
|
||||
)
|
||||
)
|
||||
showBannerPreview(Array.from(input.files))
|
||||
}
|
||||
@@ -379,6 +383,7 @@ import {
|
||||
StyledInput,
|
||||
Toggle,
|
||||
UnsavedChangesPopup,
|
||||
useFormatBytes,
|
||||
usePageLeaveSafety,
|
||||
} from '@modrinth/ui'
|
||||
import { fileIsValid, formatProjectStatus, formatProjectType } from '@modrinth/utils'
|
||||
@@ -405,6 +410,8 @@ const flags = useFeatureFlags()
|
||||
const tags = useGeneratedState()
|
||||
const router = useNativeRouter()
|
||||
|
||||
const formatBytes = useFormatBytes()
|
||||
|
||||
const name = ref(project.value.title)
|
||||
const slug = ref(project.value.slug)
|
||||
const summary = ref(project.value.description)
|
||||
|
||||
@@ -0,0 +1,184 @@
|
||||
<script setup lang="ts">
|
||||
import { RightArrowIcon, SearchIcon, SortAscIcon, SortDescIcon } from '@modrinth/assets'
|
||||
import {
|
||||
Admonition,
|
||||
ButtonStyled,
|
||||
Combobox,
|
||||
type ComboboxOption,
|
||||
commonMessages,
|
||||
defineMessages,
|
||||
EmptyState,
|
||||
IntlFormatted,
|
||||
StyledInput,
|
||||
useVIntl,
|
||||
} from '@modrinth/ui'
|
||||
import ExternalProjectPermissionsCard from '@modrinth/ui/src/components/external_files/ExternalProjectPermissionsCard.vue'
|
||||
import { ref } from 'vue'
|
||||
|
||||
const { formatMessage } = useVIntl()
|
||||
const flags = useFeatureFlags()
|
||||
|
||||
if (!flags.value.modpackPermissionsPage) {
|
||||
throw createError({
|
||||
fatal: true,
|
||||
statusCode: 404,
|
||||
})
|
||||
}
|
||||
|
||||
const externalFiles = ref([{}])
|
||||
const searchQuery = ref('')
|
||||
const currentSortType = ref('Oldest')
|
||||
|
||||
const sortTypes: ComboboxOption<string>[] = [
|
||||
{ value: 'Oldest', label: 'Oldest' },
|
||||
{ value: 'Newest', label: 'Newest' },
|
||||
]
|
||||
const messages = defineMessages({
|
||||
searchPlaceholder: {
|
||||
id: 'project.settings.permissions.search-placeholder',
|
||||
defaultMessage:
|
||||
'Search {count} {count, plural, one {external project} other {external projects}}...',
|
||||
},
|
||||
infoBannerTitle: {
|
||||
id: 'project.settings.permissions.info-banner.title',
|
||||
defaultMessage: 'Learn how attributions work',
|
||||
},
|
||||
infoBannerDescription: {
|
||||
id: 'project.settings.permissions.info-banner.description',
|
||||
defaultMessage: `If you include content that isn’t hosted on Modrinth, you need to let us know where it’s from and verify that you have permission to distribute the files. Check out <link>our guide</link> to learn about how to do this properly!`,
|
||||
},
|
||||
learnMore: {
|
||||
id: 'project.settings.permissions.learn-more',
|
||||
defaultMessage: 'Learn more',
|
||||
},
|
||||
emptyStateHeading: {
|
||||
id: 'project.settings.permissions.empty-state.heading',
|
||||
defaultMessage: `You're all set!`,
|
||||
},
|
||||
emptyStateDescription: {
|
||||
id: 'project.settings.permissions.empty-state.description',
|
||||
defaultMessage: `None of your versions contain external content, so you don't need to worry about obtaining permissions.`,
|
||||
},
|
||||
completedTitle: {
|
||||
id: 'project.settings.permissions.completed.title',
|
||||
defaultMessage: `Attributions completed!`,
|
||||
},
|
||||
completedDescription: {
|
||||
id: 'project.settings.permissions.completed.description',
|
||||
defaultMessage: 'All external content has attributions provided.',
|
||||
},
|
||||
failTitle: {
|
||||
id: 'project.settings.permissions.fail.title',
|
||||
defaultMessage: `Some content can't be included`,
|
||||
},
|
||||
failDescription: {
|
||||
id: 'project.settings.permissions.fail.description',
|
||||
defaultMessage: `You don't have permission to redistribute some of the external content you've added. In order to publish on Modrinth, remove the infringing content.`,
|
||||
},
|
||||
attentionNeededTitle: {
|
||||
id: 'project.settings.permissions.attention-needed.title',
|
||||
defaultMessage: `Unknown embedded content`,
|
||||
},
|
||||
attentionNeededDescriptionApproved: {
|
||||
id: 'project.settings.permissions.attention-needed.description.proj-approved',
|
||||
defaultMessage: `Please provide proof that you have permission to redistribute all of the following files and any withheld versions will be automatically published.`,
|
||||
},
|
||||
attentionNeededDescriptionDraft: {
|
||||
id: 'project.settings.permissions.attention-needed.description.proj-draft',
|
||||
defaultMessage: `Please provide proof that you have permission to redistribute all of the following files before you can submit your project for review.`,
|
||||
},
|
||||
})
|
||||
|
||||
function dismissInfoBanner() {
|
||||
flags.value.dismissedExternalProjectsInfo = true
|
||||
saveFeatureFlags()
|
||||
}
|
||||
</script>
|
||||
<template>
|
||||
<template v-if="externalFiles.length > 0">
|
||||
<Admonition
|
||||
v-if="!flags.dismissedExternalProjectsInfo"
|
||||
type="info"
|
||||
class="mb-4"
|
||||
:header="formatMessage(messages.infoBannerTitle)"
|
||||
dismissible
|
||||
@dismiss="dismissInfoBanner"
|
||||
>
|
||||
<IntlFormatted :message-id="messages.infoBannerDescription">
|
||||
<template #link="{ children }">
|
||||
<a class="text-link" target="_blank"> <component :is="() => children" /> </a>
|
||||
</template>
|
||||
</IntlFormatted>
|
||||
<template #actions>
|
||||
<div class="flex">
|
||||
<ButtonStyled color="blue">
|
||||
<a> {{ formatMessage(messages.learnMore) }} <RightArrowIcon /> </a>
|
||||
</ButtonStyled>
|
||||
</div>
|
||||
</template>
|
||||
</Admonition>
|
||||
<Admonition
|
||||
v-if="true"
|
||||
type="success"
|
||||
class="mb-4"
|
||||
:header="formatMessage(messages.completedTitle)"
|
||||
:body="formatMessage(messages.completedDescription)"
|
||||
/>
|
||||
<Admonition
|
||||
v-if="true"
|
||||
type="warning"
|
||||
class="mb-4"
|
||||
:header="formatMessage(messages.attentionNeededTitle)"
|
||||
:body="formatMessage(messages.attentionNeededDescriptionDraft)"
|
||||
/>
|
||||
<Admonition
|
||||
v-if="true"
|
||||
type="critical"
|
||||
class="mb-4"
|
||||
:header="formatMessage(messages.failTitle)"
|
||||
:body="formatMessage(messages.failDescription)"
|
||||
/>
|
||||
<div class="grid grid-cols-[1fr_auto] gap-2">
|
||||
<StyledInput
|
||||
v-model="searchQuery"
|
||||
type="search"
|
||||
:placeholder="
|
||||
formatMessage(messages.searchPlaceholder, {
|
||||
count: externalFiles.length,
|
||||
})
|
||||
"
|
||||
:icon="SearchIcon"
|
||||
input-class="h-[40px]"
|
||||
/>
|
||||
<div>
|
||||
<Combobox
|
||||
v-model="currentSortType"
|
||||
class="!w-full flex-grow sm:!w-[150px] sm:flex-grow-0 lg:!w-[150px]"
|
||||
:options="sortTypes"
|
||||
:placeholder="formatMessage(commonMessages.sortByLabel)"
|
||||
>
|
||||
<template #selected>
|
||||
<span class="flex flex-row gap-2 align-middle font-semibold">
|
||||
<SortAscIcon
|
||||
v-if="currentSortType === 'Oldest'"
|
||||
class="size-5 flex-shrink-0 text-secondary"
|
||||
/>
|
||||
<SortDescIcon v-else class="size-5 flex-shrink-0 text-secondary" />
|
||||
<span class="truncate text-contrast">{{ currentSortType }}</span>
|
||||
</span>
|
||||
</template>
|
||||
</Combobox>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-4 flex flex-col gap-3">
|
||||
<ExternalProjectPermissionsCard title="FTB Library" />
|
||||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<EmptyState
|
||||
:heading="formatMessage(messages.emptyStateHeading)"
|
||||
:description="formatMessage(messages.emptyStateDescription)"
|
||||
type="done"
|
||||
/>
|
||||
</template>
|
||||
</template>
|
||||
@@ -15,6 +15,37 @@
|
||||
@proceed="deleteVersion()"
|
||||
/>
|
||||
|
||||
<Admonition
|
||||
v-if="flags.modpackPermissionsPage && withheldVersions.length > 0"
|
||||
type="warning"
|
||||
class="mb-4"
|
||||
:header="
|
||||
formatMessage(messages.withheldVersionsWarningTitle, {
|
||||
count: withheldVersions.length,
|
||||
version_name: withheldVersions.length === 1 ? withheldVersions[0] : undefined,
|
||||
})
|
||||
"
|
||||
:body="
|
||||
formatMessage(messages.withheldVersionsWarningDescription, {
|
||||
count: withheldVersions.length,
|
||||
version_name: withheldVersions.length === 1 ? withheldVersions[0] : undefined,
|
||||
})
|
||||
"
|
||||
>
|
||||
<template #actions>
|
||||
<div class="flex">
|
||||
<ButtonStyled color="orange">
|
||||
<nuxt-link
|
||||
:to="`/${project.project_type}/${
|
||||
project.slug ? project.slug : project.id
|
||||
}/settings/permissions`"
|
||||
>
|
||||
{{ formatMessage(messages.withheldVersionsWarningResolve) }} <RightArrowIcon />
|
||||
</nuxt-link>
|
||||
</ButtonStyled>
|
||||
</div>
|
||||
</template>
|
||||
</Admonition>
|
||||
<ProjectPageVersions
|
||||
v-if="versions?.length"
|
||||
:project="project"
|
||||
@@ -79,7 +110,7 @@
|
||||
id: 'download',
|
||||
color: 'primary',
|
||||
hoverFilled: true,
|
||||
link: getPrimaryFile(version).url,
|
||||
link: createDownloadUrl(version),
|
||||
action: () => {
|
||||
emit('onDownload')
|
||||
},
|
||||
@@ -293,19 +324,23 @@ import {
|
||||
MoreVerticalIcon,
|
||||
PlusIcon,
|
||||
ReportIcon,
|
||||
RightArrowIcon,
|
||||
ShareIcon,
|
||||
TrashIcon,
|
||||
} from '@modrinth/assets'
|
||||
import {
|
||||
Admonition,
|
||||
ButtonStyled,
|
||||
ConfirmModal,
|
||||
defineMessages,
|
||||
injectModrinthClient,
|
||||
injectNotificationManager,
|
||||
injectProjectPageContext,
|
||||
OverflowMenu,
|
||||
ProjectPageVersions,
|
||||
useVIntl,
|
||||
} from '@modrinth/ui'
|
||||
import { useTemplateRef } from 'vue'
|
||||
import { useTemplateRef, watch } from 'vue'
|
||||
|
||||
import CreateProjectVersionModal from '~/components/ui/create-project-version/CreateProjectVersionModal.vue'
|
||||
import { getSignInRouteObj } from '~/composables/auth.js'
|
||||
@@ -313,14 +348,18 @@ import { reportVersion } from '~/utils/report-helpers.ts'
|
||||
|
||||
const route = useRoute()
|
||||
|
||||
const { createProjectDownloadUrl, updateVersionsFilterContext } = useCdnDownloadContext()
|
||||
|
||||
const client = injectModrinthClient()
|
||||
const { addNotification } = injectNotificationManager()
|
||||
const { formatMessage } = useVIntl()
|
||||
const {
|
||||
projectV2: project,
|
||||
currentMember,
|
||||
versions,
|
||||
invalidate,
|
||||
loadVersions,
|
||||
cdnDownloadReason,
|
||||
} = injectProjectPageContext()
|
||||
|
||||
// Load versions on mount (client-side)
|
||||
@@ -365,6 +404,23 @@ function getPrimaryFile(version: Labrinth.Versions.v3.Version) {
|
||||
return version.files.find((x) => x.primary) || version.files[0]
|
||||
}
|
||||
|
||||
watch(
|
||||
() => [route.query.g, route.query.l],
|
||||
() => {
|
||||
updateVersionsFilterContext(
|
||||
queryAsStringArray(route.query.g),
|
||||
queryAsStringArray(route.query.l),
|
||||
)
|
||||
},
|
||||
{ immediate: true },
|
||||
)
|
||||
|
||||
function createDownloadUrl(version: Labrinth.Versions.v3.Version) {
|
||||
return createProjectDownloadUrl(getPrimaryFile(version).url, {
|
||||
reason: cdnDownloadReason.value,
|
||||
})
|
||||
}
|
||||
|
||||
async function copyToClipboard(text: string) {
|
||||
await navigator.clipboard.writeText(text)
|
||||
}
|
||||
@@ -396,4 +452,23 @@ async function deleteVersion() {
|
||||
|
||||
stopLoading()
|
||||
}
|
||||
|
||||
const withheldVersions = computed(() => ['4.0.0'])
|
||||
|
||||
const messages = defineMessages({
|
||||
withheldVersionsWarningTitle: {
|
||||
id: 'project.versions.withheld-versions-warning.title',
|
||||
defaultMessage:
|
||||
'{count, plural, one {Version {version_name}} other {Versions}} withheld due to unknown embedded content',
|
||||
},
|
||||
withheldVersionsWarningDescription: {
|
||||
id: 'project.versions.withheld-versions-warning.description',
|
||||
defaultMessage:
|
||||
'{count, plural, one {This version is} other {These versions are}} currently withheld and not publicly listed. Please provide proof that you have permission to redistribute certain files included in the modpack {count, plural, one {version} other {versions}}.',
|
||||
},
|
||||
withheldVersionsWarningResolve: {
|
||||
id: 'project.versions.withheld-versions-warning.resolve-button',
|
||||
defaultMessage: 'Resolve',
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -139,7 +139,7 @@
|
||||
<ButtonStyled v-if="primaryFile && !currentMember" color="brand">
|
||||
<a
|
||||
v-tooltip="primaryFile.filename + ' (' + formatBytes(primaryFile.size) + ')'"
|
||||
:href="primaryFile.url"
|
||||
:href="decoratedPrimaryFileUrl"
|
||||
@click="emit('onDownload')"
|
||||
>
|
||||
<DownloadIcon aria-hidden="true" />
|
||||
@@ -213,14 +213,19 @@
|
||||
:key="index"
|
||||
class="dependency"
|
||||
:class="{ 'button-transparent': !isEditing }"
|
||||
@click="!isEditing ? router.push(dependency.link) : {}"
|
||||
@click="!isEditing ? navigateToDependency(dependency) : {}"
|
||||
>
|
||||
<Avatar
|
||||
:src="dependency.project ? dependency.project.icon_url : null"
|
||||
alt="dependency-icon"
|
||||
size="sm"
|
||||
/>
|
||||
<nuxt-link v-if="!isEditing" :to="dependency.link" class="info">
|
||||
<nuxt-link
|
||||
v-if="!isEditing"
|
||||
:to="{ path: dependency.link, query: PROJECT_DEP_MARKER_QUERY }"
|
||||
class="info"
|
||||
@click.stop
|
||||
>
|
||||
<span class="project-title">
|
||||
{{ dependency.project ? dependency.project.title : 'Unknown Project' }}
|
||||
</span>
|
||||
@@ -299,7 +304,7 @@
|
||||
</span>
|
||||
<ButtonStyled>
|
||||
<a
|
||||
:href="file.url"
|
||||
:href="decorateDownloadUrl(file.url)"
|
||||
class="raised-button"
|
||||
:title="`Download ${file.filename}`"
|
||||
tabindex="0"
|
||||
@@ -435,10 +440,12 @@ import {
|
||||
injectNotificationManager,
|
||||
injectProjectPageContext,
|
||||
MultiSelect,
|
||||
PROJECT_DEP_MARKER_QUERY,
|
||||
StyledInput,
|
||||
useFormatBytes,
|
||||
useFormatDateTime,
|
||||
} from '@modrinth/ui'
|
||||
import { formatBytes, renderHighlightedString } from '@modrinth/utils'
|
||||
import { renderHighlightedString } from '@modrinth/utils'
|
||||
|
||||
import Breadcrumbs from '~/components/ui/Breadcrumbs.vue'
|
||||
import CreateProjectVersionModal from '~/components/ui/create-project-version/CreateProjectVersionModal.vue'
|
||||
@@ -461,11 +468,13 @@ const auth = await useAuth()
|
||||
const tags = useGeneratedState()
|
||||
const flags = useFeatureFlags()
|
||||
const { addNotification } = injectNotificationManager()
|
||||
const { createProjectDownloadUrl } = useCdnDownloadContext()
|
||||
const formatDateTime = useFormatDateTime({
|
||||
timeStyle: 'short',
|
||||
dateStyle: 'long',
|
||||
})
|
||||
const formatDate = useFormatDateTime({ dateStyle: 'medium' })
|
||||
const formatBytes = useFormatBytes()
|
||||
|
||||
// Helper for accessing nuxt app $formatVersion
|
||||
const formatVersionDisplay = (versions: string[]) => (data as any).$formatVersion(versions)
|
||||
@@ -481,6 +490,7 @@ const {
|
||||
dependenciesLoading: contextDependenciesLoading,
|
||||
loadDependencies,
|
||||
invalidate,
|
||||
cdnDownloadReason,
|
||||
} = injectProjectPageContext()
|
||||
|
||||
// Load versions and dependencies in parallel
|
||||
@@ -752,6 +762,21 @@ const sortedDeps = computed(() => {
|
||||
)
|
||||
})
|
||||
|
||||
const decoratedPrimaryFileUrl = computed(() =>
|
||||
createProjectDownloadUrl(primaryFile.value?.url, { reason: cdnDownloadReason.value }),
|
||||
)
|
||||
|
||||
function decorateDownloadUrl(url: string) {
|
||||
return createProjectDownloadUrl(url, { reason: cdnDownloadReason.value })
|
||||
}
|
||||
|
||||
function navigateToDependency(dependency: { link: string }) {
|
||||
return router.push({
|
||||
path: dependency.link,
|
||||
query: { ...PROJECT_DEP_MARKER_QUERY },
|
||||
})
|
||||
}
|
||||
|
||||
const environment = computed(
|
||||
() => ENVIRONMENTS_COPY[version.value.environment as keyof typeof ENVIRONMENTS_COPY],
|
||||
)
|
||||
|
||||
@@ -46,7 +46,7 @@
|
||||
<ButtonStyled circular type="transparent">
|
||||
<a
|
||||
v-tooltip="`Download`"
|
||||
:href="getPrimaryFile(version).url"
|
||||
:href="createDownloadUrl(version)"
|
||||
class="hover:!bg-button-bg [&>svg]:!text-green"
|
||||
aria-label="Download"
|
||||
@click="emit('onDownload')"
|
||||
@@ -100,7 +100,7 @@
|
||||
id: 'download',
|
||||
color: 'primary',
|
||||
hoverFilled: true,
|
||||
link: getPrimaryFile(version).url,
|
||||
link: createDownloadUrl(version),
|
||||
action: () => {
|
||||
emit('onDownload')
|
||||
},
|
||||
@@ -266,7 +266,7 @@ import {
|
||||
OverflowMenu,
|
||||
ProjectPageVersions,
|
||||
} from '@modrinth/ui'
|
||||
import { onMounted, useTemplateRef } from 'vue'
|
||||
import { onMounted, useTemplateRef, watch } from 'vue'
|
||||
|
||||
import CreateProjectVersionModal from '~/components/ui/create-project-version/CreateProjectVersionModal.vue'
|
||||
import { getSignInRouteObj } from '~/composables/auth.js'
|
||||
@@ -274,6 +274,8 @@ import { reportVersion } from '~/utils/report-helpers.ts'
|
||||
|
||||
const route = useRoute()
|
||||
|
||||
const { createProjectDownloadUrl, updateVersionsFilterContext } = useCdnDownloadContext()
|
||||
|
||||
const tags = useGeneratedState()
|
||||
const flags = useFeatureFlags()
|
||||
const auth = await useAuth()
|
||||
@@ -287,6 +289,7 @@ const {
|
||||
versions,
|
||||
versionsLoading,
|
||||
loadVersions,
|
||||
cdnDownloadReason,
|
||||
} = injectProjectPageContext()
|
||||
|
||||
// Load versions on mount (client-side)
|
||||
@@ -316,6 +319,23 @@ function getPrimaryFile(version) {
|
||||
return version.files.find((x) => x.primary) || version.files[0]
|
||||
}
|
||||
|
||||
watch(
|
||||
() => [route.query.g, route.query.l],
|
||||
() => {
|
||||
updateVersionsFilterContext(
|
||||
queryAsStringArray(route.query.g),
|
||||
queryAsStringArray(route.query.l),
|
||||
)
|
||||
},
|
||||
{ immediate: true },
|
||||
)
|
||||
|
||||
function createDownloadUrl(version) {
|
||||
return createProjectDownloadUrl(getPrimaryFile(version).url, {
|
||||
reason: cdnDownloadReason.value,
|
||||
})
|
||||
}
|
||||
|
||||
async function copyToClipboard(text) {
|
||||
await navigator.clipboard.writeText(text)
|
||||
}
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
<script setup lang="ts">
|
||||
definePageMeta({
|
||||
middleware: ['auth', 'staff'],
|
||||
})
|
||||
|
||||
useSeoMeta({
|
||||
robots: 'noindex',
|
||||
})
|
||||
|
||||
@@ -81,11 +81,19 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { FileIcon, SpinnerIcon, UploadIcon } from '@modrinth/assets'
|
||||
import { Admonition, Avatar, CopyCode, injectNotificationManager } from '@modrinth/ui'
|
||||
import { formatBytes, type Project, type Version } from '@modrinth/utils'
|
||||
import {
|
||||
Admonition,
|
||||
Avatar,
|
||||
CopyCode,
|
||||
injectNotificationManager,
|
||||
useFormatBytes,
|
||||
} from '@modrinth/ui'
|
||||
import type { Project, Version } from '@modrinth/utils'
|
||||
|
||||
const { addNotification } = injectNotificationManager()
|
||||
|
||||
const formatBytes = useFormatBytes()
|
||||
|
||||
const fileInput = ref<HTMLInputElement>()
|
||||
const selectedFile = ref<File | null>(null)
|
||||
const fileHashes = ref<{
|
||||
|
||||
@@ -657,6 +657,7 @@ watch(
|
||||
[collection, creator],
|
||||
([col, cre]) => {
|
||||
if (col && cre) {
|
||||
const canonicalUrl = col ? `https://modrinth.com/collection/${col.id}` : undefined
|
||||
useSeoMeta({
|
||||
title: formatMessage(messages.collectionTitle, { name: col.name }),
|
||||
description: formatMessage(messages.collectionDescription, {
|
||||
@@ -667,8 +668,17 @@ watch(
|
||||
ogTitle: formatMessage(messages.collectionTitle, { name: col.name }),
|
||||
ogDescription: col.description,
|
||||
ogImage: col.icon_url ?? 'https://cdn.modrinth.com/placeholder.png',
|
||||
ogUrl: canonicalUrl,
|
||||
robots: col.status === 'listed' ? 'all' : 'noindex',
|
||||
})
|
||||
useHead({
|
||||
link: [
|
||||
{
|
||||
rel: 'canonical',
|
||||
href: canonicalUrl,
|
||||
},
|
||||
],
|
||||
})
|
||||
}
|
||||
},
|
||||
{ immediate: true },
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user